]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - lib/pty-session.c
script: fix signalfd use
[thirdparty/util-linux.git] / lib / pty-session.c
index 1c6b559691bc57923c8e06ba141011ed981f1fd1..5486fd06883c8576b1f30426b1cb0953f1a9b442 100644 (file)
@@ -102,6 +102,12 @@ int ul_pty_get_childfd(struct ul_pty *pty)
        return pty->master;
 }
 
+pid_t ul_pty_get_child(struct ul_pty *pty)
+{
+       assert(pty);
+       return pty->child;
+}
+
 /* it's active when signals are redurected to sigfd */
 int ul_pty_is_running(struct ul_pty *pty)
 {
@@ -122,11 +128,24 @@ void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv)
        }
 }
 
+static void pty_signals_cleanup(struct ul_pty *pty)
+{
+       if (pty->sigfd != -1)
+               close(pty->sigfd);
+       pty->sigfd = -1;
+
+       /* restore original setting */
+       sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
+}
+
 /* call me before fork() */
 int ul_pty_setup(struct ul_pty *pty)
 {
        struct termios slave_attrs;
-       int rc;
+       sigset_t ourset;
+       int rc = 0;
+
+       assert(pty->sigfd == -1);
 
        /* save the current signals setting */
        sigprocmask(0, NULL, &pty->orgsig);
@@ -135,11 +154,15 @@ int ul_pty_setup(struct ul_pty *pty)
                DBG(SETUP, ul_debugobj(pty, "create for terminal"));
 
                /* original setting of the current terminal */
-               if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0)
-                       return -errno;
+               if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0) {
+                       rc = -errno;
+                       goto done;
+               }
                ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
                /* create master+slave */
                rc = openpty(&pty->master, &pty->slave, NULL, &pty->stdin_attrs, &pty->win);
+               if (rc)
+                       goto done;
 
                /* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */
                slave_attrs = pty->stdin_attrs;
@@ -154,9 +177,30 @@ int ul_pty_setup(struct ul_pty *pty)
                        tcgetattr(pty->slave, &slave_attrs);
                        slave_attrs.c_lflag &= ~ECHO;
                        tcsetattr(pty->slave, TCSANOW, &slave_attrs);
-               }
+               } else
+                       goto done;
        }
 
+       sigfillset(&ourset);
+       if (sigprocmask(SIG_BLOCK, &ourset, NULL)) {
+               rc = -errno;
+               goto done;
+       }
+
+       sigemptyset(&ourset);
+       sigaddset(&ourset, SIGCHLD);
+       sigaddset(&ourset, SIGWINCH);
+       sigaddset(&ourset, SIGALRM);
+       sigaddset(&ourset, SIGTERM);
+       sigaddset(&ourset, SIGINT);
+       sigaddset(&ourset, SIGQUIT);
+
+       if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0)
+               rc = -errno;
+done:
+       if (rc)
+               ul_pty_cleanup(pty);
+
        DBG(SETUP, ul_debugobj(pty, "pty setup done [master=%d, slave=%d, rc=%d]",
                                pty->master, pty->slave, rc));
        return rc;
