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