From c0b9ea4c76fc283b66952dea5e873a3cb2b26a8c Mon Sep 17 00:00:00 2001 From: davidn Date: Sat, 2 Aug 1997 00:22:52 +0000 Subject: Add /etc/rc.shutdown capability to init. Add sample /etc/rc.shutdown (which is just a shell for now). Submitted by: Ollivier Robert --- etc/Makefile | 4 +- etc/rc.shutdown | 26 ++++++++ sbin/init/init.8 | 13 +++- sbin/init/init.c | 175 +++++++++++++++++++++++++++++++++++++++++++++----- sbin/init/pathnames.h | 1 + 5 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 etc/rc.shutdown diff --git a/etc/Makefile b/etc/Makefile index 607099b..b19a125 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -1,5 +1,5 @@ # from: @(#)Makefile 5.11 (Berkeley) 5/21/91 -# $Id: Makefile,v 1.152 1997/07/05 19:35:22 pst Exp $ +# $Id: Makefile,v 1.153 1997/07/18 03:49:47 asami Exp $ # -rw-r--r-- BINOWN= root @@ -9,7 +9,7 @@ BIN1= aliases amd.map csh.cshrc csh.login csh.logout dm.conf \ inetd.conf login.conf login.access motd modems networks \ newsyslog.conf phones pccard.conf.sample printcap profile protocols \ rc rc.conf rc.firewall rc.local rc.network rc.pccard rc.serial \ - etc.${MACHINE}/rc.${MACHINE} \ + rc.shutdown etc.${MACHINE}/rc.${MACHINE} \ remote security services shells \ syslog.conf ttys etc.${MACHINE}/disktab rpc make.conf \ ${.CURDIR}/../gnu/usr.bin/man/manpath/manpath.config \ diff --git a/etc/rc.shutdown b/etc/rc.shutdown new file mode 100644 index 0000000..f935834 --- /dev/null +++ b/etc/rc.shutdown @@ -0,0 +1,26 @@ +#!/bin/sh +# $Id$ + +# site-specific closing actions for daemons run by init on shutdown +# or before going single-user from multi-user. +# Output and errors are directed to console by init, and the +# console is the controlling terminal. + +stty status '^T' + +# Set shell to ignore SIGINT (2), but not children; +# shell catches SIGQUIT (3) and returns to single user after fsck. +trap : 2 +trap : 3 # shouldn't be needed + +HOME=/; export HOME +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin +export PATH + +echo -n Shutting down daemon processes: + +# Insert shutdown procedures here + + +echo '.' +exit 0 diff --git a/sbin/init/init.8 b/sbin/init/init.8 index 0591d9d..1b5bf5f 100644 --- a/sbin/init/init.8 +++ b/sbin/init/init.8 @@ -33,7 +33,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)init.8 8.3 (Berkeley) 4/18/94 -.\" $Id: init.8,v 1.8 1997/02/22 14:32:34 peter Exp $ +.\" $Id: init.8,v 1.9 1997/04/01 20:41:04 mpp Exp $ .\" .Dd April 18, 1994 .Dt INIT 8 @@ -241,6 +241,15 @@ signal, i.e. This is useful for shutting the machine down cleanly from inside the kernel or from X when the machine appears to be hung. .Pp +When shuting down the machine, +.Nm init +will try to run the +.Pa /etc/rc.shutdown +script. This script can be used to cleanly terminate specific programs such +as +.Nm innd +(the InterNetNews server). +.Pp The role of .Nm init is so critical that if it dies, the system will reboot itself @@ -280,6 +289,8 @@ Record of all logins and logouts. The terminal initialization information file. .It Pa /etc/rc System startup commands. +.It Pa /etc/rc.shutdown +System shutdown commands. .El .Sh SEE ALSO .Xr kill 1 , diff --git a/sbin/init/init.c b/sbin/init/init.c index a0b95c9..d549f4a 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -33,7 +33,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: init.c,v 1.22 1997/07/05 19:36:55 ache Exp $ + * $Id: init.c,v 1.23 1997/07/08 11:51:11 ache Exp $ */ #ifndef lint @@ -47,6 +47,7 @@ static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; #endif /* not lint */ #include +#include #include #include #include @@ -91,6 +92,7 @@ static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; #define WINDOW_WAIT 3 /* wait N secs after starting window */ #define STALL_TIMEOUT 30 /* wait N secs after warning */ #define DEATH_WATCH 10 /* wait N secs for procs to die */ +#define DEATH_SCRIPT 120 /* wait for 2mn for /etc/rc.shutdown */ #define RESOURCE_RC "daemon" #define RESOURCE_WINDOW "default" #define RESOURCE_GETTY "default" @@ -103,6 +105,7 @@ void warning __P((char *, ...)); void emergency __P((char *, ...)); void disaster __P((int)); void badsys __P((int)); +int runshutdown __P((void)); /* * We really need a recursive typedef... @@ -309,7 +312,7 @@ handle(va_alist) sa.sa_handler = handler; sigfillset(&mask_everything); - while (sig = va_arg(ap, int)) { + while ((sig = va_arg(ap, int)) != NULL) { sa.sa_mask = mask_everything; /* XXX SA_RESTART? */ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; @@ -340,7 +343,7 @@ delset(va_alist) va_start(ap, maskp); #endif - while (sig = va_arg(ap, int)) + while ((sig = va_arg(ap, int)) != NULL) sigdelset(maskp, sig); va_end(ap); } @@ -451,7 +454,7 @@ disaster(sig) int sig; { emergency("fatal signal: %s", - sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal"); sleep(STALL_TIMEOUT); _exit(sig); /* reboot */ @@ -891,7 +894,7 @@ construct_argv(command) if ((argv[argc++] = strk(command)) == 0) return 0; - while (argv[argc++] = strk((char *) 0)) + while ((argv[argc++] = strk((char *) 0)) != NULL) continue; return argv; } @@ -1051,8 +1054,8 @@ read_ttys() * Allocate a session entry for each active port. * Note that sp starts at 0. */ - while (typ = getttyent()) - if (snext = new_session(sp, ++session_index, typ)) + while ((typ = getttyent()) != NULL) + if ((snext = new_session(sp, ++session_index, typ)) != NULL) sp = snext; endttyent(); @@ -1195,11 +1198,11 @@ collect_child(pid) sp->se_process = 0; if (sp->se_flags & SE_SHUTDOWN) { - if (sprev = sp->se_prev) + if ((sprev = sp->se_prev) != NULL) sprev->se_next = sp->se_next; else sessions = sp->se_next; - if (snext = sp->se_next) + if ((snext = sp->se_next) != NULL) snext->se_prev = sp->se_prev; free_session(sp); return; @@ -1298,7 +1301,7 @@ clean_ttys() return (state_func_t) multi_user; devlen = sizeof(_PATH_DEV) - 1; - while (typ = getttyent()) { + while ((typ = getttyent()) != NULL) { ++session_index; for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) @@ -1329,13 +1332,13 @@ clean_ttys() kill(sp->se_process, SIGHUP); } else if ( !old_getty - || !old_type && sp->se_type - || old_type && !sp->se_type - || !old_window && sp->se_window - || old_window && !sp->se_window - || strcmp(old_getty, sp->se_getty) != 0 - || old_window && strcmp(old_window, sp->se_window) != 0 - || old_type && strcmp(old_type, sp->se_type) != 0 + || (!old_type && sp->se_type) + || (old_type && !sp->se_type) + || (!old_window && sp->se_window) + || (old_window && !sp->se_window) + || (strcmp(old_getty, sp->se_getty) != 0) + || (old_window && strcmp(old_window, sp->se_window) != 0) + || (old_type && strcmp(old_type, sp->se_type) != 0) ) { /* Don't set SE_SHUTDOWN here */ sp->se_nspace = 0; @@ -1380,6 +1383,7 @@ void alrm_handler(sig) int sig; { + (void)sig; clang = 1; } @@ -1390,6 +1394,7 @@ state_func_t death() { register session_t *sp; + register int rcdown; register int i; pid_t pid; static const int death_sigs[2] = { SIGTERM, SIGKILL }; @@ -1402,6 +1407,9 @@ death() (void) revoke(sp->se_device); } + /* Try to run the rc.shutdown script within a period of time */ + rcdown = runshutdown(); + for (i = 0; i < 2; ++i) { if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) return (state_func_t) single_user; @@ -1421,6 +1429,139 @@ death() return (state_func_t) single_user; } + +/* + * Run the system shutdown script. + * + * Exit codes: XXX I should document more + * -2 shutdown script terminated abnormally + * -1 fatal error - can't run script + * 0 good. + * >0 some error (exit code) + */ +int +runshutdown() +{ + pid_t pid, wpid; + int status; + int shutdowntimeout; + size_t len; + char *argv[3]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + int fd; + + setsid(); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + /* + * Clean our descriptor table to be sure of + * getting /dev/console as control terminal. + */ + for (fd = getdtablesize(); fd-- > 0; ) + (void)close(fd); + + if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) + warning("can't open %s: %m", _PATH_CONSOLE); + else { + if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1) + warning("can't get %s for controlling terminal: %m", _PATH_CONSOLE); + (void) dup2(fd, 1); + (void) dup2(fd, 2); + } + + /* + * Run the shutdown script. + */ + argv[0] = "sh"; + argv[1] = _PATH_RUNDOWN; + argv[2] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + warning("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNDOWN); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNDOWN); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return -1; + } + + len = sizeof(shutdowntimeout); + if (sysctlbyname("kern.shutdown_timeout", + &shutdowntimeout, + &len, NULL, 0) == -1 || shutdowntimeout < 2) + shutdowntimeout = DEATH_SCRIPT; + alarm(shutdowntimeout); + clang = 0; + /* + * Copied from single_user(). This is a bit paranoid. + * Use the same ALRM handler. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (clang == 1) { + /* we were waiting for the sub-shell */ + kill(wpid, SIGTERM); + warning("timeout expired for %s on %s: %m; going to single used mode", + _PATH_BSHELL, _PATH_RUNDOWN); + return -1; + } + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNDOWN); + return -1; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNDOWN); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !clang); + + /* Turn off the alarm */ + alarm(0); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* + * /etc/rc.shutdown executed /sbin/reboot; + * wait for the end quietly + */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNDOWN); + return -2; + } + + if ((status = WEXITSTATUS(status)) != 0) + warning("%s returned status %d", _PATH_RUNDOWN, status); + + return status; +} + char * strk (char *p) { diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h index abb874a..27837f6 100644 --- a/sbin/init/pathnames.h +++ b/sbin/init/pathnames.h @@ -40,3 +40,4 @@ #define _PATH_SLOGGER "/sbin/session_logger" #define _PATH_RUNCOM "/etc/rc" +#define _PATH_RUNDOWN "/etc/rc.shutdown" -- cgit v1.1