]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/pty-session.c
lib/pty: save sigmask, add API to free all resources
[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
ab61a038 74
6954895c
KZ
75int ul_pty_get_delivered_signal(struct ul_pty *pty)
76{
77 assert(pty);
78 return pty->delivered_signal;
79}
80
81struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty)
82{
83 assert(pty);
84 return &pty->callbacks;
85}
86
87void ul_pty_set_callback_data(struct ul_pty *pty, void *data)
88{
89 assert(pty);
90 pty->callback_data = data;
91}
92
93void ul_pty_set_child(struct ul_pty *pty, pid_t child)
94{
95 assert(pty);
96 pty->child = child;
97}
98
4d5b2fed
KZ
99int ul_pty_get_childfd(struct ul_pty *pty)
100{
101 assert(pty);
102 return pty->master;
103}
104
6954895c
KZ
105/* it's active when signals are redurected to sigfd */
106int ul_pty_is_running(struct ul_pty *pty)
107{
108 assert(pty);
109 return pty->sigfd >= 0;
110}
111
4d5b2fed
KZ
112void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv)
113{
114 assert(pty);
115 if (!tv) {
116 DBG(IO, ul_debugobj(pty, "mainloop time: clear"));
117 timerclear(&pty->next_callback_time);
118 } else {
119 pty->next_callback_time.tv_sec = tv->tv_sec;
120 pty->next_callback_time.tv_usec = tv->tv_usec;
121 DBG(IO, ul_debugobj(pty, "mainloop time: %ld.%06ld", tv->tv_sec, tv->tv_usec));
122 }
123}
124
6954895c
KZ
125/* call me before fork() */
126int ul_pty_setup(struct ul_pty *pty)
127{
128 struct termios slave_attrs;
129 int rc;
130
ab61a038
KZ
131 /* save the current signals setting */
132 sigprocmask(0, NULL, &pty->orgsig);
133
6954895c
KZ
134 if (pty->isterm) {
135 DBG(SETUP, ul_debugobj(pty, "create for terminal"));
136
137 /* original setting of the current terminal */
138 if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0)
139 return -errno;
140 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
141 /* create master+slave */
142 rc = openpty(&pty->master, &pty->slave, NULL, &pty->stdin_attrs, &pty->win);
143
144 /* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */
145 slave_attrs = pty->stdin_attrs;
146 cfmakeraw(&slave_attrs);
147 slave_attrs.c_lflag &= ~ECHO;
148 tcsetattr(STDIN_FILENO, TCSANOW, &slave_attrs);
149 } else {
150 DBG(SETUP, ul_debugobj(pty, "create for non-terminal"));
151 rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL);
152
153 if (!rc) {
154 tcgetattr(pty->slave, &slave_attrs);
155 slave_attrs.c_lflag &= ~ECHO;
156 tcsetattr(pty->slave, TCSANOW, &slave_attrs);
157 }
158 }
159
160 DBG(SETUP, ul_debugobj(pty, "pty setup done [master=%d, slave=%d, rc=%d]",
161 pty->master, pty->slave, rc));
162 return rc;
163}
164
165/* cleanup in parent process */
166void ul_pty_cleanup(struct ul_pty *pty)
167{
168 struct termios rtt;
169
170 if (pty->master == -1 || !pty->isterm)
171 return;
172
173 DBG(DONE, ul_debugobj(pty, "cleanup"));
174 rtt = pty->stdin_attrs;
175 tcsetattr(STDIN_FILENO, TCSADRAIN, &rtt);
176}
177
178/* call me in child process */
179void ul_pty_init_slave(struct ul_pty *pty)
180{
181 DBG(SETUP, ul_debugobj(pty, "initialize slave"));
182
183 setsid();
184
185 ioctl(pty->slave, TIOCSCTTY, 1);
186 close(pty->master);
187
188 dup2(pty->slave, STDIN_FILENO);
189 dup2(pty->slave, STDOUT_FILENO);
190 dup2(pty->slave, STDERR_FILENO);
191
192 close(pty->slave);
193
194 if (pty->sigfd >= 0)
195 close(pty->sigfd);
196
197 pty->slave = -1;
198 pty->master = -1;
199 pty->sigfd = -1;
200
201 sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
202
203 DBG(SETUP, ul_debugobj(pty, "... initialize slave done"));
204}
205
206static int write_output(char *obuf, ssize_t bytes)
207{
208 DBG(IO, ul_debug(" writing output"));
209
210 if (write_all(STDOUT_FILENO, obuf, bytes)) {
211 DBG(IO, ul_debug(" writing output *failed*"));
212 return -errno;
213 }
214
215 return 0;
216}
217
4d5b2fed 218static int write_to_child(struct ul_pty *pty, char *buf, size_t bufsz)
6954895c
KZ
219{
220 return write_all(pty->master, buf, bufsz);
221}
222
223/*
224 * The pty is usually faster than shell, so it's a good idea to wait until
225 * the previous message has been already read by shell from slave before we
226 * write to master. This is necessary especially for EOF situation when we can
227 * send EOF to master before shell is fully initialized, to workaround this
228 * problem we wait until slave is empty. For example:
229 *
230 * echo "date" | su --pty
231 *
232 * Unfortunately, the child (usually shell) can ignore stdin at all, so we
233 * don't wait forever to avoid dead locks...
234 *
235 * Note that su --pty is primarily designed for interactive sessions as it
236 * maintains master+slave tty stuff within the session. Use pipe to write to
237 * pty and assume non-interactive (tee-like) behavior is NOT well supported.
238 */
239static void write_eof_to_child(struct ul_pty *pty)
240{
241 unsigned int tries = 0;
242 struct pollfd fds[] = {
243 { .fd = pty->slave, .events = POLLIN }
244 };
245 char c = DEF_EOF;
246
247 DBG(IO, ul_debugobj(pty, " waiting for empty slave"));
248 while (poll(fds, 1, 10) == 1 && tries < 8) {
249 DBG(IO, ul_debugobj(pty, " slave is not empty"));
250 xusleep(250000);
251 tries++;
252 }
253 if (tries < 8)
254 DBG(IO, ul_debugobj(pty, " slave is empty now"));
255
256 DBG(IO, ul_debugobj(pty, " sending EOF to master"));
257 write_to_child(pty, &c, sizeof(char));
258}
259
4d5b2fed
KZ
260static int mainloop_callback(struct ul_pty *pty)
261{
262 if (!pty->callbacks.mainloop)
263 return 0;
264
265 DBG(IO, ul_debugobj(pty, "calling mainloop callback"));
266 return pty->callbacks.mainloop(pty->callback_data);
267}
268
6954895c
KZ
269static int handle_io(struct ul_pty *pty, int fd, int *eof)
270{
271 char buf[BUFSIZ];
272 ssize_t bytes;
273
274 DBG(IO, ul_debugobj(pty, "%d FD active", fd));
275 *eof = 0;
276
277 /* read from active FD */
278 bytes = read(fd, buf, sizeof(buf));
279 if (bytes < 0) {
280 if (errno == EAGAIN || errno == EINTR)
281 return 0;
282 return -errno;
283 }
284
285 if (bytes == 0) {
286 *eof = 1;
287 return 0;
288 }
289
290 /* from stdin (user) to command */
291 if (fd == STDIN_FILENO) {
292 DBG(IO, ul_debugobj(pty, " stdin --> master %zd bytes", bytes));
293
294 if (write_to_child(pty, buf, bytes))
295 return -errno;
296
297 /* without sync write_output() will write both input &
298 * shell output that looks like double echoing */
299 fdatasync(pty->master);
300
301 /* from command (master) to stdout */
302 } else if (fd == pty->master) {
303 DBG(IO, ul_debugobj(pty, " master --> stdout %zd bytes", bytes));
304 write_output(buf, bytes);
305 }
306
307 return 0;
308}
309
310static int handle_signal(struct ul_pty *pty, int fd)
311{
312 struct signalfd_siginfo info;
313 ssize_t bytes;
314
315 DBG(SIG, ul_debugobj(pty, "signal FD %d active", fd));
316
317 bytes = read(fd, &info, sizeof(info));
318 if (bytes != sizeof(info)) {
319 if (bytes < 0 && (errno == EAGAIN || errno == EINTR))
320 return 0;
321 return -errno;
322 }
323
324 switch (info.ssi_signo) {
325 case SIGCHLD:
326 DBG(SIG, ul_debugobj(pty, " get signal SIGCHLD"));
327
328 if (info.ssi_code == CLD_EXITED
329 || info.ssi_code == CLD_KILLED
330 || info.ssi_code == CLD_DUMPED)
331 pty->callbacks.child_wait(pty->callback_data);
332
333 else if (info.ssi_status == SIGSTOP && pty->child > 0)
334 pty->callbacks.child_sigstop(pty->callback_data);
335
984082fa 336 if (pty->child <= 0) {
6954895c 337 pty->poll_timeout = 10;
984082fa
KZ
338 timerclear(&pty->next_callback_time);
339 }
6954895c
KZ
340 return 0;
341 case SIGWINCH:
342 DBG(SIG, ul_debugobj(pty, " get signal SIGWINCH"));
343 if (pty->isterm) {
344 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
345 ioctl(pty->slave, TIOCSWINSZ, (char *)&pty->win);
346 }
347 break;
348 case SIGTERM:
349 /* fallthrough */
350 case SIGINT:
351 /* fallthrough */
352 case SIGQUIT:
353 DBG(SIG, ul_debugobj(pty, " get signal SIG{TERM,INT,QUIT}"));
354 pty->delivered_signal = info.ssi_signo;
355 /* Child termination is going to generate SIGCHILD (see above) */
356 if (pty->child > 0)
357 kill(pty->child, SIGTERM);
358 break;
359 default:
360 abort();
361 }
362
363 return 0;
364}
365
366/* loop in parent */
367int ul_pty_proxy_master(struct ul_pty *pty)
368{
369 sigset_t ourset;
370 int rc = 0, ret, eof = 0;
371 enum {
372 POLLFD_SIGNAL = 0,
373 POLLFD_MASTER,
374 POLLFD_STDIN
375
376 };
377 struct pollfd pfd[] = {
378 [POLLFD_SIGNAL] = { .fd = -1, .events = POLLIN | POLLERR | POLLHUP },
379 [POLLFD_MASTER] = { .fd = pty->master, .events = POLLIN | POLLERR | POLLHUP },
380 [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP }
381 };
382
383 /* We use signalfd and standard signals by handlers are blocked
384 * at all
385 */
386 sigfillset(&ourset);
387 if (sigprocmask(SIG_BLOCK, &ourset, NULL))
388 return -errno;
389
390 sigemptyset(&ourset);
391 sigaddset(&ourset, SIGCHLD);
392 sigaddset(&ourset, SIGWINCH);
393 sigaddset(&ourset, SIGALRM);
394 sigaddset(&ourset, SIGTERM);
395 sigaddset(&ourset, SIGINT);
396 sigaddset(&ourset, SIGQUIT);
397
398 if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0) {
399 rc = -errno;
400 goto done;
401 }
402
403 pfd[POLLFD_SIGNAL].fd = pty->sigfd;
404 pty->poll_timeout = -1;
405
406 while (!pty->delivered_signal) {
407 size_t i;
4d5b2fed
KZ
408 int errsv, timeout;
409
410 DBG(IO, ul_debugobj(pty, "--poll() loop--"));
411
412 /* note, callback usually updates @next_callback_time */
413 if (timerisset(&pty->next_callback_time)) {
414 struct timeval now;
415
416 DBG(IO, ul_debugobj(pty, " callback requested"));
417 gettime_monotonic(&now);
418 if (timercmp(&now, &pty->next_callback_time, >)) {
419 rc = mainloop_callback(pty);
420 if (rc)
421 break;
422 }
423 }
424
425 /* set timeout */
426 if (timerisset(&pty->next_callback_time)) {
427 struct timeval now, rest;
428
429 gettime_monotonic(&now);
430 timersub(&pty->next_callback_time, &now, &rest);
431 timeout = (rest.tv_sec * 1000) + (rest.tv_usec / 1000);
432 } else
433 timeout = pty->poll_timeout;
6954895c 434
4d5b2fed
KZ
435 /* wait for input, signal or timeout */
436 DBG(IO, ul_debugobj(pty, "calling poll() [timeout=%dms]", timeout));
437 ret = poll(pfd, ARRAY_SIZE(pfd), timeout);
6954895c 438
6954895c
KZ
439 errsv = errno;
440 DBG(IO, ul_debugobj(pty, "poll() rc=%d", ret));
441
4d5b2fed 442 /* error */
6954895c
KZ
443 if (ret < 0) {
444 if (errsv == EAGAIN)
445 continue;
446 rc = -errno;
447 break;
448 }
4d5b2fed
KZ
449
450 /* timeout */
6954895c 451 if (ret == 0) {
4d5b2fed
KZ
452 if (timerisset(&pty->next_callback_time)) {
453 rc = mainloop_callback(pty);
454 if (rc == 0)
455 continue;
456 } else
457 rc = 0;
458
984082fa 459 DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", timeout, rc));
6954895c
KZ
460 break;
461 }
4d5b2fed 462 /* event */
6954895c
KZ
463 for (i = 0; i < ARRAY_SIZE(pfd); i++) {
464 rc = 0;
465
466 if (pfd[i].revents == 0)
467 continue;
468
469 DBG(IO, ul_debugobj(pty, " active pfd[%s].fd=%d %s %s %s",
470 i == POLLFD_STDIN ? "stdin" :
471 i == POLLFD_MASTER ? "master" :
472 i == POLLFD_SIGNAL ? "signal" : "???",
473 pfd[i].fd,
474 pfd[i].revents & POLLIN ? "POLLIN" : "",
475 pfd[i].revents & POLLHUP ? "POLLHUP" : "",
476 pfd[i].revents & POLLERR ? "POLLERR" : ""));
477 switch (i) {
478 case POLLFD_STDIN:
479 case POLLFD_MASTER:
480 /* data */
481 if (pfd[i].revents & POLLIN)
482 rc = handle_io(pty, pfd[i].fd, &eof);
483 /* EOF maybe detected by two ways:
484 * A) poll() return POLLHUP event after close()
485 * B) read() returns 0 (no data) */
486 if ((pfd[i].revents & POLLHUP) || eof) {
487 DBG(IO, ul_debugobj(pty, " ignore FD"));
488 pfd[i].fd = -1;
489 if (i == POLLFD_STDIN) {
490 write_eof_to_child(pty);
491 DBG(IO, ul_debugobj(pty, " ignore STDIN"));
492 }
493 }
494 continue;
495 case POLLFD_SIGNAL:
496 rc = handle_signal(pty, pfd[i].fd);
497 break;
498 }
499 if (rc)
500 break;
501 }
502 }
503
504done:
505 if (pty->sigfd != -1)
506 close(pty->sigfd);
507 pty->sigfd = -1;
508
509 /* restore original setting */
510 sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
511
512 DBG(IO, ul_debug("poll() done [signal=%d, rc=%d]", pty->delivered_signal, rc));
513 return rc;
514}
515
516#ifdef TEST_PROGRAM_PTY
517/*
518 * $ make test_pty
519 * $ ./test_pty
520 *
521 * ... and see for example tty(1) or "ps afu"
522 */
523struct ptytest {
524 pid_t child;
525 int childstatus;
526
527 struct ul_pty *pty;
528};
529
530/* on child exit/dump/... */
531static void wait_for_child(void *data)
532{
533 struct ptytest *ss = (struct ptytest *) data;
534 int status;
535 pid_t pid;
536 int options = 0;
537
538 if (ss->child == (pid_t) -1)
539 return;
540
541 if (ul_pty_is_running(ss->pty)) {
542 /* wait for specific child */
543 options = WNOHANG;
544 for (;;) {
545 pid = waitpid(ss->child, &status, options);
546 if (pid != (pid_t) - 1) {
547 ss->childstatus = status;
548 ss->child = (pid_t) -1;
549 ul_pty_set_child(ss->pty, (pid_t) -1);
550 } else
551 break;
552 }
553 } else {
554 /* final wait */
555 while ((pid = wait3(&status, options, NULL)) > 0) {
556 if (pid == ss->child) {
557 ss->childstatus = status;
558 ss->child = (pid_t) -1;
559 ul_pty_set_child(ss->pty, (pid_t) -1);
560 }
561 }
562 }
563}
564
565static void child_sigstop(void *data)
566{
567 struct ptytest *ss = (struct ptytest *) data;
568 kill(getpid(), SIGSTOP);
569 kill(ss->child, SIGCONT);
570}
571
572int main(int argc, char *argv[])
573{
574 struct ptytest ss = { .child = (pid_t) -1 };
575 struct ul_pty_callbacks *cb;
576 const char *shell, *command = NULL, *shname = NULL;
577 int caught_signal = 0;
578
579 shell = getenv("SHELL");
580 if (shell == NULL)
581 shell = _PATH_BSHELL;
582 if (argc == 2)
583 command = argv[1];
584
585 ul_pty_init_debug(0);
586
587 ss.pty = ul_new_pty(isatty(STDIN_FILENO));
588 if (!ss.pty)
589 err(EXIT_FAILURE, "failed to allocate PTY handler");
590
591 ul_pty_set_callback_data(ss.pty, (void *) &ss);
592 cb = ul_pty_get_callbacks(ss.pty);
593 cb->child_wait = wait_for_child;
594 cb->child_sigstop = child_sigstop;
595
6954895c
KZ
596 if (ul_pty_setup(ss.pty))
597 err(EXIT_FAILURE, "failed to create pseudo-terminal");
598
599 fflush(stdout); /* ??? */
600
601 switch ((int) (ss.child = fork())) {
602 case -1: /* error */
603 ul_pty_cleanup(ss.pty);
604 err(EXIT_FAILURE, "cannot create child process");
605 break;
606
607 case 0: /* child */
608 ul_pty_init_slave(ss.pty);
609
610 signal(SIGTERM, SIG_DFL); /* because /etc/csh.login */
611
612 shname = strrchr(shell, '/');
613 shname = shname ? shname + 1 : shell;
614
615 if (command)
616 execl(shell, shname, "-c", command, NULL);
617 else
618 execl(shell, shname, "-i", NULL);
619 err(EXIT_FAILURE, "failed to execute %s", shell);
620 break;
621
622 default:
623 break;
624 }
625
626 /* parent */
627 ul_pty_set_child(ss.pty, ss.child);
628
629 /* this is the main loop */
630 ul_pty_proxy_master(ss.pty);
631
632 /* all done; cleanup and kill */
633 caught_signal = ul_pty_get_delivered_signal(ss.pty);
634
635 if (!caught_signal && ss.child != (pid_t)-1)
636 wait_for_child(&ss); /* final wait */
637
638 if (caught_signal && ss.child != (pid_t)-1) {
639 fprintf(stderr, "\nSession terminated, killing shell...");
640 kill(ss.child, SIGTERM);
641 sleep(2);
642 kill(ss.child, SIGKILL);
643 fprintf(stderr, " ...killed.\n");
644 }
645
646 ul_pty_cleanup(ss.pty);
647 return EXIT_SUCCESS;
648}
649
650#endif /* TEST_PROGRAM */
651