1 /* $Id: stdio2pty.c,v 1.1.2.1 2011/01/15 12:48:44 steve Exp $ */
2
3 #include <sys/types.h>
4 #include <sys/ioctl.h>
5 #include <sys/wait.h>
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <poll.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include "buffer.h"
17 #include "stdio2pty.h"
18
19 #define MAX_BUFF_LEN 32768
20
21 #ifndef WALLSIG
22 #define WALLSIG 0
23 #endif
24
25 static int all_done, exit_status, child_pid;
26
27 static struct buffer_ctx *stdout_buff;
28 static struct buffer_ctx *ptyout_buff;
29
30 static void
childsig(int dummy)31 childsig(int dummy)
32 {
33 int status;
34 pid_t pid;
35
36 if ((pid = wait3(&status, WNOHANG | WALLSIG, NULL)) == 0)
37 return;
38
39 if (WIFEXITED(status)) {
40 exit_status = WEXITSTATUS(status);
41 all_done = 1;
42 } else
43 if (WIFSIGNALED(status)) {
44 exit_status = -1;
45 all_done = 1;
46 }
47 }
48
49 static int
copyfrom(int fd,struct buffer_ctx * bc)50 copyfrom(int fd, struct buffer_ctx *bc)
51 {
52 char buff[1024];
53 ssize_t rv;
54
55 #ifndef MIN
56 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
57 #endif
58
59 for (;;) {
60 if ((rv = read(fd, buff, sizeof(buff))) == 0)
61 break;
62
63 if (rv < 0) {
64 if (errno == EINTR)
65 continue;
66
67 if (errno == EWOULDBLOCK)
68 break;
69
70 return (-1);
71 }
72
73 if ((rv = buffer_fill(bc, buff, (size_t)rv)) < 0)
74 return ((int)rv);
75 }
76
77 return (0);
78 }
79
80 static int
mainloop(int fd)81 mainloop(int fd)
82 {
83 struct pollfd pfds[3];
84 int nfds, rv = 0;
85
86 while (all_done == 0 && rv >= 0) {
87 pfds[0].fd = fd;
88 pfds[0].events = POLLHUP;
89 pfds[0].revents = 0;
90 if (buffer_length(stdout_buff) < MAX_BUFF_LEN)
91 pfds[0].events |= POLLRDNORM;
92 if (buffer_length(ptyout_buff) > 0)
93 pfds[0].events |= POLLWRNORM;
94
95 pfds[1].fd = STDIN_FILENO;
96 pfds[1].revents = 0;
97 if (buffer_length(ptyout_buff) < MAX_BUFF_LEN)
98 pfds[1].events = POLLRDNORM;
99 else
100 pfds[1].events = 0;
101
102 pfds[2].fd = STDOUT_FILENO;
103 pfds[2].revents = 0;
104 if (buffer_length(stdout_buff) > 0)
105 pfds[2].events = POLLWRNORM;
106 else
107 pfds[2].events = 0;
108
109 if ((nfds = poll(pfds, 3, INFTIM)) < 0 && errno == EINTR)
110 continue;
111
112 if (nfds < 0) {
113 perror("poll");
114 return(-1);
115 }
116
117 if (pfds[0].revents & POLLHUP)
118 return (3); /* Child closed slave device */
119
120 if (pfds[0].revents & POLLRDNORM)
121 rv = copyfrom(pfds[0].fd, stdout_buff);
122
123 if (rv >= 0 && pfds[2].revents & POLLWRNORM)
124 rv = buffer_drain(stdout_buff);
125
126 if (rv >= 0 && pfds[1].revents & POLLRDNORM)
127 rv = copyfrom(pfds[1].fd, ptyout_buff);
128
129 if (rv >= 0 && pfds[0].revents & POLLWRNORM)
130 rv = buffer_drain(ptyout_buff);
131 }
132
133 return (rv);
134 }
135
136 int
stdio2pty_main(int argc,char ** argv,char ** envp)137 stdio2pty_main(int argc, char **argv, char **envp)
138 {
139 char *slavename, **cmdargs;
140 int fd, rv;
141
142 if ((fd = posix_openpt(O_RDWR | O_NONBLOCK)) < 0) {
143 perror("posix_openpt");
144 exit(1);
145 }
146
147 if (unlockpt(fd) < 0) {
148 perror("unlockpt");
149 (void) close(fd);
150 exit(1);
151 }
152
153 if (grantpt(fd) < 0) {
154 perror("grantpt");
155 (void) close(fd);
156 exit(1);
157 }
158
159 if ((slavename = ptsname(fd)) == NULL) {
160 perror("ptsname");
161 (void) close(fd);
162 exit(1);
163 }
164
165 signal(SIGCHLD, childsig);
166
167 if ((child_pid = fork()) < 0) {
168 perror("fork");
169 (void) close(fd);
170 exit(1);
171 } else
172 if (child_pid == 0) {
173 (void) close(fd);
174 signal(SIGCHLD, SIG_DFL);
175
176 if ((fd = open(slavename, O_RDWR)) < 0) {
177 perror("ptsname");
178 exit(1);
179 }
180
181 if (dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0) {
182 perror("dup2");
183 (void) close(fd);
184 exit(1);
185 }
186
187 if ((cmdargs = calloc(argc, sizeof(*cmdargs))) == NULL) {
188 perror("calloc");
189 (void) close(fd);
190 exit(1);
191 }
192
193 memcpy(cmdargs, &argv[1], (argc - 1) * sizeof(*cmdargs));
194 cmdargs[argc - 1] = NULL;
195
196 (void) execve(argv[1], cmdargs, envp);
197
198 perror("execve");
199 fprintf(stderr, "%s: Failed to exec '%s'\n", argv[0], argv[1]);
200 (void) close(fd);
201 exit(2);
202 }
203
204 if (buffer_init(&stdout_buff, STDOUT_FILENO) < 0 ||
205 buffer_init(&ptyout_buff, fd) < 0) {
206 perror("buffer_init");
207 sleep(1);
208 kill(child_pid, SIGHUP);
209 sleep(1);
210 (void) close(fd);
211 exit(1);
212 }
213
214 if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) {
215 perror("fcntl(O_NONBLOCK)");
216 sleep(1);
217 kill(child_pid, SIGHUP);
218 sleep(1);
219 (void) close(fd);
220 exit(1);
221 }
222
223 if ((rv = mainloop(fd)) != 0) {
224 sleep(1);
225 kill(child_pid, SIGHUP);
226 sleep(1);
227 close(fd);
228 exit(rv);
229 }
230
231 exit(exit_status);
232 }
233