DF-5660 / fix.diff
--- a/sys/platform/vkernel64/platform/shutdown.c +++ b/sys/platform/vkernel64/platform/shutdown.c @@ -38,6 +38,8 @@ #include <sys/types.h> #include <sys/systm.h> #include <sys/reboot.h> +#include <sys/proc.h> +#include <sys/signalvar.h> #include <sys/kernel.h> #include <sys/kthread.h> #include <sys/interrupt.h> @@ -91,9 +93,27 @@ shutdown_intr(void *arg __unused, void *frame __unused) { kprintf("Caught SIGTERM from host system. Shutting down...\n"); - if (initproc != NULL) { + + /* + * (1) ksignal(initproc, SIGUSR2) is a silent no-op until init(8) + * has installed its SIGUSR2 handler: issignal() drops + * default-action signals to pid <= 1 (kern_sig.c:2134) and + * execsigs() only resets caught signals, so a pre-exec or + * non-init(8) pid 1 can never take it. Gate on p_sigcatch -- + * the kernel's own SIG_CATCH/SIG_DFL delivery criterion. + * + * (2) reboot(RB_POWEROFF) resolved to the HOST libc reboot(2) + * (the kernel defines no symbol `reboot`; the vkernel links + * as an ordinary host program), asking the host to reboot or + * power off the machine it runs on. shutdown_nice() takes + * the in-kernel path; RB_HALT|RB_POWEROFF rides in + * shutdown_howto into boot(), ending in shutdown_halt -> + * cpu_halt() (exit(EX_OK), monitor exits -- a clean stop). + */ + if (initproc != NULL && + SIGISMEMBER(initproc->p_sigacts->ps_sigcatch, SIGUSR2)) { ksignal(initproc, SIGUSR2); } else { - reboot(RB_POWEROFF); + shutdown_nice(RB_POWEROFF | RB_HALT); } } --- a/sys/kern/kern_shutdown.c +++ b/sys/kern/kern_shutdown.c @@ -202,11 +202,19 @@ { shutdown_howto = howto; - /* Send a signal to init(8) and have it shutdown the world */ - if (initproc != NULL) { + /* + * Send a signal to init(8) and have it shutdown the world. + * Only signal init if init can actually receive it: issignal() + * drops default-action signals to pid <= 1 (kern_sig.c), so a + * blind ksignal() before init(8) has armed its handlers (or for + * a pid 1 that is not init(8)) is a silent no-op. Fall back to + * the in-kernel shutdown path in that case. + */ + if (initproc != NULL && + SIGISMEMBER(initproc->p_sigacts->ps_sigcatch, SIGINT)) { ksignal(initproc, SIGINT); } else { - /* No init(8) running, so simply reboot */ + /* No init(8) able to take the signal: simply shut down */ boot(RB_NOSYNC); } return; @@ -573,7 +581,14 @@ if (p == NULL) return; if ((fdp = p->p_fd) != NULL) { - kern_closefrom(0); + /* + * kern_closefrom() operates on curproc; only valid here + * when the process being cleaned IS the shutdown caller. + * boot() can be entered from an ithread (e.g. the vkernel + * shutdown_intr), where curproc is NULL. + */ + if (p == curproc) + kern_closefrom(0); if (fdp->fd_cdir) { cache_drop(&fdp->fd_ncdir); vrele(fdp->fd_cdir); |