diff --git a/sbin/init/init.c b/sbin/init/init.c --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -47,21 +47,26 @@ #endif /* not lint */ #include +#include #include #include #include -#include -#include +#include #include +#include #include +#include #include +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -69,10 +74,6 @@ #include #include #include -#include -#include - -#include #ifdef SECURE #include @@ -213,6 +214,8 @@ if (getuid() != 0) errx(1, "%s", strerror(EPERM)); + BOOTTRACE("init(8) starting..."); + /* System V users like to reexec init. */ if (getpid() != 1) { #ifdef COMPAT_SYSV_INIT @@ -876,6 +879,7 @@ if (Reboot) { /* Instead of going single user, let's reboot the machine */ + BOOTTRACE("shutting down the system"); sync(); /* Run scripts after all processes have been terminated. */ runfinal(); @@ -887,6 +891,7 @@ _exit(0); /* panic as well */ } + BOOTTRACE("going to single user mode"); shell = get_shell(); if ((pid = fork()) == 0) { @@ -1028,8 +1033,10 @@ { state_func_t next_transition; + BOOTTRACE("/etc/rc starting..."); if ((next_transition = run_script(_PATH_RUNCOM)) != NULL) return next_transition; + BOOTTRACE("/etc/rc finished"); runcom_mode = AUTOBOOT; /* the default */ return (state_func_t) read_ttys; @@ -1598,6 +1605,59 @@ add_session(sp); } +static const char * +get_current_state(void) +{ + + if (current_state == single_user) + return ("single-user"); + if (current_state == runcom) + return ("runcom"); + if (current_state == read_ttys) + return ("read-ttys"); + if (current_state == multi_user) + return ("multi-user"); + if (current_state == clean_ttys) + return ("clean-ttys"); + if (current_state == catatonia) + return ("catatonia"); + if (current_state == death) + return ("death"); + if (current_state == death_single) + return ("death-single"); + return ("unknown"); +} + +static void +boottrace_transition(int sig) +{ + const char *action; + + switch (sig) { + case SIGUSR2: + action = "halt & poweroff"; + break; + case SIGUSR1: + action = "halt"; + break; + case SIGINT: + action = "reboot"; + break; + case SIGWINCH: + action = "powercycle"; + break; + case SIGTERM: + action = Reboot ? "reboot" : "single-user"; + break; + default: + BOOTTRACE("signal %d from %s", sig, get_current_state()); + return; + } + + /* Trace the shutdown reason. */ + SHUTTRACE("%s from %s", action, get_current_state()); +} + /* * Catch a signal and request a state transition. */ @@ -1605,6 +1665,7 @@ transition_handler(int sig) { + boottrace_transition(sig); switch (sig) { case SIGHUP: if (current_state == read_ttys || current_state == multi_user || @@ -1648,6 +1709,7 @@ static state_func_t multi_user(void) { + static bool inmultiuser = false; pid_t pid; session_t *sp; @@ -1677,6 +1739,11 @@ add_session(sp); } + if (requested_transition == 0 && !inmultiuser) { + inmultiuser = true; + /* This marks the change from boot-time tracing to run-time. */ + RUNTRACE("multi-user start"); + } while (!requested_transition) if ((pid = waitpid(-1, (int *) 0, 0)) != -1) collect_child(pid); @@ -1843,6 +1910,7 @@ revoke(_PATH_CONSOLE); + BOOTTRACE("start killing user processes"); for (i = 0; i < 2; ++i) { if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) return (state_func_t) single_user; @@ -1894,6 +1962,8 @@ char *argv[4]; struct stat sb; + BOOTTRACE("init(8): start rc.shutdown"); + /* * rc.shutdown is optional, so to prevent any unnecessary * complaints from the shell we simply don't run it if the @@ -1944,6 +2014,8 @@ kill(wpid, SIGTERM); warning("timeout expired for %s: %m; going to " "single user mode", _PATH_RUNDOWN); + BOOTTRACE("rc.shutdown's %d sec timeout expired", + shutdowntimeout); return -1; } if (wpid == -1) { diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c --- a/sbin/reboot/reboot.c +++ b/sbin/reboot/reboot.c @@ -43,10 +43,12 @@ #include __FBSDID("$FreeBSD$"); -#include -#include #include +#include +#include #include +#include + #include #include #include @@ -210,10 +212,12 @@ } /* Just stop init -- if we fail, we'll restart it. */ + BOOTTRACE("SIGTSTP to init(8)..."); if (kill(1, SIGTSTP) == -1) err(1, "SIGTSTP init"); /* Send a SIGTERM first, a chance to save the buffers. */ + BOOTTRACE("SIGTERM to all other processes..."); if (kill(-1, SIGTERM) == -1 && errno != ESRCH) err(1, "SIGTERM processes"); @@ -235,6 +239,7 @@ } for (i = 1;; ++i) { + BOOTTRACE("SIGKILL to all other processes(%d)...", i); if (kill(-1, SIGKILL) == -1) { if (errno == ESRCH) break; @@ -252,6 +257,7 @@ /* FALLTHROUGH */ restart: + BOOTTRACE("SIGHUP to init(8)..."); sverrno = errno; errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "", strerror(sverrno)); diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c --- a/sbin/shutdown/shutdown.c +++ b/sbin/shutdown/shutdown.c @@ -44,9 +44,10 @@ __FBSDID("$FreeBSD$"); #include -#include +#include #include #include +#include #include #include @@ -221,10 +222,13 @@ } mbuflen = strlen(mbuf); - if (offset) + if (offset) { + BOOTTRACE("Shutdown at %s", ctime(&shuttime)); (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); - else + } else { + BOOTTRACE("Shutdown NOW!"); (void)printf("Shutdown NOW!\n"); + } if (!(whom = getlogin())) whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; @@ -360,12 +364,16 @@ { char *empty_environ[] = { NULL }; + BOOTTRACE("%s by %s", + doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : + docycle ? "power-cycle" : "shutdown", whom); syslog(LOG_NOTICE, "%s by %s: %s", doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : docycle ? "power-cycle" : "shutdown", whom, mbuf); (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); if (killflg) { + BOOTTRACE("fake shutdown..."); (void)printf("\rbut you'll have to do it yourself\r\n"); exit(0); } @@ -383,6 +391,7 @@ (void)printf("\nkill -HUP 1\n"); #else if (!oflag) { + BOOTTRACE("signal to init(8)..."); (void)kill(1, doreboot ? SIGINT : /* reboot */ dohalt ? SIGUSR1 : /* halt */ dopower ? SIGUSR2 : /* power-down */ @@ -390,6 +399,7 @@ SIGTERM); /* single-user */ } else { if (doreboot) { + BOOTTRACE("exec reboot(8) -l..."); execle(_PATH_REBOOT, "reboot", "-l", nosync, (char *)NULL, empty_environ); syslog(LOG_ERR, "shutdown: can't exec %s: %m.", @@ -397,6 +407,7 @@ warn(_PATH_REBOOT); } else if (dohalt) { + BOOTTRACE("exec halt(8) -l..."); execle(_PATH_HALT, "halt", "-l", nosync, (char *)NULL, empty_environ); syslog(LOG_ERR, "shutdown: can't exec %s: %m.", @@ -404,6 +415,7 @@ warn(_PATH_HALT); } else if (dopower) { + BOOTTRACE("exec halt(8) -l -p..."); execle(_PATH_HALT, "halt", "-l", "-p", nosync, (char *)NULL, empty_environ); syslog(LOG_ERR, "shutdown: can't exec %s: %m.", @@ -417,6 +429,7 @@ _PATH_HALT); warn(_PATH_HALT); } + BOOTTRACE("SIGTERM to init(8)..."); (void)kill(1, SIGTERM); /* to single-user */ } #endif diff --git a/sys/sys/boottrace.h b/sys/sys/boottrace.h --- a/sys/sys/boottrace.h +++ b/sys/sys/boottrace.h @@ -36,9 +36,42 @@ #define BT_EVENT_NAMELEN 40 #define BT_MSGLEN (BT_EVENT_NAMELEN + 1 + BT_EVENT_TDNAMELEN) -#ifdef _KERNEL +#ifndef _KERNEL +#include +#include +#include +#include + +/* + * Convenience macros. Userland API. + */ +#define BOOTTRACE(...) _boottrace(_BOOTTRACE_BOOTTRACE, __VA_ARGS__) +#define RUNTRACE(...) _boottrace(_BOOTTRACE_RUNTRACE, __VA_ARGS__) +#define SHUTTRACE(...) _boottrace(_BOOTTRACE_SHUTTRACE, __VA_ARGS__) + +/* + * Call the requested boottrace sysctl with provided va-formatted message. + */ +static __inline void +_boottrace(const char *sysctlname, const char *fmt, ...) +{ + va_list ap; + char msg[BT_MSGLEN]; + int len; + + va_start(ap, fmt); + len = vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + /* Log the event, even if truncated. */ + if (len >= 0) + (void)sysctlbyname(sysctlname, NULL, NULL, msg, strlen(msg)); +} + +#else /* _KERNEL */ + /* - * Convenience macros. + * Convenience macros. Kernel API. */ #define _BOOTTRACE(tdname, ...) do { \ if (boottrace_enabled) \