Date: Sun, 23 Jan 2011 14:22:26 +0000 (UTC) From: Jilles Tjoelker <jilles@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r217750 - head/sbin/init Message-ID: <201101231422.p0NEMQjZ057827@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jilles Date: Sun Jan 23 14:22:26 2011 New Revision: 217750 URL: http://svn.freebsd.org/changeset/base/217750 Log: 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@ Modified: head/sbin/init/init.c Modified: head/sbin/init/init.c ============================================================================== --- head/sbin/init/init.c Sun Jan 23 14:08:59 2011 (r217749) +++ head/sbin/init/init.c Sun Jan 23 14:22:26 2011 (r217750) @@ -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;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201101231422.p0NEMQjZ057827>