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