]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/pty-session.c
kill: add missing ifdefs
[thirdparty/util-linux.git] / lib / pty-session.c
CommitLineData
6954895c
KZ
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"
4d5b2fed 26#include "monotonic.h"
6954895c
KZ
27#include "debug.h"
28
29static UL_DEBUG_DEFINE_MASK(ulpty);
30UL_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
45void 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
52struct 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
ab61a038 69void ul_free_pty(struct ul_pty *pty)
6954895c 70{
ab61a038 71 free(pty);
6954895c
KZ
72}
73
1eee1acb 74void ul_pty_slave_echo(struct ul_pty *pty, int enable)
4169bcb7
KZ
75{
76 assert(pty);
1eee1acb 77 pty->slave_echo = enable ? 1 : 0;
4169bcb7 78}
ab61a038 79
6954895c
KZ
80int ul_pty_get_delivered_signal(struct ul_pty *pty)
81{
82 assert(pty);
83 return pty->delivered_signal;
84}
85
86struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty)
87{
88 assert(pty);
89 return &pty->callbacks;
90}
91
92void ul_pty_set_callback_data(struct ul_pty *pty, void *data)
93{
94 assert(pty);
95 pty->callback_data = data;
96}
97
98void ul_pty_set_child(struct ul_pty *pty, pid_t child)
99{
100 assert(pty);
101 pty->child = child;
102}
103
4d5b2fed
KZ
104int ul_pty_get_childfd(struct ul_pty *pty)
105{
106 assert(pty);
107 return pty->master;
108}
109
bdd43357
KZ
110pid_t ul_pty_get_child(struct ul_pty *pty)
111{
112 assert(pty);
113 return pty->child;
114}
115
6954895c
KZ
116/* it's active when signals are redurected to sigfd */
117int ul_pty_is_running(struct ul_pty *pty)
118{
119 assert(pty);
120 return pty->sigfd >= 0;
121}
122
4d5b2fed
KZ
123void 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
b1154c4e
KZ
136static 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
6954895c
KZ
146/* call me before fork() */
147int ul_pty_setup(struct ul_pty *pty)
148{
149 struct termios slave_attrs;
b1154c4e
KZ
150 sigset_t ourset;
151 int rc = 0;
152
153 assert(pty->sigfd == -1);
6954895c 154
ab61a038
KZ
155 /* save the current signals setting */
156 sigprocmask(0, NULL, &pty->orgsig);
157
6954895c
KZ
158 if (pty->isterm) {
159 DBG(SETUP, ul_debugobj(pty, "create for terminal"));
160
161 /* original setting of the current terminal */
b1154c4e
KZ
162 if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0) {
163 rc = -errno;
164 goto done;
165 }
6954895c
KZ
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);
b1154c4e
KZ
169 if (rc)
170 goto done;
6954895c
KZ
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);
1eee1acb
KZ
175
176 if (pty->slave_echo)
177 slave_attrs.c_lflag |= ECHO;
178 else
179 slave_attrs.c_lflag &= ~ECHO;
180
6954895c
KZ
181 tcsetattr(STDIN_FILENO, TCSANOW, &slave_attrs);
182 } else {
183 DBG(SETUP, ul_debugobj(pty, "create for non-terminal"));
6954895c 184
1eee1acb
KZ
185 rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL);
186 if (rc)
b1154c4e 187 goto done;
1eee1acb
KZ
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);
b1154c4e
KZ
197 }
198
199 sigfillset(&ourset);
200 if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
201 rc = -errno;
202 goto done;
6954895c
KZ
203 }
204
b1154c4e
KZ
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
7727be1a
KZ
213 if (pty->callbacks.flush_logs)
214 sigaddset(&ourset, SIGUSR1);
215
b1154c4e
KZ
216 if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0)
217 rc = -errno;
218done:
219 if (rc)
220 ul_pty_cleanup(pty);
221
6954895c
KZ
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 */
228void ul_pty_cleanup(struct ul_pty *pty)
229{
230 struct termios rtt;
231
b1154c4e
KZ
232 pty_signals_cleanup(pty);
233
6954895c
KZ
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 */
243void 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
270static 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
4d5b2fed 282static int write_to_child(struct ul_pty *pty, char *buf, size_t bufsz)
6954895c
KZ
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 */
95d255a8 303void ul_pty_write_eof_to_child(struct ul_pty *pty)
6954895c
KZ
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
4d5b2fed
KZ
324static int mainloop_callback(struct ul_pty *pty)
325{
f896aef3
KZ
326 int rc;
327
4d5b2fed
KZ
328 if (!pty->callbacks.mainloop)
329 return 0;
330
331 DBG(IO, ul_debugobj(pty, "calling mainloop callback"));
f896aef3
KZ
332 rc = pty->callbacks.mainloop(pty->callback_data);
333
334 DBG(IO, ul_debugobj(pty, " callback done [rc=%d]", rc));
335 return rc;
4d5b2fed
KZ
336}
337
6954895c
KZ
338static int handle_io(struct ul_pty *pty, int fd, int *eof)
339{
340 char buf[BUFSIZ];
341 ssize_t bytes;
4f7f723b 342 int rc = 0;
6954895c 343
c4bacbd1 344 DBG(IO, ul_debugobj(pty, " handle I/O on fd=%d", fd));
6954895c
KZ
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
4f7f723b
KZ
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;
6954895c
KZ
382}
383
bdd43357
KZ
384void 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
125314c0 393 DBG(SIG, ul_debug("waiting for child [child=%d]", (int) pty->child));
bdd43357
KZ
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);
125314c0 400 DBG(SIG, ul_debug(" waitpid done [rc=%d]", (int) pid));
bdd43357
KZ
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) {
125314c0 413 DBG(SIG, ul_debug(" wait3 done [rc=%d]", (int) pid));
bdd43357
KZ
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
6954895c
KZ
425static int handle_signal(struct ul_pty *pty, int fd)
426{
427 struct signalfd_siginfo info;
428 ssize_t bytes;
4f7f723b 429 int rc = 0;
6954895c 430
c4bacbd1 431 DBG(SIG, ul_debugobj(pty, " handle signal on fd=%d", fd));
6954895c
KZ
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
bdd43357
KZ
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);
6954895c 453
bdd43357
KZ
454 } else if (info.ssi_status == SIGSTOP && pty->child > 0)
455 pty->callbacks.child_sigstop(pty->callback_data,
456 pty->child);
6954895c 457
984082fa 458 if (pty->child <= 0) {
125314c0 459 DBG(SIG, ul_debugobj(pty, " no child, setting leaving timeout"));
6954895c 460 pty->poll_timeout = 10;
984082fa
KZ
461 timerclear(&pty->next_callback_time);
462 }
6954895c
KZ
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);
4f7f723b
KZ
469
470 if (pty->callbacks.log_signal)
471 rc = pty->callbacks.log_signal(pty->callback_data,
472 &info, (void *) &pty->win);
6954895c
KZ
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);
4f7f723b
KZ
485
486 if (pty->callbacks.log_signal)
487 rc = pty->callbacks.log_signal(pty->callback_data,
488 &info, (void *) &pty->win);
6954895c 489 break;
7727be1a
KZ
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;
6954895c
KZ
495 default:
496 abort();
497 }
498
4f7f723b 499 return rc;
6954895c
KZ
500}
501
502/* loop in parent */
503int ul_pty_proxy_master(struct ul_pty *pty)
504{
6954895c
KZ
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 */
b1154c4e 521 assert(pty->sigfd >= 0);
6954895c
KZ
522
523 pfd[POLLFD_SIGNAL].fd = pty->sigfd;
524 pty->poll_timeout = -1;
525
526 while (!pty->delivered_signal) {
527 size_t i;
4d5b2fed
KZ
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;
6954895c 554
4d5b2fed
KZ
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);
6954895c 558
6954895c
KZ
559 errsv = errno;
560 DBG(IO, ul_debugobj(pty, "poll() rc=%d", ret));
561
4d5b2fed 562 /* error */
6954895c
KZ
563 if (ret < 0) {
564 if (errsv == EAGAIN)
565 continue;
566 rc = -errno;
567 break;
568 }
4d5b2fed
KZ
569
570 /* timeout */
6954895c 571 if (ret == 0) {
4d5b2fed
KZ
572 if (timerisset(&pty->next_callback_time)) {
573 rc = mainloop_callback(pty);
574 if (rc == 0)
575 continue;
576 } else
577 rc = 0;
578
984082fa 579 DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", timeout, rc));
6954895c
KZ
580 break;
581 }
4d5b2fed 582 /* event */
6954895c
KZ
583 for (i = 0; i < ARRAY_SIZE(pfd); i++) {
584 rc = 0;
585
586 if (pfd[i].revents == 0)
587 continue;
588
f896aef3 589 DBG(IO, ul_debugobj(pty, " active pfd[%s].fd=%d %s %s %s %s",
6954895c
KZ
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" : "",
f896aef3
KZ
596 pfd[i].revents & POLLERR ? "POLLERR" : "",
597 pfd[i].revents & POLLNVAL ? "POLLNVAL" : ""));
598
6954895c
KZ
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()
f896aef3
KZ
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) {
6954895c
KZ
612 DBG(IO, ul_debugobj(pty, " ignore FD"));
613 pfd[i].fd = -1;
614 if (i == POLLFD_STDIN) {
95d255a8 615 ul_pty_write_eof_to_child(pty);
6954895c
KZ
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
b1154c4e 629 pty_signals_cleanup(pty);
6954895c
KZ
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 */
bdd43357 642static void child_sigstop(void *data __attribute__((__unused__)), pid_t child)
6954895c 643{
6954895c 644 kill(getpid(), SIGSTOP);
bdd43357 645 kill(child, SIGCONT);
6954895c
KZ
646}
647
648int main(int argc, char *argv[])
649{
6954895c
KZ
650 struct ul_pty_callbacks *cb;
651 const char *shell, *command = NULL, *shname = NULL;
652 int caught_signal = 0;
bdd43357 653 pid_t child;
8cf448e9 654 struct ul_pty *pty;
6954895c
KZ
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
8cf448e9
KZ
664 pty = ul_new_pty(isatty(STDIN_FILENO));
665 if (!pty)
6954895c
KZ
666 err(EXIT_FAILURE, "failed to allocate PTY handler");
667
8cf448e9 668 cb = ul_pty_get_callbacks(pty);
6954895c
KZ
669 cb->child_sigstop = child_sigstop;
670
8cf448e9 671 if (ul_pty_setup(pty))
6954895c
KZ
672 err(EXIT_FAILURE, "failed to create pseudo-terminal");
673
674 fflush(stdout); /* ??? */
675
bdd43357 676 switch ((int) (child = fork())) {
6954895c 677 case -1: /* error */
8cf448e9 678 ul_pty_cleanup(pty);
6954895c
KZ
679 err(EXIT_FAILURE, "cannot create child process");
680 break;
681
682 case 0: /* child */
8cf448e9 683 ul_pty_init_slave(pty);
6954895c
KZ
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 */
8cf448e9 702 ul_pty_set_child(pty, child);
6954895c
KZ
703
704 /* this is the main loop */
8cf448e9 705 ul_pty_proxy_master(pty);
6954895c
KZ
706
707 /* all done; cleanup and kill */
8cf448e9 708 caught_signal = ul_pty_get_delivered_signal(pty);
6954895c 709
8cf448e9
KZ
710 if (!caught_signal && ul_pty_get_child(pty) != (pid_t)-1)
711 ul_pty_wait_for_child(pty); /* final wait */
6954895c 712
8cf448e9 713 if (caught_signal && ul_pty_get_child(pty) != (pid_t)-1) {
6954895c 714 fprintf(stderr, "\nSession terminated, killing shell...");
bdd43357 715 kill(child, SIGTERM);
6954895c 716 sleep(2);
bdd43357 717 kill(child, SIGKILL);
6954895c
KZ
718 fprintf(stderr, " ...killed.\n");
719 }
720
8cf448e9
KZ
721 ul_pty_cleanup(pty);
722 ul_free_pty(pty);
6954895c
KZ
723 return EXIT_SUCCESS;
724}
725
726#endif /* TEST_PROGRAM */
727