1 /*
2  * This is pseudo-terminal container for child process where parent creates a
3  * proxy between the current std{in,out,etrr} and the child's pty. Advantages:
4  *
5  * - child has no access to parent's terminal (e.g. su --pty)
6  * - parent can log all traffic between user and child's terminall (e.g. script(1))
7  * - it's possible to start commands on terminal although parent has no terminal
8  *
9  * This code is in the public domain; do with it what you wish.
10  *
11  * Written by Karel Zak <kzak@redhat.com> in Jul 2019
12  */
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <pty.h>
16 #include <poll.h>
17 #include <sys/signalfd.h>
18 #include <paths.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 
22 #include "c.h"
23 #include "all-io.h"
24 #include "ttyutils.h"
25 #include "pty-session.h"
26 #include "monotonic.h"
27 #include "debug.h"
28 
29 static UL_DEBUG_DEFINE_MASK(ulpty);
30 UL_DEBUG_DEFINE_MASKNAMES(ulpty) = UL_DEBUG_EMPTY_MASKNAMES;
31 
32 #define ULPTY_DEBUG_INIT	(1 << 1)
33 #define ULPTY_DEBUG_SETUP	(1 << 2)
34 #define ULPTY_DEBUG_SIG		(1 << 3)
35 #define ULPTY_DEBUG_IO		(1 << 4)
36 #define ULPTY_DEBUG_DONE	(1 << 5)
37 #define ULPTY_DEBUG_ALL		0xFFFF
38 
39 #define DBG(m, x)       __UL_DBG(ulpty, ULPTY_DEBUG_, m, x)
40 #define ON_DBG(m, x)    __UL_DBG_CALL(ulpty, ULPTY_DEBUG_, m, x)
41 
42 #define UL_DEBUG_CURRENT_MASK   UL_DEBUG_MASK(ulpty)
43 #include "debugobj.h"
44 
ul_pty_init_debug(int mask)45 void ul_pty_init_debug(int mask)
46 {
47 	if (ulpty_debug_mask)
48 		return;
49 	__UL_INIT_DEBUG_FROM_ENV(ulpty, ULPTY_DEBUG_, mask, ULPTY_DEBUG);
50 }
51 
ul_new_pty(int is_stdin_tty)52 struct ul_pty *ul_new_pty(int is_stdin_tty)
53 {
54 	struct ul_pty *pty = calloc(1, sizeof(*pty));
55 
56 	if (!pty)
57 		return NULL;
58 
59 	DBG(SETUP, ul_debugobj(pty, "alloc handler"));
60 	pty->isterm = is_stdin_tty;
61 	pty->master = -1;
62 	pty->slave = -1;
63 	pty->sigfd = -1;
64 	pty->child = (pid_t) -1;
65 
66 	return pty;
67 }
68 
ul_free_pty(struct ul_pty * pty)69 void ul_free_pty(struct ul_pty *pty)
70 {
71 	free(pty);
72 }
73 
ul_pty_slave_echo(struct ul_pty * pty,int enable)74 void ul_pty_slave_echo(struct ul_pty *pty, int enable)
75 {
76 	assert(pty);
77 	pty->slave_echo = enable ? 1 : 0;
78 }
79 
ul_pty_get_delivered_signal(struct ul_pty * pty)80 int ul_pty_get_delivered_signal(struct ul_pty *pty)
81 {
82 	assert(pty);
83 	return pty->delivered_signal;
84 }
85 
ul_pty_get_callbacks(struct ul_pty * pty)86 struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty)
87 {
88 	assert(pty);
89 	return &pty->callbacks;
90 }
91 
ul_pty_set_callback_data(struct ul_pty * pty,void * data)92 void ul_pty_set_callback_data(struct ul_pty *pty, void *data)
93 {
94 	assert(pty);
95 	pty->callback_data = data;
96 }
97 
ul_pty_set_child(struct ul_pty * pty,pid_t child)98 void ul_pty_set_child(struct ul_pty *pty, pid_t child)
99 {
100 	assert(pty);
101 	pty->child = child;
102 }
103 
ul_pty_get_childfd(struct ul_pty * pty)104 int ul_pty_get_childfd(struct ul_pty *pty)
105 {
106 	assert(pty);
107 	return pty->master;
108 }
109 
ul_pty_get_child(struct ul_pty * pty)110 pid_t ul_pty_get_child(struct ul_pty *pty)
111 {
112 	assert(pty);
113 	return pty->child;
114 }
115 
116 /* it's active when signals are redurected to sigfd */
ul_pty_is_running(struct ul_pty * pty)117 int ul_pty_is_running(struct ul_pty *pty)
118 {
119 	assert(pty);
120 	return pty->sigfd >= 0;
121 }
122 
ul_pty_set_mainloop_time(struct ul_pty * pty,struct timeval * tv)123 void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv)
124 {
125 	assert(pty);
126 	if (!tv) {
127 		DBG(IO, ul_debugobj(pty, "mainloop time: clear"));
128 		timerclear(&pty->next_callback_time);
129 	} else {
130 		pty->next_callback_time.tv_sec = tv->tv_sec;
131 		pty->next_callback_time.tv_usec = tv->tv_usec;
132 		DBG(IO, ul_debugobj(pty, "mainloop time: %ld.%06ld", tv->tv_sec, tv->tv_usec));
133 	}
134 }
135 
pty_signals_cleanup(struct ul_pty * pty)136 static void pty_signals_cleanup(struct ul_pty *pty)
137 {
138 	if (pty->sigfd != -1)
139 		close(pty->sigfd);
140 	pty->sigfd = -1;
141 
142 	/* restore original setting */
143 	sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
144 }
145 
146 /* call me before fork() */
ul_pty_setup(struct ul_pty * pty)147 int ul_pty_setup(struct ul_pty *pty)
148 {
149 	struct termios slave_attrs;
150 	sigset_t ourset;
151 	int rc = 0;
152 
153 	assert(pty->sigfd == -1);
154 
155 	/* save the current signals setting */
156 	sigprocmask(0, NULL, &pty->orgsig);
157 
158 	if (pty->isterm) {
159 	        DBG(SETUP, ul_debugobj(pty, "create for terminal"));
160 
161 		/* original setting of the current terminal */
162 		if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0) {
163 			rc = -errno;
164 			goto done;
165 		}
166 		ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
167 		/* create master+slave */
168 		rc = openpty(&pty->master, &pty->slave, NULL, &pty->stdin_attrs, &pty->win);
169 		if (rc)
170 			goto done;
171 
172 		/* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */
173 		slave_attrs = pty->stdin_attrs;
174 		cfmakeraw(&slave_attrs);
175 
176 		if (pty->slave_echo)
177 			slave_attrs.c_lflag |= ECHO;
178 		else
179 			slave_attrs.c_lflag &= ~ECHO;
180 
181 		tcsetattr(STDIN_FILENO, TCSANOW, &slave_attrs);
182 	} else {
183 	        DBG(SETUP, ul_debugobj(pty, "create for non-terminal"));
184 
185 		rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL);
186 		if (rc)
187 			goto done;
188 
189 		tcgetattr(pty->slave, &slave_attrs);
190 
191 		if (pty->slave_echo)
192 			slave_attrs.c_lflag |= ECHO;
193 		else
194 			slave_attrs.c_lflag &= ~ECHO;
195 
196 		tcsetattr(pty->slave, TCSANOW, &slave_attrs);
197 	}
198 
199 	sigfillset(&ourset);
200 	if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
201 		rc = -errno;
202 		goto done;
203 	}
204 
205 	sigemptyset(&ourset);
206 	sigaddset(&ourset, SIGCHLD);
207 	sigaddset(&ourset, SIGWINCH);
208 	sigaddset(&ourset, SIGALRM);
209 	sigaddset(&ourset, SIGTERM);
210 	sigaddset(&ourset, SIGINT);
211 	sigaddset(&ourset, SIGQUIT);
212 
213 	if (pty->callbacks.flush_logs)
214 		sigaddset(&ourset, SIGUSR1);
215 
216 	if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0)
217 		rc = -errno;
218 done:
219 	if (rc)
220 		ul_pty_cleanup(pty);
221 
222 	DBG(SETUP, ul_debugobj(pty, "pty setup done [master=%d, slave=%d, rc=%d]",
223 				pty->master, pty->slave, rc));
224 	return rc;
225 }
226 
227 /* cleanup in parent process */
ul_pty_cleanup(struct ul_pty * pty)228 void ul_pty_cleanup(struct ul_pty *pty)
229 {
230 	struct termios rtt;
231 
232 	pty_signals_cleanup(pty);
233 
234 	if (pty->master == -1 || !pty->isterm)
235 		return;
236 
237 	DBG(DONE, ul_debugobj(pty, "cleanup"));
238 	rtt = pty->stdin_attrs;
239 	tcsetattr(STDIN_FILENO, TCSADRAIN, &rtt);
240 }
241 
242 /* call me in child process */
ul_pty_init_slave(struct ul_pty * pty)243 void ul_pty_init_slave(struct ul_pty *pty)
244 {
245 	DBG(SETUP, ul_debugobj(pty, "initialize slave"));
246 
247 	setsid();
248 
249 	ioctl(pty->slave, TIOCSCTTY, 1);
250 	close(pty->master);
251 
252 	dup2(pty->slave, STDIN_FILENO);
253 	dup2(pty->slave, STDOUT_FILENO);
254 	dup2(pty->slave, STDERR_FILENO);
255 
256 	close(pty->slave);
257 
258 	if (pty->sigfd >= 0)
259 		close(pty->sigfd);
260 
261 	pty->slave = -1;
262 	pty->master = -1;
263 	pty->sigfd = -1;
264 
265 	sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
266 
267 	DBG(SETUP, ul_debugobj(pty, "... initialize slave done"));
268 }
269 
write_output(char * obuf,ssize_t bytes)270 static int write_output(char *obuf, ssize_t bytes)
271 {
272 	DBG(IO, ul_debug(" writing output"));
273 
274 	if (write_all(STDOUT_FILENO, obuf, bytes)) {
275 		DBG(IO, ul_debug("  writing output *failed*"));
276 		return -errno;
277 	}
278 
279 	return 0;
280 }
281 
write_to_child(struct ul_pty * pty,char * buf,size_t bufsz)282 static int write_to_child(struct ul_pty *pty, char *buf, size_t bufsz)
283 {
284 	return write_all(pty->master, buf, bufsz);
285 }
286 
287 /*
288  * The pty is usually faster than shell, so it's a good idea to wait until
289  * the previous message has been already read by shell from slave before we
290  * write to master. This is necessary especially for EOF situation when we can
291  * send EOF to master before shell is fully initialized, to workaround this
292  * problem we wait until slave is empty. For example:
293  *
294  *   echo "date" | su --pty
295  *
296  * Unfortunately, the child (usually shell) can ignore stdin at all, so we
297  * don't wait forever to avoid dead locks...
298  *
299  * Note that su --pty is primarily designed for interactive sessions as it
300  * maintains master+slave tty stuff within the session. Use pipe to write to
301  * pty and assume non-interactive (tee-like) behavior is NOT well supported.
302  */
ul_pty_write_eof_to_child(struct ul_pty * pty)303 void ul_pty_write_eof_to_child(struct ul_pty *pty)
304 {
305 	unsigned int tries = 0;
306 	struct pollfd fds[] = {
307 	           { .fd = pty->slave, .events = POLLIN }
308 	};
309 	char c = DEF_EOF;
310 
311 	DBG(IO, ul_debugobj(pty, " waiting for empty slave"));
312 	while (poll(fds, 1, 10) == 1 && tries < 8) {
313 		DBG(IO, ul_debugobj(pty, "   slave is not empty"));
314 		xusleep(250000);
315 		tries++;
316 	}
317 	if (tries < 8)
318 		DBG(IO, ul_debugobj(pty, "   slave is empty now"));
319 
320 	DBG(IO, ul_debugobj(pty, " sending EOF to master"));
321 	write_to_child(pty, &c, sizeof(char));
322 }
323 
mainloop_callback(struct ul_pty * pty)324 static int mainloop_callback(struct ul_pty *pty)
325 {
326 	int rc;
327 
328 	if (!pty->callbacks.mainloop)
329 		return 0;
330 
331 	DBG(IO, ul_debugobj(pty, "calling mainloop callback"));
332 	rc = pty->callbacks.mainloop(pty->callback_data);
333 
334 	DBG(IO, ul_debugobj(pty, " callback done [rc=%d]", rc));
335 	return rc;
336 }
337 
handle_io(struct ul_pty * pty,int fd,int * eof)338 static int handle_io(struct ul_pty *pty, int fd, int *eof)
339 {
340 	char buf[BUFSIZ];
341 	ssize_t bytes;
342 	int rc = 0;
343 
344 	DBG(IO, ul_debugobj(pty, " handle I/O on fd=%d", fd));
345 	*eof = 0;
346 
347 	/* read from active FD */
348 	bytes = read(fd, buf, sizeof(buf));
349 	if (bytes < 0) {
350 		if (errno == EAGAIN || errno == EINTR)
351 			return 0;
352 		return -errno;
353 	}
354 
355 	if (bytes == 0) {
356 		*eof = 1;
357 		return 0;
358 	}
359 
360 	/* from stdin (user) to command */
361 	if (fd == STDIN_FILENO) {
362 		DBG(IO, ul_debugobj(pty, " stdin --> master %zd bytes", bytes));
363 
364 		if (write_to_child(pty, buf, bytes))
365 			return -errno;
366 
367 		/* without sync write_output() will write both input &
368 		 * shell output that looks like double echoing */
369 		fdatasync(pty->master);
370 
371 	/* from command (master) to stdout */
372 	} else if (fd == pty->master) {
373 		DBG(IO, ul_debugobj(pty, " master --> stdout %zd bytes", bytes));
374 		write_output(buf, bytes);
375 	}
376 
377 	if (pty->callbacks.log_stream_activity)
378 		rc = pty->callbacks.log_stream_activity(
379 					pty->callback_data, fd, buf, bytes);
380 
381 	return rc;
382 }
383 
ul_pty_wait_for_child(struct ul_pty * pty)384 void ul_pty_wait_for_child(struct ul_pty *pty)
385 {
386 	int status;
387 	pid_t pid;
388 	int options = 0;
389 
390 	if (pty->child == (pid_t) -1)
391 		return;
392 
393 	DBG(SIG, ul_debug("waiting for child [child=%d]", (int) pty->child));
394 
395 	if (ul_pty_is_running(pty)) {
396 		/* wait for specific child */
397 		options = WNOHANG;
398 		for (;;) {
399 			pid = waitpid(pty->child, &status, options);
400 			DBG(SIG, ul_debug(" waitpid done [rc=%d]", (int) pid));
401 			if (pid != (pid_t) - 1) {
402 				if (pty->callbacks.child_die)
403 					pty->callbacks.child_die(
404 							pty->callback_data,
405 							pty->child, status);
406 				ul_pty_set_child(pty, (pid_t) -1);
407 			} else
408 				break;
409 		}
410 	} else {
411 		/* final wait */
412 		while ((pid = wait3(&status, options, NULL)) > 0) {
413 			DBG(SIG, ul_debug(" wait3 done [rc=%d]", (int) pid));
414 			if (pid == pty->child) {
415 				if (pty->callbacks.child_die)
416 					pty->callbacks.child_die(
417 							pty->callback_data,
418 							pty->child, status);
419 				ul_pty_set_child(pty, (pid_t) -1);
420 			}
421 		}
422 	}
423 }
424 
handle_signal(struct ul_pty * pty,int fd)425 static int handle_signal(struct ul_pty *pty, int fd)
426 {
427 	struct signalfd_siginfo info;
428 	ssize_t bytes;
429 	int rc = 0;
430 
431 	DBG(SIG, ul_debugobj(pty, " handle signal on fd=%d", fd));
432 
433 	bytes = read(fd, &info, sizeof(info));
434 	if (bytes != sizeof(info)) {
435 		if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
436 			return 0;
437 		return -errno;
438 	}
439 
440 	switch (info.ssi_signo) {
441 	case SIGCHLD:
442 		DBG(SIG, ul_debugobj(pty, " get signal SIGCHLD"));
443 
444 		if (info.ssi_code == CLD_EXITED
445 		    || info.ssi_code == CLD_KILLED
446 		    || info.ssi_code == CLD_DUMPED) {
447 
448 			if (pty->callbacks.child_wait)
449 				pty->callbacks.child_wait(pty->callback_data,
450 							  pty->child);
451 			else
452 				ul_pty_wait_for_child(pty);
453 
454 		} else if (info.ssi_status == SIGSTOP && pty->child > 0)
455 			pty->callbacks.child_sigstop(pty->callback_data,
456 						     pty->child);
457 
458 		if (pty->child <= 0) {
459 			DBG(SIG, ul_debugobj(pty, " no child, setting leaving timeout"));
460 			pty->poll_timeout = 10;
461 			timerclear(&pty->next_callback_time);
462 		}
463 		return 0;
464 	case SIGWINCH:
465 		DBG(SIG, ul_debugobj(pty, " get signal SIGWINCH"));
466 		if (pty->isterm) {
467 			ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
468 			ioctl(pty->slave, TIOCSWINSZ, (char *)&pty->win);
469 
470 			if (pty->callbacks.log_signal)
471 				rc = pty->callbacks.log_signal(pty->callback_data,
472 							&info, (void *) &pty->win);
473 		}
474 		break;
475 	case SIGTERM:
476 		/* fallthrough */
477 	case SIGINT:
478 		/* fallthrough */
479 	case SIGQUIT:
480 		DBG(SIG, ul_debugobj(pty, " get signal SIG{TERM,INT,QUIT}"));
481 		pty->delivered_signal = info.ssi_signo;
482                 /* Child termination is going to generate SIGCHILD (see above) */
483 		if (pty->child > 0)
484 	                kill(pty->child, SIGTERM);
485 
486 		if (pty->callbacks.log_signal)
487 			rc = pty->callbacks.log_signal(pty->callback_data,
488 					&info, (void *) &pty->win);
489 		break;
490 	case SIGUSR1:
491 		DBG(SIG, ul_debugobj(pty, " get signal SIGUSR1"));
492 		if (pty->callbacks.flush_logs)
493 			rc = pty->callbacks.flush_logs(pty->callback_data);
494 		break;
495 	default:
496 		abort();
497 	}
498 
499 	return rc;
500 }
501 
502 /* loop in parent */
ul_pty_proxy_master(struct ul_pty * pty)503 int ul_pty_proxy_master(struct ul_pty *pty)
504 {
505 	int rc = 0, ret, eof = 0;
506 	enum {
507 		POLLFD_SIGNAL = 0,
508 		POLLFD_MASTER,
509 		POLLFD_STDIN
510 
511 	};
512 	struct pollfd pfd[] = {
513 		[POLLFD_SIGNAL] = { .fd = -1,		.events = POLLIN | POLLERR | POLLHUP },
514 		[POLLFD_MASTER] = { .fd = pty->master,  .events = POLLIN | POLLERR | POLLHUP },
515 		[POLLFD_STDIN]	= { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP }
516 	};
517 
518 	/* We use signalfd and standard signals by handlers are blocked
519 	 * at all
520 	 */
521 	assert(pty->sigfd >= 0);
522 
523 	pfd[POLLFD_SIGNAL].fd = pty->sigfd;
524 	pty->poll_timeout = -1;
525 
526 	while (!pty->delivered_signal) {
527 		size_t i;
528 		int errsv, timeout;
529 
530 		DBG(IO, ul_debugobj(pty, "--poll() loop--"));
531 
532 		/* note, callback usually updates @next_callback_time */
533 		if (timerisset(&pty->next_callback_time)) {
534 			struct timeval now;
535 
536 			DBG(IO, ul_debugobj(pty, " callback requested"));
537 			gettime_monotonic(&now);
538 			if (timercmp(&now, &pty->next_callback_time, >)) {
539 				rc = mainloop_callback(pty);
540 				if (rc)
541 					break;
542 			}
543 		}
544 
545 		/* set timeout */
546 		if (timerisset(&pty->next_callback_time)) {
547 			struct timeval now, rest;
548 
549 			gettime_monotonic(&now);
550 			timersub(&pty->next_callback_time, &now, &rest);
551 			timeout = (rest.tv_sec * 1000) +  (rest.tv_usec / 1000);
552 		} else
553 			timeout = pty->poll_timeout;
554 
555 		/* wait for input, signal or timeout */
556 		DBG(IO, ul_debugobj(pty, "calling poll() [timeout=%dms]", timeout));
557 		ret = poll(pfd, ARRAY_SIZE(pfd), timeout);
558 
559 		errsv = errno;
560 		DBG(IO, ul_debugobj(pty, "poll() rc=%d", ret));
561 
562 		/* error */
563 		if (ret < 0) {
564 			if (errsv == EAGAIN)
565 				continue;
566 			rc = -errno;
567 			break;
568 		}
569 
570 		/* timeout */
571 		if (ret == 0) {
572 			if (timerisset(&pty->next_callback_time)) {
573 				rc = mainloop_callback(pty);
574 				if (rc == 0)
575 					continue;
576 			} else
577 				rc = 0;
578 
579 			DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", timeout, rc));
580 			break;
581 		}
582 		/* event */
583 		for (i = 0; i < ARRAY_SIZE(pfd); i++) {
584 			rc = 0;
585 
586 			if (pfd[i].revents == 0)
587 				continue;
588 
589 			DBG(IO, ul_debugobj(pty, " active pfd[%s].fd=%d %s %s %s %s",
590 						i == POLLFD_STDIN  ? "stdin" :
591 						i == POLLFD_MASTER ? "master" :
592 						i == POLLFD_SIGNAL ? "signal" : "???",
593 						pfd[i].fd,
594 						pfd[i].revents & POLLIN  ? "POLLIN" : "",
595 						pfd[i].revents & POLLHUP ? "POLLHUP" : "",
596 						pfd[i].revents & POLLERR ? "POLLERR" : "",
597 						pfd[i].revents & POLLNVAL ? "POLLNVAL" : ""));
598 
599 			switch (i) {
600 			case POLLFD_STDIN:
601 			case POLLFD_MASTER:
602 				/* data */
603 				if (pfd[i].revents & POLLIN)
604 					rc = handle_io(pty, pfd[i].fd, &eof);
605 				/* EOF maybe detected by two ways:
606 				 *	A) poll() return POLLHUP event after close()
607 				 *	B) read() returns 0 (no data)
608 				 *
609 				 * POLLNVAL means that fd is closed.
610 				 */
611 				if ((pfd[i].revents & POLLHUP) || (pfd[i].revents & POLLNVAL) || eof) {
612 					DBG(IO, ul_debugobj(pty, " ignore FD"));
613 					pfd[i].fd = -1;
614 					if (i == POLLFD_STDIN) {
615 						ul_pty_write_eof_to_child(pty);
616 						DBG(IO, ul_debugobj(pty, "  ignore STDIN"));
617 					}
618 				}
619 				continue;
620 			case POLLFD_SIGNAL:
621 				rc = handle_signal(pty, pfd[i].fd);
622 				break;
623 			}
624 			if (rc)
625 				break;
626 		}
627 	}
628 
629 	pty_signals_cleanup(pty);
630 
631 	DBG(IO, ul_debug("poll() done [signal=%d, rc=%d]", pty->delivered_signal, rc));
632 	return rc;
633 }
634 
635 #ifdef TEST_PROGRAM_PTY
636 /*
637  * $ make test_pty
638  * $ ./test_pty
639  *
640  * ... and see for example tty(1) or "ps afu"
641  */
child_sigstop(void * data,pid_t child)642 static void child_sigstop(void *data __attribute__((__unused__)), pid_t child)
643 {
644 	kill(getpid(), SIGSTOP);
645 	kill(child, SIGCONT);
646 }
647 
main(int argc,char * argv[])648 int main(int argc, char *argv[])
649 {
650 	struct ul_pty_callbacks *cb;
651 	const char *shell, *command = NULL, *shname = NULL;
652 	int caught_signal = 0;
653 	pid_t child;
654 	struct ul_pty *pty;
655 
656 	shell = getenv("SHELL");
657 	if (shell == NULL)
658 		shell = _PATH_BSHELL;
659 	if (argc == 2)
660 		command = argv[1];
661 
662 	ul_pty_init_debug(0);
663 
664 	pty = ul_new_pty(isatty(STDIN_FILENO));
665 	if (!pty)
666 		err(EXIT_FAILURE, "failed to allocate PTY handler");
667 
668 	cb = ul_pty_get_callbacks(pty);
669 	cb->child_sigstop = child_sigstop;
670 
671 	if (ul_pty_setup(pty))
672 		err(EXIT_FAILURE, "failed to create pseudo-terminal");
673 
674 	fflush(stdout);			/* ??? */
675 
676 	switch ((int) (child = fork())) {
677 	case -1: /* error */
678 		ul_pty_cleanup(pty);
679 		err(EXIT_FAILURE, "cannot create child process");
680 		break;
681 
682 	case 0: /* child */
683 		ul_pty_init_slave(pty);
684 
685 		signal(SIGTERM, SIG_DFL); /* because /etc/csh.login */
686 
687 		shname = strrchr(shell, '/');
688 		shname = shname ? shname + 1 : shell;
689 
690 		if (command)
691 			execl(shell, shname, "-c", command, NULL);
692 		else
693 			execl(shell, shname, "-i", NULL);
694 		err(EXIT_FAILURE, "failed to execute %s", shell);
695 		break;
696 
697 	default:
698 		break;
699 	}
700 
701 	/* parent */
702 	ul_pty_set_child(pty, child);
703 
704 	/* this is the main loop */
705 	ul_pty_proxy_master(pty);
706 
707 	/* all done; cleanup and kill */
708 	caught_signal = ul_pty_get_delivered_signal(pty);
709 
710 	if (!caught_signal && ul_pty_get_child(pty) != (pid_t)-1)
711 		ul_pty_wait_for_child(pty);	/* final wait */
712 
713 	if (caught_signal && ul_pty_get_child(pty) != (pid_t)-1) {
714 		fprintf(stderr, "\nSession terminated, killing shell...");
715 		kill(child, SIGTERM);
716 		sleep(2);
717 		kill(child, SIGKILL);
718 		fprintf(stderr, " ...killed.\n");
719 	}
720 
721 	ul_pty_cleanup(pty);
722 	ul_free_pty(pty);
723 	return EXIT_SUCCESS;
724 }
725 
726 #endif /* TEST_PROGRAM */
727 
728