diff options
author | cheloha <cheloha@cvs.openbsd.org> | 2018-02-24 20:00:08 +0000 |
---|---|---|
committer | cheloha <cheloha@cvs.openbsd.org> | 2018-02-24 20:00:08 +0000 |
commit | 167a5c4245ffba8bbff2b436c2ad7ae2bcf8bc06 (patch) | |
tree | af2724cc9b9129091508f944f9f5fa02506b6acf /sbin/shutdown | |
parent | e54a953cfec5f684b03c58f18dafc93dde6ada64 (diff) |
Replace popen/setjmp/pclose with a manual pipe/fork/exec/wait.
We can limit the time we wait on wall(1) without the complexity
inherent to setjmp.
Actually wait (instead of waitpid) to pick up any straggler wall
processes from prior timewarn() calls.
With a tweak from millert@ to ensure we don't accidentally close
stdin before we exec wall.
ok millert@ tb@
Diffstat (limited to 'sbin/shutdown')
-rw-r--r-- | sbin/shutdown/shutdown.c | 78 |
1 files changed, 50 insertions, 28 deletions
diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c index 7f957f6ab2d..1689bb32430 100644 --- a/sbin/shutdown/shutdown.c +++ b/sbin/shutdown/shutdown.c @@ -1,4 +1,4 @@ -/* $OpenBSD: shutdown.c,v 1.47 2018/02/04 04:28:41 cheloha Exp $ */ +/* $OpenBSD: shutdown.c,v 1.48 2018/02/24 20:00:07 cheloha Exp $ */ /* $NetBSD: shutdown.c,v 1.9 1995/03/18 15:01:09 cgd Exp $ */ /* @@ -40,7 +40,6 @@ #include <fcntl.h> #include <sys/termios.h> #include <pwd.h> -#include <setjmp.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -86,7 +85,7 @@ struct interval { static time_t offset, shuttime; static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync; -static sig_atomic_t killflg; +static sig_atomic_t killflg, timed_out; static char *whom, mbuf[BUFSIZ]; void badtime(void); @@ -268,8 +267,6 @@ loop(void) die_you_gravy_sucking_pig_dog(); } -static jmp_buf alarmbuf; - static char *restricted_environ[] = { "PATH=" _PATH_STDPATH, NULL @@ -279,59 +276,84 @@ void timewarn(int timeleft) { static char hostname[HOST_NAME_MAX+1]; - char wcmd[PATH_MAX + 4]; - extern char **environ; static int first; - FILE *pf; + int fd[2]; + pid_t pid, wpid; if (!first++) (void)gethostname(hostname, sizeof(hostname)); - /* undoc -n option to wall suppresses normal wall banner */ - (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); - environ = restricted_environ; - if (!(pf = popen(wcmd, "w"))) { - syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); + if (pipe(fd) == -1) { + syslog(LOG_ERR, "pipe: %m"); + return; + } + switch (pid = fork()) { + case -1: + syslog(LOG_ERR, "fork: %m"); + close(fd[0]); + close(fd[1]); return; + case 0: + if (dup2(fd[0], STDIN_FILENO) == -1) { + syslog(LOG_ERR, "dup2: %m"); + _exit(1); + } + if (fd[0] != STDIN_FILENO) + close(fd[0]); + close(fd[1]); + /* wall(1)'s undocumented '-n' flag suppresses its banner. */ + execle(_PATH_WALL, _PATH_WALL, "-n", (char *)NULL, + restricted_environ); + syslog(LOG_ERR, "%s: %m", _PATH_WALL); + _exit(1); + default: + close(fd[0]); } - (void)fprintf(pf, + dprintf(fd[1], "\007*** %sSystem shutdown message from %s@%s ***\007\n", timeleft ? "": "FINAL ", whom, hostname); if (timeleft > 10*60) { struct tm *tm = localtime(&shuttime); - fprintf(pf, "System going down at %d:%02d\n\n", + dprintf(fd[1], "System going down at %d:%02d\n\n", tm->tm_hour, tm->tm_min); } else if (timeleft > 59) - (void)fprintf(pf, "System going down in %d minute%s\n\n", + dprintf(fd[1], "System going down in %d minute%s\n\n", timeleft / 60, (timeleft > 60) ? "s" : ""); else if (timeleft) - (void)fprintf(pf, "System going down in 30 seconds\n\n"); + dprintf(fd[1], "System going down in 30 seconds\n\n"); else - (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); + dprintf(fd[1], "System going down IMMEDIATELY\n\n"); if (mbuflen) - (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); + (void)write(fd[1], mbuf, mbuflen); + close(fd[1]); /* - * play some games, just in case wall doesn't come back - * probably unnecessary, given that wall is careful. + * If we wait longer than 30 seconds for wall(1) to exit we'll + * throw off our schedule. */ - if (!setjmp(alarmbuf)) { - (void)signal(SIGALRM, timeout); - (void)alarm((u_int)30); - (void)pclose(pf); - (void)alarm((u_int)0); - (void)signal(SIGALRM, SIG_DFL); + signal(SIGALRM, timeout); + siginterrupt(SIGALRM, 1); + alarm(30); + while ((wpid = wait(NULL)) != pid && !timed_out) + continue; + alarm(0); + signal(SIGALRM, SIG_DFL); + if (timed_out) { + syslog(LOG_NOTICE, + "wall[%ld] is taking too long to exit; continuing", + (long)pid); + timed_out = 0; } } void timeout(int signo) { - longjmp(alarmbuf, 1); /* XXX signal/longjmp resource leaks */ + timed_out = 1; } void |