Skip to content

Instantly share code, notes, and snippets.

@a0u
Created September 6, 2015 01:05
Show Gist options
  • Save a0u/2d533ca4ea91764a0fa8 to your computer and use it in GitHub Desktop.
Save a0u/2d533ca4ea91764a0fa8 to your computer and use it in GitHub Desktop.
Wrapper for redirecting stdin/stdout of a shell command to a pseudo-terminal
/*
* \brief Wrapper for redirecting stdin/stdout to a pseudo-terminal
* \author Albert Ou <aou@eecs.berkeley.edu>
* \copyright BSD 2-Clause License
*/
#define _XOPEN_SOURCE (600)
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/wait.h>
#include <termios.h>
int main(int argc, char **argv)
{
const char *errmsg;
const char *name;
int pty, pts;
pid_t pid;
sigset_t set;
struct termios tios;
if (argc < 2) {
fprintf(stderr, "usage: %s [command]\n", argv[0]);
return EXIT_FAILURE;
}
errno = 0;
errmsg = NULL;
if ((pty = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
errmsg = "posix_openpt";
goto exit;
}
if (grantpt(pty)) {
errmsg = "grantpt";
goto close;
}
if (unlockpt(pty)) {
errmsg = "unlockpt";
goto close;
}
name = ptsname(pty);
if ((pts = open(name, O_RDWR | O_NOCTTY)) < 0) {
errmsg = "open";
goto close;
}
close(pts);
puts(name);
/* Poll until HUP condition vanishes, which should
indicate that the slave device has been reopened */
for (;;) {
struct pollfd fds;
fds.fd = pty;
fds.events = POLLHUP;
if (poll(&fds, 1, 0) < 0) {
errmsg = "poll";
goto close;
}
if (!(fds.revents & POLLHUP))
break;
usleep(100);
}
if (tcgetattr(pty, &tios)) {
errmsg = "tcgetattr";
goto close;
}
tios.c_lflag &= ~(ICANON | ECHO | ISIG);
if (tcsetattr(pty, TCSANOW, &tios)) {
errmsg = "tcsetattr";
goto close;
}
if ((pid = fork()) < 0) {
errmsg = "fork";
goto close;
}
if (pid == 0) {
if ((dup2(pty, STDIN_FILENO) < 0) ||
(dup2(pty, STDOUT_FILENO) < 0)) {
errmsg = "dup2";
goto close;
}
execvp(argv[1], argv + 1);
errmsg = "execvp";
goto close;
}
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, NULL);
for (;;) {
int signo;
sigwait(&set, &signo);
if (signo == SIGCHLD) {
int status;
waitpid(pid, &status, WNOHANG);
if (WIFEXITED(status))
break;
} else {
kill(pid, SIGTERM);
break;
}
}
close:
close(pty);
exit:
if (errno)
perror(errmsg);
return errno;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment