comparison ptyhelper.c @ 0:bd412f63ce0d

Put this project under version control, finally.
author John "Elwin" Edwards <elwin@sdf.org>
date Sun, 06 May 2012 08:45:40 -0700
parents
children 9bef0941c6dd
comparison
equal deleted inserted replaced
-1:000000000000 0:bd412f63ce0d
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <signal.h>
5 #include <pty.h>
6 #include <utmp.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <sys/select.h>
10 #include <termios.h>
11
12 int got_sighup = 0;
13
14 void handle_HUP(int signum) {
15 if (signum == SIGHUP)
16 got_sighup = 1;
17 return;
18 }
19
20 int main(int argc, char *argv[]) {
21
22 int ptymaster, ptyslave; /* File descriptors */
23 int child;
24 int status, selstatus;
25 int w = 80, h = 24, t;
26 struct sigaction sighup_act;
27 fd_set readset;
28 struct timeval select_time;
29 char buf[4096];
30 int nread;
31 char *penv, *ptmp;
32 #if 0
33 struct termios ptysettings;
34 #endif
35 struct winsize ptysize;
36
37 if (argc == 1) {
38 fprintf(stderr, "No command given.\n");
39 exit(1);
40 }
41
42 /* Set up the signal handler. */
43 sighup_act.sa_handler = &handle_HUP;
44 sighup_act.sa_flags = SA_RESTART;
45 sigaction(SIGHUP, &sighup_act, NULL);
46
47 /* Check the environment for configuration options. */
48 penv = getenv("PTYHELPER");
49 if (penv != NULL) {
50 t = strtol(penv, &ptmp, 10);
51 if (t > 0 && t < 256)
52 h = t;
53 if (*ptmp != '\0') {
54 penv = ptmp + 1;
55 t = strtol(penv, &ptmp, 10);
56 if (t > 0 && t < 256)
57 w = t;
58 }
59 }
60 /* Set up the size. */
61 ptysize.ws_row = h;
62 ptysize.ws_col = w;
63
64 /* Open a pty */
65 if (openpty(&ptymaster, &ptyslave, NULL, NULL, &ptysize)) {
66 return 1;
67 }
68 #if 0
69 /* Put it into raw mode. */
70 tcgetattr(ptyslave, &ptysettings);
71 cfmakeraw(&ptysettings);
72 tcsetattr(ptyslave, TCSANOW, &ptysettings);
73 #endif
74
75 /* Start the child */
76 /* forkpty() might be more convenient. */
77 if (!(child = fork())) {
78 /* Child process */
79 login_tty(ptyslave);
80 close(ptymaster);
81 execvp(argv[1], argv + 1);
82 perror("execvp() failed");
83 return 1;
84 }
85 close(ptyslave);
86
87 while (1) {
88 /* Now do a select() over stdin and ptymaster, and write anything that
89 * appears to ptymaster and stdout respectively. */
90 FD_ZERO(&readset);
91 FD_SET(0, &readset);
92 FD_SET(ptymaster, &readset);
93 select_time.tv_sec = 1;
94 select_time.tv_usec = 0;
95 selstatus = select(ptymaster + 1, &readset, NULL, NULL, &select_time);
96 if (selstatus > 0) {
97 /* TODO make sure it all gets written if a signal interrupts write(). */
98 if (FD_ISSET(0, &readset)) {
99 nread = read(0, buf, 4096);
100 if (nread > 0) {
101 write(ptymaster, buf, nread);
102 }
103 }
104 if (FD_ISSET(ptymaster, &readset)) {
105 nread = read(ptymaster, buf, 4096);
106 if (nread > 0) {
107 write(1, buf, nread);
108 }
109 }
110 }
111
112 /* Periodically check to see if we're done. */
113 /* TODO: catch SIGCHLD and only wait() if it is delivered. */
114 if (waitpid(child, &status, WNOHANG)) {
115 break;
116 }
117
118 /* If node sighup's us, pass it along. */
119 if (got_sighup) {
120 kill(child, SIGHUP);
121 }
122 }
123
124 /* Get any leftover output and clean up. */
125 /* FIXME looping over select() is pointless if there's only one fd that
126 * nothing's writing to. Just loop over read() until it's empty. */
127 while (1) {
128 FD_ZERO(&readset);
129 FD_SET(ptymaster, &readset);
130 select_time.tv_sec = 0;
131 select_time.tv_usec = 0;
132 if (select(ptymaster + 1, &readset, NULL, NULL, &select_time) > 0) {
133 nread = read(ptymaster, buf, 4096);
134 if (nread > 0) {
135 write(1, buf, nread);
136 }
137 else
138 break;
139 }
140 else
141 break;
142 }
143 close(ptymaster);
144
145 /* Return the child's exit status. */
146 if (WIFEXITED(status))
147 return WEXITSTATUS(status);
148 return 0;
149 }