pts.c (3821B)
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils-pts@hacktivis.me>
- // SPDX-License-Identifier: MIT
- #define _POSIX_C_SOURCE 200809L
- #define _XOPEN_SOURCE 800
- #include <stdlib.h> // posix_openpt, ptsname
- #include <fcntl.h> // O_*
- #include <string.h> // strerror
- #include <stdio.h> // fprintf, perror
- #include <errno.h>
- #include <poll.h>
- #include <unistd.h> // execvp, dup2
- #include <sys/wait.h>
- #include <signal.h>
- static int child_status = 0;
- static pid_t pid = -1;
- static void
- handle_sigchld(int sig)
- {
- (void)sig;
- waitpid(pid, &child_status, WNOHANG);
- exit(WEXITSTATUS(child_status));
- }
- int
- main(int argc, char *argv[])
- {
- argc--;
- argv++;
- if(argc < 1)
- {
- fprintf(stderr, "pts: Expected >= 1 arguments, got %d\n", argc);
- return 1;
- }
- int manager = posix_openpt(O_RDWR|O_NOCTTY);
- if(manager < 0)
- {
- fprintf(stderr, "pts: Failed opening pseudo-terminal: %s\n", strerror(errno));
- return 1;
- }
- if(grantpt(manager) < 0)
- {
- perror("pts: Error in grantpt(manager)");
- return 1;
- }
- if(unlockpt(manager) < 0)
- {
- perror("pts: Error in unlockpt(manager)");
- return 1;
- }
- pid = fork();
- if(pid < 0)
- {
- fprintf(stderr, "pts: Fork failed: %s\n", strerror(errno));
- return 1;
- }
- if(pid != 0)
- {
- nfds_t nfds = 2;
- struct pollfd pfds[2] = {
- {
- .fd = manager,
- .events = POLLIN
- },
- {
- .fd = STDIN_FILENO,
- .events = POLLIN
- }
- };
- if(signal(SIGCHLD, handle_sigchld) == SIG_ERR)
- {
- fprintf(stderr, "timeout: Failed registering handler for SIGCHLD: %s\n", strerror(errno));
- return 1;
- }
- // parent
- do {
- int ret = poll(pfds, nfds, -1);
- if(ret < 0)
- {
- fprintf(stderr, "pts: Poll failed: %s\n", strerror(errno));
- return 1;
- }
- if(ret == 0) continue; // timeout
- if(pfds[0].revents & POLLIN)
- {
- char buf[BUFSIZ] = "";
- ssize_t nread = read(pfds[0].fd, buf, BUFSIZ);
- if(nread < 0)
- {
- fprintf(stderr, "pts: Failed reading from pseudo-terminal: %s\n", strerror(errno));
- return 1;
- }
- if(nread == 0) break;
- if(write(STDOUT_FILENO, buf, nread) < 0)
- {
- fprintf(stderr, "pts: Failed writing pseudo-terminal content to stdout: %s\n", strerror(errno));
- return 1;
- }
- }
- if(pfds[1].revents & POLLIN)
- {
- char buf[BUFSIZ] = "";
- ssize_t nread = read(pfds[1].fd, buf, BUFSIZ);
- if(nread < 0)
- {
- fprintf(stderr, "pts: Failed reading from stdin: %s\n", strerror(errno));
- return 1;
- }
- if(nread == 0) break;
- if(write(manager, buf, nread) < 0)
- {
- fprintf(stderr, "pts: Failed writing stdin content to pseudo-terminal: %s\n", strerror(errno));
- return 1;
- }
- }
- if(waitpid(pid, &child_status, WNOHANG) < 0)
- {
- fprintf(stderr, "pts: Failed getting status for child process: %s\n", strerror(errno));
- return 1;
- }
- } while(child_status == 0 || !WIFEXITED(child_status));
- return WEXITSTATUS(child_status);
- }
- else
- {
- // child
- char *name = ptsname(manager);
- int sub = open(name, O_RDWR|O_NOCTTY);
- if(sub < 0)
- {
- fprintf(stderr, "pts: Failed opening subsidiary pseudo-terminal at '%s': %s\n", name, strerror(errno));
- return 1;
- }
- if(dup2(sub, STDIN_FILENO) < 0)
- {
- fprintf(stderr, "pts: Failed assigning subsidiary pseudo-terminal to stdin: %s\n", strerror(errno));
- return 1;
- }
- if(dup2(sub, STDOUT_FILENO) < 0)
- {
- fprintf(stderr, "pts: Failed assigning subsidiary pseudo-terminal to stdout: %s\n", strerror(errno));
- return 1;
- }
- if(dup2(sub, STDERR_FILENO) < 0)
- {
- fprintf(stderr, "pts: Failed assigning subsidiary pseudo-terminal to stderr: %s\n", strerror(errno));
- return 1;
- }
- if(execvp(argv[0], argv) < 0)
- {
- fprintf(stderr, "pts: Failed to execute '%s': %s\n", argv[0], strerror(errno));
- return errno == ENOENT ? 127 : 126;
- }
- abort();
- }
- }