summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2015-11-14 23:56:42 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2015-11-14 23:56:42 +0000
commit3a40f36a51668b8edf062c8f7c9fce866ff3e547 (patch)
treeff3bd7f18091b77d79b5280bc9d634ecda5e7ec6
parent3af3fc94c46d90d9e0b022b5c4b9da0576167849 (diff)
Fix an issue reported by deraadt@: When hitting Ctrl-Backslash (= SIGQUIT)
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@
-rw-r--r--usr.bin/mandoc/main.c40
1 files changed, 35 insertions, 5 deletions
diff --git a/usr.bin/mandoc/main.c b/usr.bin/mandoc/main.c
index 8268fb26118..b9d4b3fafa6 100644
--- a/usr.bin/mandoc/main.c
+++ b/usr.bin/mandoc/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.164 2015/11/07 17:58:52 schwarze Exp $ */
+/* $OpenBSD: main.c,v 1.165 2015/11/14 23:56:41 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -24,6 +24,7 @@
#include <assert.h>
#include <ctype.h>
#include <err.h>
+#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <signal.h>
@@ -120,14 +121,16 @@ main(int argc, char *argv[])
int show_usage;
int options;
int use_pager;
+ int status;
int c;
+ pid_t pager_pid;
progname = getprogname();
if (strncmp(progname, "mandocdb", 8) == 0 ||
strncmp(progname, "makewhatis", 10) == 0)
return mandocdb(argc, argv);
- 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");
/* Search options. */
@@ -387,7 +390,7 @@ main(int argc, char *argv[])
/* mandoc(1) */
- 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");
@@ -484,7 +487,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();
}
@@ -971,9 +996,14 @@ 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:
- if (pledge("stdio rpath tmppath", NULL) == -1)
+ (void)setpgid(pager_pid, 0);
+ if (tcsetpgrp(STDIN_FILENO, pager_pid) == -1)
+ err((int)MANDOCLEVEL_SYSERR, "tcsetpgrp");
+ if (pledge("stdio rpath tmppath tty proc", NULL) == -1)
err((int)MANDOCLEVEL_SYSERR, "pledge");
return pager_pid;
}