]> git.cameronkatri.com Git - mandoc.git/commitdiff
Fix an issue reported by deraadt@: When hitting Ctrl-Backslash (= SIGQUIT)
authorIngo Schwarze <schwarze@openbsd.org>
Sat, 14 Nov 2015 23:57:47 +0000 (23:57 +0000)
committerIngo Schwarze <schwarze@openbsd.org>
Sat, 14 Nov 2015 23:57:47 +0000 (23:57 +0000)
in the less(1) spawned by man(1), man(1) died uncleanly, leaving behind
its temp files, and killed less(1) uncleanly as well with SIGPIPE,
leaving the terminal in the wrong state.

Fix this by giving less(1) its own process group and handing it
control of the terminal, but in such a way that Ctrl-z (= SIGSTOP)
still works: In that case, let man(1) stop itself, too, and let it
continue the pager when it continues itself.

Joint work with millert@ who contributed most of the expertise
required, and also most parts of the code.
OK deraadt@ millert@

main.c

diff --git a/main.c b/main.c
index d2d7e087ed0901c0bab73bd160356639c975513c..ecdf4eb278b0cc74ba061d63083f10e7b58a2284 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/*     $Id: main.c,v 1.257 2015/11/07 17:58:55 schwarze Exp $ */
+/*     $Id: main.c,v 1.258 2015/11/14 23:57:47 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -27,6 +27,7 @@
 #if HAVE_ERR
 #include <err.h>
 #endif
+#include <errno.h>
 #include <fcntl.h>
 #include <glob.h>
 #include <signal.h>
@@ -131,7 +132,9 @@ main(int argc, char *argv[])
        int              show_usage;
        int              options;
        int              use_pager;
+       int              status;
        int              c;
+       pid_t            pager_pid;
 
 #if HAVE_PROGNAME
        progname = getprogname();
@@ -152,7 +155,7 @@ main(int argc, char *argv[])
 #endif
 
 #if HAVE_PLEDGE
-       if (pledge("stdio rpath tmppath proc exec flock", NULL) == -1)
+       if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1)
                err((int)MANDOCLEVEL_SYSERR, "pledge");
 #endif
 
@@ -427,7 +430,7 @@ main(int argc, char *argv[])
        /* mandoc(1) */
 
 #if HAVE_PLEDGE
-       if (pledge(use_pager ? "stdio rpath tmppath proc exec" :
+       if (pledge(use_pager ? "stdio rpath tmppath tty proc exec" :
            "stdio rpath", NULL) == -1)
                err((int)MANDOCLEVEL_SYSERR, "pledge");
 #endif
@@ -527,7 +530,29 @@ out:
        if (tag_files != NULL) {
                fclose(stdout);
                tag_write();
-               waitpid(spawn_pager(tag_files), NULL, 0);
+               pager_pid = spawn_pager(tag_files);
+               for (;;) {
+                       if (waitpid(pager_pid, &status, WUNTRACED) == -1) {
+                               if (errno == EINTR)
+                                       continue;
+                               warn("wait");
+                               rc = MANDOCLEVEL_SYSERR;
+                               break;
+                       }
+                       if (!WIFSTOPPED(status))
+                               break;
+
+                       (void)tcsetpgrp(STDIN_FILENO, getpgid(0));
+                       kill(0, WSTOPSIG(status));
+       
+                       /*
+                        * I'm now stopped.
+                        * When getting SIGCONT, continue here:
+                        */
+
+                       (void)tcsetpgrp(STDIN_FILENO, pager_pid);
+                       kill(pager_pid, SIGCONT);
+               }
                tag_unlink();
        }
 
@@ -1016,10 +1041,15 @@ spawn_pager(struct tag_files *tag_files)
        case -1:
                err((int)MANDOCLEVEL_SYSERR, "fork");
        case 0:
+               /* Set pgrp in both parent and child to avoid racing exec. */
+               (void)setpgid(0, 0);
                break;
        default:
+               (void)setpgid(pager_pid, 0);
+               if (tcsetpgrp(STDIN_FILENO, pager_pid) == -1)
+                       err((int)MANDOCLEVEL_SYSERR, "tcsetpgrp");
 #if HAVE_PLEDGE
-               if (pledge("stdio rpath tmppath", NULL) == -1)
+               if (pledge("stdio rpath tmppath tty proc", NULL) == -1)
                        err((int)MANDOCLEVEL_SYSERR, "pledge");
 #endif
                return pager_pid;