Index: sbin/init/init.c =================================================================== --- sbin/init/init.c +++ sbin/init/init.c @@ -47,13 +47,15 @@ #endif /* not lint */ #include +#include #include #include #include -#include -#include +#include #include +#include #include +#include #include #include @@ -69,7 +71,6 @@ #include #include #include -#include #include #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 || @@ -1677,6 +1738,11 @@ add_session(sp); } + static int inmultiuser = 0; + if (!requested_transition && !inmultiuser) { + inmultiuser = 1; + RUNTRACE("multi-user start"); + } while (!requested_transition) if ((pid = waitpid(-1, (int *) 0, 0)) != -1) collect_child(pid); @@ -1819,6 +1885,8 @@ */ revoke_ttys(); + BOOTTRACE("init(8): start rc.shutdown"); + /* Try to run the rc.shutdown script within a period of time */ runshutdown(); @@ -1843,6 +1911,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; @@ -1858,6 +1927,8 @@ return (state_func_t) single_user; } + /* We reboot by default in single user mode. */ + BOOTTRACE("some processes would not die"); warning("some processes would not die; ps axl advised"); return (state_func_t) single_user; @@ -1944,6 +2015,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) { Index: sbin/reboot/reboot.c =================================================================== --- sbin/reboot/reboot.c +++ sbin/reboot/reboot.c @@ -43,16 +43,19 @@ #include __FBSDID("$FreeBSD$"); -#include -#include #include +#include +#include #include +#include + #include #include #include #include #include #include +#include #include #include #include @@ -210,10 +213,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 +240,7 @@ } for (i = 1;; ++i) { + BOOTTRACE("SIGKILL to all other processes(%d)...", i); if (kill(-1, SIGKILL) == -1) { if (errno == ESRCH) break; @@ -252,6 +258,7 @@ /* FALLTHROUGH */ restart: + BOOTTRACE("SIGHUP to init(8)..."); sverrno = errno; errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "", strerror(sverrno)); Index: sbin/shutdown/shutdown.c =================================================================== --- sbin/shutdown/shutdown.c +++ sbin/shutdown/shutdown.c @@ -44,9 +44,11 @@ __FBSDID("$FreeBSD$"); #include -#include +#include #include +#include #include +#include #include #include @@ -221,10 +223,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 +365,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 +392,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 +400,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 +408,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 +416,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 +430,7 @@ _PATH_HALT); warn(_PATH_HALT); } + BOOTTRACE("SIGTERM to init(8)..."); (void)kill(1, SIGTERM); /* to single-user */ } #endif Index: sys/sys/boottrace.h =================================================================== --- sys/sys/boottrace.h +++ 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_BOOTTIMES, __VA_ARGS__) +#define RUNTRACE(...) _boottrace(_BOOTTRACE_RUNTIMES, __VA_ARGS__) +#define SHUTTRACE(...) _boottrace(_BOOTTRACE_SHUTTIMES, __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, ...) (void)boottrace_format(tdname, __VA_ARGS__) #define BOOTTRACE(...) _BOOTTRACE(NULL, __VA_ARGS__)