summaryrefslogtreecommitdiffstats
path: root/sbin/init
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2011-01-23 14:22:26 +0000
committerjilles <jilles@FreeBSD.org>2011-01-23 14:22:26 +0000
commit681c5b70eced43a9fc88d9343f82054010b46ea1 (patch)
tree7d35a3ecaed953bbba379094ed25161905d4234e /sbin/init
parentb7eb600358707a4319be9897805c732f8b821aa6 (diff)
downloadFreeBSD-src-681c5b70eced43a9fc88d9343f82054010b46ea1.zip
FreeBSD-src-681c5b70eced43a9fc88d9343f82054010b46ea1.tar.gz
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@
Diffstat (limited to 'sbin/init')
-rw-r--r--sbin/init/init.c45
1 files changed, 35 insertions, 10 deletions
diff --git a/sbin/init/init.c b/sbin/init/init.c
index e146199..3f6b345 100644
--- a/sbin/init/init.c
+++ b/sbin/init/init.c
@@ -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;
OpenPOWER on IntegriCloud