@@ -167,6 +211,8 @@ void ul_pty_cleanup(struct ul_pty *pty)
 {
        struct termios rtt;
 
+       pty_signals_cleanup(pty);
+
        if (pty->master == -1 || !pty->isterm)
                return;
 
@@ -270,8 +316,9 @@ static int handle_io(struct ul_pty *pty, int fd, int *eof)
 {
        char buf[BUFSIZ];
        ssize_t bytes;
+       int rc = 0;
 
-       DBG(IO, ul_debugobj(pty, "%d FD active", fd));
+       DBG(IO, ul_debugobj(pty, " handle I/O on fd=%d", fd));
        *eof = 0;
 
        /* read from active FD */
@@ -304,15 +351,61 @@ static int handle_io(struct ul_pty *pty, int fd, int *eof)
                write_output(buf, bytes);
        }
 
-       return 0;
+       if (pty->callbacks.log_stream_activity)
+               rc = pty->callbacks.log_stream_activity(
+                                       pty->callback_data, fd, buf, bytes);
+
+       return rc;
+}
+
+void ul_pty_wait_for_child(struct ul_pty *pty)
+{
+       int status;
+       pid_t pid;
+       int options = 0;
+
+       if (pty->child == (pid_t) -1)
+               return;
+
+       DBG(SIG, ul_debug("waiting for child [child=%d]", (int) pty->child));
+
+       if (ul_pty_is_running(pty)) {
+               /* wait for specific child */
+               options = WNOHANG;
+               for (;;) {
+                       pid = waitpid(pty->child, &status, options);
+                       DBG(SIG, ul_debug(" waitpid done [rc=%d]", (int) pid));
+                       if (pid != (pid_t) - 1) {
+                               if (pty->callbacks.child_die)
+                                       pty->callbacks.child_die(
+                                                       pty->callback_data,
+                                                       pty->child, status);
+                               ul_pty_set_child(pty, (pid_t) -1);
+                       } else
+                               break;
+               }
+       } else {
+               /* final wait */
+               while ((pid = wait3(&status, options, NULL)) > 0) {
+                       DBG(SIG, ul_debug(" wait3 done [rc=%d]", (int) pid));
+                       if (pid == pty->child) {
+                               if (pty->callbacks.child_die)
+                                       pty->callbacks.child_die(
+                                                       pty->callback_data,
+                                                       pty->child, status);
+                               ul_pty_set_child(pty, (pid_t) -1);
+                       }
+               }
+       }
 }
 
 static int handle_signal(struct ul_pty *pty, int fd)
 {
        struct signalfd_siginfo info;
        ssize_t bytes;
+       int rc = 0;
 
-       DBG(SIG, ul_debugobj(pty, "signal FD %d active", fd));
+       DBG(SIG, ul_debugobj(pty, " handle signal on fd=%d", fd));
 
        bytes = read(fd, &info, sizeof(info));
        if (bytes != sizeof(info)) {
@@ -327,13 +420,20 @@ static int handle_signal(struct ul_pty *pty, int fd)
 
                if (info.ssi_code == CLD_EXITED
                    || info.ssi_code == CLD_KILLED
-                   || info.ssi_code == CLD_DUMPED)
-                       pty->callbacks.child_wait(pty->callback_data);
+                   || info.ssi_code == CLD_DUMPED) {
 
-               else if (info.ssi_status == SIGSTOP && pty->child > 0)
-                       pty->callbacks.child_sigstop(pty->callback_data);
+                       if (pty->callbacks.child_wait)
+                               pty->callbacks.child_wait(pty->callback_data,
+                                                         pty->child);
+                       else
+                               ul_pty_wait_for_child(pty);
+
+               } else if (info.ssi_status == SIGSTOP && pty->child > 0)
+                       pty->callbacks.child_sigstop(pty->callback_data,
+                                                    pty->child);
 
                if (pty->child <= 0) {
+                       DBG(SIG, ul_debugobj(pty, " no child, setting leaving timeout"));
                        pty->poll_timeout = 10;
                        timerclear(&pty->next_callback_time);
                }
@@ -343,6 +443,10 @@ static int handle_signal(struct ul_pty *pty, int fd)
                if (pty->isterm) {
                        ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win);
                        ioctl(pty->slave, TIOCSWINSZ, (char *)&pty->win);
+
+                       if (pty->callbacks.log_signal)
+                               rc = pty->callbacks.log_signal(pty->callback_data,
+                                                       &info, (void *) &pty->win);
                }
                break;
        case SIGTERM:
@@ -355,18 +459,21 @@ static int handle_signal(struct ul_pty *pty, int fd)
                 /* Child termination is going to generate SIGCHILD (see above) */
                if (pty->child > 0)
                        kill(pty->child, SIGTERM);
+
+               if (pty->callbacks.log_signal)
+                       rc = pty->callbacks.log_signal(pty->callback_data,
+                                       &info, (void *) &pty->win);
                break;
        default:
                abort();
        }
 
-       return 0;
+       return rc;
 }
 
 /* loop in parent */
 int ul_pty_proxy_master(struct ul_pty *pty)
 {
-       sigset_t ourset;
        int rc = 0, ret, eof = 0;
        enum {
                POLLFD_SIGNAL = 0,
@@ -383,22 +490,7 @@ int ul_pty_proxy_master(struct ul_pty *pty)
        /* We use signalfd and standard signals by handlers are blocked
         * at all
         */
-       sigfillset(&ourset);
-       if (sigprocmask(SIG_BLOCK, &ourset, NULL))
-               return -errno;
-
-       sigemptyset(&ourset);
-       sigaddset(&ourset, SIGCHLD);
-       sigaddset(&ourset, SIGWINCH);
-       sigaddset(&ourset, SIGALRM);
-       sigaddset(&ourset, SIGTERM);
-       sigaddset(&ourset, SIGINT);
-       sigaddset(&ourset, SIGQUIT);
-
-       if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0) {
-               rc = -errno;
-               goto done;
-       }
+       assert(pty->sigfd >= 0);
 
        pfd[POLLFD_SIGNAL].fd = pty->sigfd;
        pty->poll_timeout = -1;
@@ -501,13 +593,7 @@ int ul_pty_proxy_master(struct ul_pty *pty)
                }
        }
 
-done:
-       if (pty->sigfd != -1)
-               close(pty->sigfd);
-       pty->sigfd = -1;
-
-       /* restore original setting */
-       sigprocmask(SIG_SETMASK, &pty->orgsig, NULL);
+       pty_signals_cleanup(pty);
 
        DBG(IO, ul_debug("poll() done [signal=%d, rc=%d]", pty->delivered_signal, rc));
        return rc;
