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