Add /etc/rc.shutdown capability to init.

Add sample /etc/rc.shutdown (which is just a shell for now).
Submitted by:	Ollivier Robert <roberto@keltia.freenix.fr>
This commit is contained in:
David Nugent 1997-08-02 00:22:52 +00:00
parent 4c33feed58
commit 8889c700f3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=27837
5 changed files with 199 additions and 20 deletions

View File

@ -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 \

26
etc/rc.shutdown Normal file
View File

@ -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

View File

@ -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 ,

View File

@ -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 <sys/param.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
@ -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)
{

View File

@ -40,3 +40,4 @@
#define _PATH_SLOGGER "/sbin/session_logger"
#define _PATH_RUNCOM "/etc/rc"
#define _PATH_RUNDOWN "/etc/rc.shutdown"