Mercurial > hg > rlgwebd
view ptyhelper.c @ 79:3fd410c52a61 beta3
Call this beta3.
author | John "Elwin" Edwards <elwin@sdf.org> |
---|---|
date | Sat, 23 Jun 2012 19:45:06 -0700 |
parents | 9bef0941c6dd |
children |
line wrap: on
line source
/* * ptyhelper: a utility that runs a command in a pseudoterm and then streams * stdio to/from it. Intended to get around the node.js loss of C/UNIX * functionality. * Remember to compile with -lutil. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <pty.h> #include <utmp.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/select.h> #include <termios.h> int got_sighup = 0; void handle_HUP(int signum) { if (signum == SIGHUP) got_sighup = 1; return; } int main(int argc, char *argv[]) { int ptymaster, ptyslave; /* File descriptors */ int child; int status, selstatus; int w = 80, h = 24, t; struct sigaction sighup_act; fd_set readset; struct timeval select_time; char buf[4096]; int nread; char *penv, *ptmp; #if 0 struct termios ptysettings; #endif struct winsize ptysize; if (argc == 1) { fprintf(stderr, "No command given.\n"); exit(1); } /* Set up the signal handler. */ sighup_act.sa_handler = &handle_HUP; sighup_act.sa_flags = SA_RESTART; sigaction(SIGHUP, &sighup_act, NULL); /* Check the environment for configuration options. */ penv = getenv("PTYHELPER"); if (penv != NULL) { t = strtol(penv, &ptmp, 10); if (t > 0 && t < 256) h = t; if (*ptmp != '\0') { penv = ptmp + 1; t = strtol(penv, &ptmp, 10); if (t > 0 && t < 256) w = t; } } /* Set up the size. */ ptysize.ws_row = h; ptysize.ws_col = w; /* Open a pty */ if (openpty(&ptymaster, &ptyslave, NULL, NULL, &ptysize)) { return 1; } #if 0 /* Put it into raw mode. */ tcgetattr(ptyslave, &ptysettings); cfmakeraw(&ptysettings); tcsetattr(ptyslave, TCSANOW, &ptysettings); #endif /* Start the child */ /* forkpty() might be more convenient. */ if (!(child = fork())) { /* Child process */ login_tty(ptyslave); close(ptymaster); execvp(argv[1], argv + 1); perror("execvp() failed"); return 1; } close(ptyslave); while (1) { /* Now do a select() over stdin and ptymaster, and write anything that * appears to ptymaster and stdout respectively. */ FD_ZERO(&readset); FD_SET(0, &readset); FD_SET(ptymaster, &readset); select_time.tv_sec = 1; select_time.tv_usec = 0; selstatus = select(ptymaster + 1, &readset, NULL, NULL, &select_time); if (selstatus > 0) { /* TODO make sure it all gets written if a signal interrupts write(). */ if (FD_ISSET(0, &readset)) { nread = read(0, buf, 4096); if (nread > 0) { write(ptymaster, buf, nread); } } if (FD_ISSET(ptymaster, &readset)) { nread = read(ptymaster, buf, 4096); if (nread > 0) { write(1, buf, nread); } } } /* Periodically check to see if we're done. */ /* TODO: catch SIGCHLD and only wait() if it is delivered. */ if (waitpid(child, &status, WNOHANG)) { break; } /* If node sighup's us, pass it along. */ if (got_sighup) { kill(child, SIGHUP); } } /* Get any leftover output and clean up. */ /* FIXME looping over select() is pointless if there's only one fd that * nothing's writing to. Just loop over read() until it's empty. */ while (1) { FD_ZERO(&readset); FD_SET(ptymaster, &readset); select_time.tv_sec = 0; select_time.tv_usec = 0; if (select(ptymaster + 1, &readset, NULL, NULL, &select_time) > 0) { nread = read(ptymaster, buf, 4096); if (nread > 0) { write(1, buf, nread); } else break; } else break; } close(ptymaster); /* Return the child's exit status. */ if (WIFEXITED(status)) return WEXITSTATUS(status); return 0; }