@@ -520,61 +606,19 @@ done:
  *
  * ... and see for example tty(1) or "ps afu"
  */
-struct ptytest {
-       pid_t   child;
-       int     childstatus;
-
-       struct ul_pty *pty;
-};
-
-/* on child exit/dump/... */
-static void wait_for_child(void *data)
+static void child_sigstop(void *data __attribute__((__unused__)), pid_t child)
 {
-       struct ptytest *ss = (struct ptytest *) data;
-       int status;
-       pid_t pid;
-       int options = 0;
-
-       if (ss->child == (pid_t) -1)
-               return;
-
-       if (ul_pty_is_running(ss->pty)) {
-               /* wait for specific child */
-               options = WNOHANG;
-               for (;;) {
-                       pid = waitpid(ss->child, &status, options);
-                       if (pid != (pid_t) - 1) {
-                               ss->childstatus = status;
-                               ss->child = (pid_t) -1;
-                               ul_pty_set_child(ss->pty, (pid_t) -1);
-                       } else
-                               break;
-               }
-       } else {
-               /* final wait */
-               while ((pid = wait3(&status, options, NULL)) > 0) {
-                       if (pid == ss->child) {
-                               ss->childstatus = status;
-                               ss->child = (pid_t) -1;
-                               ul_pty_set_child(ss->pty, (pid_t) -1);
-                       }
-               }
-       }
-}
-
-static void child_sigstop(void *data)
-{
-       struct ptytest *ss = (struct ptytest *) data;
        kill(getpid(), SIGSTOP);
-       kill(ss->child, SIGCONT);
+       kill(child, SIGCONT);
 }
 
 int main(int argc, char *argv[])
 {
-       struct ptytest ss = { .child = (pid_t) -1 };
        struct ul_pty_callbacks *cb;
        const char *shell, *command = NULL, *shname = NULL;
        int caught_signal = 0;
+       pid_t child;
+       struct ul_pty *pty;
 
        shell = getenv("SHELL");
        if (shell == NULL)
@@ -584,28 +628,26 @@ int main(int argc, char *argv[])
 
        ul_pty_init_debug(0);
 
-       ss.pty = ul_new_pty(isatty(STDIN_FILENO));
-       if (!ss.pty)
+       pty = ul_new_pty(isatty(STDIN_FILENO));
+       if (!pty)
                err(EXIT_FAILURE, "failed to allocate PTY handler");
 
-       ul_pty_set_callback_data(ss.pty, (void *) &ss);
-       cb = ul_pty_get_callbacks(ss.pty);
-       cb->child_wait = wait_for_child;
+       cb = ul_pty_get_callbacks(pty);
        cb->child_sigstop = child_sigstop;
 
-       if (ul_pty_setup(ss.pty))
+       if (ul_pty_setup(pty))
                err(EXIT_FAILURE, "failed to create pseudo-terminal");
 
        fflush(stdout);                 /* ??? */
 
-       switch ((int) (ss.child = fork())) {
+       switch ((int) (child = fork())) {
        case -1: /* error */
-               ul_pty_cleanup(ss.pty);
+               ul_pty_cleanup(pty);
                err(EXIT_FAILURE, "cannot create child process");
                break;
 
        case 0: /* child */
-               ul_pty_init_slave(ss.pty);
+               ul_pty_init_slave(pty);
 
                signal(SIGTERM, SIG_DFL); /* because /etc/csh.login */
 
@@ -624,26 +666,27 @@ int main(int argc, char *argv[])
        }
 
        /* parent */
-       ul_pty_set_child(ss.pty, ss.child);
+       ul_pty_set_child(pty, child);
 
        /* this is the main loop */
-       ul_pty_proxy_master(ss.pty);
+       ul_pty_proxy_master(pty);
 
        /* all done; cleanup and kill */
-       caught_signal = ul_pty_get_delivered_signal(ss.pty);
+       caught_signal = ul_pty_get_delivered_signal(pty);
 
-       if (!caught_signal && ss.child != (pid_t)-1)
-               wait_for_child(&ss);    /* final wait */
+       if (!caught_signal && ul_pty_get_child(pty) != (pid_t)-1)
+               ul_pty_wait_for_child(pty);     /* final wait */
 
-       if (caught_signal && ss.child != (pid_t)-1) {
+       if (caught_signal && ul_pty_get_child(pty) != (pid_t)-1) {
                fprintf(stderr, "\nSession terminated, killing shell...");
-               kill(ss.child, SIGTERM);
+               kill(child, SIGTERM);
                sleep(2);
-               kill(ss.child, SIGKILL);
+               kill(child, SIGKILL);
                fprintf(stderr, " ...killed.\n");
        }
 
-       ul_pty_cleanup(ss.pty);
+       ul_pty_cleanup(pty);
+       ul_free_pty(pty);
        return EXIT_SUCCESS;
 }