init: Only run /etc/rc.shutdown if /etc/rc was run.

It does not make sense to shut down daemons that were not started. In
particular, this fixes loss of mixer settings when shutting down using
shutdown(8), init(8) or ctrl+alt+del from single-user mode.

If /etc/rc reboots, /etc/rc.shutdown is not run.

Also fix segfaults and other erratic behaviour if init receives SIGHUP or
SIGTSTP while in single-user mode.

This commit does not attempt to fix any badness with signal handlers
(assumption that pointers can be read and written atomically, EINTR race
condition). I believe it does not make this badness any worse.

Silence on:	-arch@
This commit is contained in:
Jilles Tjoelker 2011-01-23 14:22:26 +00:00
parent 89db1db777
commit acf0ab0669
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=217750

View File

@ -122,6 +122,7 @@ static state_func_t multi_user(void);
static state_func_t clean_ttys(void);
static state_func_t catatonia(void);
static state_func_t death(void);
static state_func_t death_single(void);
static state_func_t run_script(const char *);
@ -136,6 +137,7 @@ int devfs;
static void transition(state_t);
static state_t requested_transition;
static state_t current_state = death_single;
static void setctty(const char *);
static const char *get_shell(void);
@ -559,8 +561,9 @@ static void
transition(state_t s)
{
current_state = s;
for (;;)
s = (state_t) (*s)();
current_state = (state_t) (*current_state)();
}
/*
@ -796,7 +799,7 @@ runcom(void)
* Returns 0 on success, otherwise the next transition to enter:
* - single_user if fork/execv/waitpid failed, or if the script
* terminated with a signal or exit code != 0.
* - death if a SIGTERM was delivered to init(8).
* - death_single if a SIGTERM was delivered to init(8).
*/
static state_func_t
run_script(const char *script)
@ -852,8 +855,8 @@ run_script(const char *script)
if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
collect_child(wpid);
if (wpid == -1) {
if (requested_transition == death)
return (state_func_t) death;
if (requested_transition == death_single)
return (state_func_t) death_single;
if (errno == EINTR)
continue;
warning("wait for %s on %s failed: %m; going to "
@ -1306,7 +1309,9 @@ transition_handler(int sig)
switch (sig) {
case SIGHUP:
requested_transition = clean_ttys;
if (current_state == read_ttys || current_state == multi_user ||
current_state == clean_ttys || current_state == catatonia)
requested_transition = clean_ttys;
break;
case SIGUSR2:
howto = RB_POWEROFF;
@ -1315,10 +1320,17 @@ transition_handler(int sig)
case SIGINT:
Reboot = TRUE;
case SIGTERM:
requested_transition = death;
if (current_state == read_ttys || current_state == multi_user ||
current_state == clean_ttys || current_state == catatonia)
requested_transition = death;
else
requested_transition = death_single;
break;
case SIGTSTP:
requested_transition = catatonia;
if (current_state == runcom || current_state == read_ttys ||
current_state == clean_ttys ||
current_state == multi_user || current_state == catatonia)
requested_transition = catatonia;
break;
default:
requested_transition = 0;
@ -1494,9 +1506,6 @@ death(void)
{
struct utmpx utx;
session_t *sp;
int i;
pid_t pid;
static const int death_sigs[2] = { SIGTERM, SIGKILL };
/* NB: should send a message to the session logger to avoid blocking. */
utx.ut_type = SHUTDOWN_TIME;
@ -1518,6 +1527,22 @@ death(void)
/* Try to run the rc.shutdown script within a period of time */
runshutdown();
return (state_func_t) death_single;
}
/*
* Do what is necessary to reinitialize single user mode or reboot
* from an incomplete state.
*/
static state_func_t
death_single(void)
{
int i;
pid_t pid;
static const int death_sigs[2] = { SIGTERM, SIGKILL };
revoke(_PATH_CONSOLE);
for (i = 0; i < 2; ++i) {
if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
return (state_func_t) single_user;