Changeset View
Standalone View
sbin/init/init.c
Show All 40 Lines | |||||
static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; | static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; | ||||
#endif | #endif | ||||
static const char rcsid[] = | static const char rcsid[] = | ||||
"$FreeBSD$"; | "$FreeBSD$"; | ||||
#endif /* not lint */ | #endif /* not lint */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||
#include <sys/mman.h> | |||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <db.h> | #include <db.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
Show All 17 Lines | |||||
#ifdef SECURE | #ifdef SECURE | ||||
#include <pwd.h> | #include <pwd.h> | ||||
#endif | #endif | ||||
#ifdef LOGIN_CAP | #ifdef LOGIN_CAP | ||||
#include <login_cap.h> | #include <login_cap.h> | ||||
#endif | #endif | ||||
#include "mntopts.h" | |||||
#include "pathnames.h" | #include "pathnames.h" | ||||
/* | /* | ||||
* Sleep times; used to prevent thrashing. | * Sleep times; used to prevent thrashing. | ||||
*/ | */ | ||||
#define GETTY_SPACING 5 /* N secs minimum getty spacing */ | #define GETTY_SPACING 5 /* N secs minimum getty spacing */ | ||||
#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ | #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ | ||||
#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ | #define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ | ||||
#define WINDOW_WAIT 3 /* wait N secs after starting window */ | #define WINDOW_WAIT 3 /* wait N secs after starting window */ | ||||
#define STALL_TIMEOUT 30 /* wait N secs after warning */ | #define STALL_TIMEOUT 30 /* wait N secs after warning */ | ||||
#define DEATH_WATCH 10 /* wait N secs for procs to die */ | #define DEATH_WATCH 10 /* wait N secs for procs to die */ | ||||
#define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ | #define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ | ||||
#define RESOURCE_RC "daemon" | #define RESOURCE_RC "daemon" | ||||
#define RESOURCE_WINDOW "default" | #define RESOURCE_WINDOW "default" | ||||
#define RESOURCE_GETTY "default" | #define RESOURCE_GETTY "default" | ||||
static void handle(sig_t, ...); | static void handle(sig_t, ...); | ||||
static void delset(sigset_t *, ...); | static void delset(sigset_t *, ...); | ||||
static void stall(const char *, ...) __printflike(1, 2); | static void stall(const char *, ...) __printflike(1, 2); | ||||
static void warning(const char *, ...) __printflike(1, 2); | static void warning(const char *, ...) __printflike(1, 2); | ||||
static void emergency(const char *, ...) __printflike(1, 2); | static void emergency(const char *, ...) __printflike(1, 2); | ||||
static void disaster(int); | static void disaster(int); | ||||
static void badsys(int); | static void badsys(int); | ||||
static void revoke_ttys(void); | |||||
static int runshutdown(void); | static int runshutdown(void); | ||||
static char *strk(char *); | static char *strk(char *); | ||||
/* | /* | ||||
* We really need a recursive typedef... | * We really need a recursive typedef... | ||||
* The following at least guarantees that the return type of (*state_t)() | * The following at least guarantees that the return type of (*state_t)() | ||||
* is sufficiently wide to hold a function pointer. | * is sufficiently wide to hold a function pointer. | ||||
*/ | */ | ||||
typedef long (*state_func_t)(void); | typedef long (*state_func_t)(void); | ||||
typedef state_func_t (*state_t)(void); | typedef state_func_t (*state_t)(void); | ||||
static state_func_t single_user(void); | static state_func_t single_user(void); | ||||
static state_func_t runcom(void); | static state_func_t runcom(void); | ||||
static state_func_t read_ttys(void); | static state_func_t read_ttys(void); | ||||
static state_func_t multi_user(void); | static state_func_t multi_user(void); | ||||
static state_func_t clean_ttys(void); | static state_func_t clean_ttys(void); | ||||
static state_func_t catatonia(void); | static state_func_t catatonia(void); | ||||
static state_func_t death(void); | static state_func_t death(void); | ||||
static state_func_t death_single(void); | static state_func_t death_single(void); | ||||
static state_func_t reroot(void); | |||||
static state_func_t reroot_phase_two(void); | |||||
static state_func_t run_script(const char *); | static state_func_t run_script(const char *); | ||||
static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; | static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; | ||||
#define FALSE 0 | #define FALSE 0 | ||||
#define TRUE 1 | #define TRUE 1 | ||||
static int Reboot = FALSE; | static int Reboot = FALSE; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* The mother of all processes. | * The mother of all processes. | ||||
*/ | */ | ||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
state_t initial_transition = runcom; | state_t initial_transition = runcom; | ||||
char kenv_value[PATH_MAX]; | char kenv_value[PATH_MAX]; | ||||
int c; | int c, error; | ||||
struct sigaction sa; | struct sigaction sa; | ||||
sigset_t mask; | sigset_t mask; | ||||
/* Dispose of random users. */ | /* Dispose of random users. */ | ||||
if (getuid() != 0) | if (getuid() != 0) | ||||
errx(1, "%s", strerror(EPERM)); | errx(1, "%s", strerror(EPERM)); | ||||
/* System V users like to reexec init. */ | /* System V users like to reexec init. */ | ||||
Show All 16 Lines | if (argc > 1) { | ||||
sig = SIGINT; | sig = SIGINT; | ||||
break; | break; | ||||
case 'c': /* block further logins */ | case 'c': /* block further logins */ | ||||
sig = SIGTSTP; | sig = SIGTSTP; | ||||
break; | break; | ||||
case 'q': /* rescan /etc/ttys */ | case 'q': /* rescan /etc/ttys */ | ||||
sig = SIGHUP; | sig = SIGHUP; | ||||
break; | break; | ||||
case 'r': /* remount root */ | |||||
kib: Wouldn't it be somewhat more consistent to use 'R' for option ? | |||||
Done Inline ActionsHm, all the other flags for reboot(8) are lowercase. Or did you mean to change "init -R" to "init -r"? trasz: Hm, all the other flags for reboot(8) are lowercase. Or did you mean to change "init -R" to… | |||||
Done Inline ActionsErm, what I meant was - all the other flags for init(8) are lowercase, so how does "-R" improve consistency? trasz: Erm, what I meant was - all the other flags for init(8) are lowercase, so how does "-R" improve… | |||||
Done Inline ActionsSeveral lines below, where you handle options for pid == 1 (i.e. reexec) case, you use '-R'. In fact I think that the re-exec flag should be changed to -r. kib: Several lines below, where you handle options for pid == 1 (i.e. reexec) case, you use '-R'. | |||||
sig = SIGEMT; | |||||
break; | |||||
default: | default: | ||||
goto invalid; | goto invalid; | ||||
} | } | ||||
kill(1, sig); | kill(1, sig); | ||||
_exit(0); | _exit(0); | ||||
} else | } else | ||||
invalid: | invalid: | ||||
errx(1, "invalid run-level ``%s''", argv[1]); | errx(1, "invalid run-level ``%s''", argv[1]); | ||||
} else | } else | ||||
#endif | #endif | ||||
errx(1, "already running"); | errx(1, "already running"); | ||||
} | } | ||||
/* | /* | ||||
* Note that this does NOT open a file... | * Note that this does NOT open a file... | ||||
* Does 'init' deserve its own facility number? | * Does 'init' deserve its own facility number? | ||||
*/ | */ | ||||
openlog("init", LOG_CONS, LOG_AUTH); | openlog("init", LOG_CONS, LOG_AUTH); | ||||
/* | /* | ||||
* Create an initial session. | * Create an initial session. | ||||
*/ | */ | ||||
if (setsid() < 0) | if (setsid() < 0 && (errno != EPERM || getsid(0) != 1)) | ||||
warning("initial setsid() failed: %m"); | warning("initial setsid() failed: %m"); | ||||
/* | /* | ||||
* Establish an initial user so that programs running | * Establish an initial user so that programs running | ||||
* single user do not freak out and die (like passwd). | * single user do not freak out and die (like passwd). | ||||
*/ | */ | ||||
if (setlogin("root") < 0) | if (setlogin("root") < 0) | ||||
warning("setlogin() failed: %m"); | warning("setlogin() failed: %m"); | ||||
/* | /* | ||||
* This code assumes that we always get arguments through flags, | * This code assumes that we always get arguments through flags, | ||||
* never through bits set in some random machine register. | * never through bits set in some random machine register. | ||||
*/ | */ | ||||
while ((c = getopt(argc, argv, "dsf")) != -1) | while ((c = getopt(argc, argv, "dsfr")) != -1) | ||||
switch (c) { | switch (c) { | ||||
case 'd': | case 'd': | ||||
devfs = 1; | devfs = 1; | ||||
break; | break; | ||||
case 's': | case 's': | ||||
initial_transition = single_user; | initial_transition = single_user; | ||||
break; | break; | ||||
case 'f': | case 'f': | ||||
runcom_mode = FASTBOOT; | runcom_mode = FASTBOOT; | ||||
break; | break; | ||||
case 'r': | |||||
initial_transition = reroot_phase_two; | |||||
break; | |||||
default: | default: | ||||
warning("unrecognized flag '-%c'", c); | warning("unrecognized flag '-%c'", c); | ||||
break; | break; | ||||
} | } | ||||
if (optind != argc) | if (optind != argc) | ||||
warning("ignoring excess arguments"); | warning("ignoring excess arguments"); | ||||
/* | /* | ||||
* We catch or block signals rather than ignore them, | * We catch or block signals rather than ignore them, | ||||
* so that they get reset on exec. | * so that they get reset on exec. | ||||
*/ | */ | ||||
handle(badsys, SIGSYS, 0); | handle(badsys, SIGSYS, 0); | ||||
handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, | handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, | ||||
SIGXFSZ, 0); | SIGXFSZ, 0); | ||||
handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGUSR1, | handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, | ||||
SIGUSR2, 0); | SIGUSR1, SIGUSR2, 0); | ||||
handle(alrm_handler, SIGALRM, 0); | handle(alrm_handler, SIGALRM, 0); | ||||
sigfillset(&mask); | sigfillset(&mask); | ||||
delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, | delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, | ||||
SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, | SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, | ||||
SIGUSR1, SIGUSR2, 0); | SIGALRM, SIGUSR1, SIGUSR2, 0); | ||||
sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); | sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); | ||||
sigemptyset(&sa.sa_mask); | sigemptyset(&sa.sa_mask); | ||||
sa.sa_flags = 0; | sa.sa_flags = 0; | ||||
sa.sa_handler = SIG_IGN; | sa.sa_handler = SIG_IGN; | ||||
sigaction(SIGTTIN, &sa, (struct sigaction *)0); | sigaction(SIGTTIN, &sa, (struct sigaction *)0); | ||||
sigaction(SIGTTOU, &sa, (struct sigaction *)0); | sigaction(SIGTTOU, &sa, (struct sigaction *)0); | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | if (s != NULL) { | ||||
iov[3].iov_base = _path_dev; | iov[3].iov_base = _path_dev; | ||||
iov[3].iov_len = sizeof(_path_dev); | iov[3].iov_len = sizeof(_path_dev); | ||||
} | } | ||||
nmount(iov, 4, 0); | nmount(iov, 4, 0); | ||||
if (s != NULL) | if (s != NULL) | ||||
free(s); | free(s); | ||||
} | } | ||||
if (initial_transition != reroot_phase_two) { | |||||
/* | /* | ||||
* Unmount reroot leftovers. This runs after init(8) | |||||
* gets reexecuted after reroot_phase_two() is done. | |||||
*/ | |||||
error = unmount(_PATH_REROOT, MNT_FORCE); | |||||
Not Done Inline ActionsShouldn't some additional checks be done there ? E.g. verify that something is mounted (as a compliment to kernel, to avoid dangerous syscall), and that the type of the mounted fs is expected one (tmpfs) ? This again raises the question of the MNT flag that could be checked to ensure that we unmount our fs. kib: Shouldn't some additional checks be done there ? E.g. verify that something is mounted (as a… | |||||
Not Done Inline ActionsWhat's dangerous in unmount(2)? As for interfering: on one hand you're right, on the other - if someone wants to interfere with the operation, and they are UID 0, then it's their fault. trasz: What's dangerous in unmount(2)? As for interfering: on one hand you're right, on the other… | |||||
Not Done Inline ActionsSystem should be as tamper-resistant as it could be, but not prevent users from being ingenious in their system usage. I do not see any useful consequences in tricking init into unmounting something which was not mounted transiently by reroot. In other words, the check could protect a mistaken user from destroying the system. kib: System should be as tamper-resistant as it could be, but not prevent users from being ingenious… | |||||
Not Done Inline ActionsI understand your point, but still - if a "mistaken user" manages to mount something on /dev/reroot/ during root mount, before even the init(8) gets started, then... well, it's their fault. There is no way to do it by accident. trasz: I understand your point, but still - if a "mistaken user" manages to mount something on… | |||||
if (error != 0 && errno != EINVAL) | |||||
warning("Cannot unmount %s: %m", _PATH_REROOT); | |||||
} | |||||
/* | |||||
* Start the state machine. | * Start the state machine. | ||||
*/ | */ | ||||
transition(initial_transition); | transition(initial_transition); | ||||
/* | /* | ||||
* Should never reach here. | * Should never reach here. | ||||
*/ | */ | ||||
return 1; | return 1; | ||||
▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
write_stderr(const char *message) | write_stderr(const char *message) | ||||
{ | { | ||||
write(STDERR_FILENO, message, strlen(message)); | write(STDERR_FILENO, message, strlen(message)); | ||||
} | } | ||||
static int | |||||
read_file(const char *path, void **bufp, size_t *bufsizep) | |||||
{ | |||||
struct stat sb; | |||||
size_t bufsize; | |||||
void *buf; | |||||
ssize_t nbytes; | |||||
int error, fd; | |||||
fd = open(path, O_RDONLY); | |||||
if (fd < 0) { | |||||
emergency("%s: %s", path, strerror(errno)); | |||||
return (-1); | |||||
} | |||||
error = fstat(fd, &sb); | |||||
if (error != 0) { | |||||
emergency("fstat: %s", strerror(errno)); | |||||
return (error); | |||||
} | |||||
bufsize = sb.st_size; | |||||
buf = malloc(bufsize); | |||||
if (buf == NULL) { | |||||
emergency("malloc: %s", strerror(errno)); | |||||
return (error); | |||||
} | |||||
nbytes = read(fd, buf, bufsize); | |||||
if (nbytes != (ssize_t)bufsize) { | |||||
emergency("read: %s", strerror(errno)); | |||||
free(buf); | |||||
return (error); | |||||
} | |||||
error = close(fd); | |||||
if (error != 0) { | |||||
emergency("close: %s", strerror(errno)); | |||||
free(buf); | |||||
return (error); | |||||
} | |||||
*bufp = buf; | |||||
*bufsizep = bufsize; | |||||
return (0); | |||||
} | |||||
static int | |||||
create_file(const char *path, void *buf, size_t bufsize) | |||||
{ | |||||
ssize_t nbytes; | |||||
int error, fd; | |||||
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0700); | |||||
if (fd < 0) { | |||||
emergency("%s: %s", path, strerror(errno)); | |||||
return (-1); | |||||
} | |||||
nbytes = write(fd, buf, bufsize); | |||||
if (nbytes != (ssize_t)bufsize) { | |||||
emergency("write: %s", strerror(errno)); | |||||
return (-1); | |||||
} | |||||
error = close(fd); | |||||
if (error != 0) { | |||||
emergency("close: %s", strerror(errno)); | |||||
free(buf); | |||||
return (-1); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
mount_tmpfs(const char *fspath) | |||||
{ | |||||
struct iovec *iov; | |||||
char errmsg[255]; | |||||
int error, iovlen; | |||||
Done Inline ActionsStyle: do not use initialization in local declarations. kib: Style: do not use initialization in local declarations. | |||||
iov = NULL; | |||||
iovlen = 0; | |||||
Done Inline ActionsUnneeded empty line. kib: Unneeded empty line. | |||||
memset(errmsg, 0, sizeof(errmsg)); | |||||
build_iovec(&iov, &iovlen, "fstype", | |||||
__DECONST(void *, "tmpfs"), (size_t)-1); | |||||
build_iovec(&iov, &iovlen, "fspath", | |||||
__DECONST(void *, fspath), (size_t)-1); | |||||
build_iovec(&iov, &iovlen, "errmsg", | |||||
errmsg, sizeof(errmsg)); | |||||
error = nmount(iov, iovlen, 0); | |||||
if (error != 0) { | |||||
if (*errmsg != '\0') { | |||||
emergency("cannot mount tmpfs on %s: %s: %s", | |||||
fspath, errmsg, strerror(errno)); | |||||
} else { | |||||
emergency("cannot mount tmpfs on %s: %s", | |||||
fspath, strerror(errno)); | |||||
} | |||||
return (error); | |||||
} | |||||
Done Inline ActionsSame. kib: Same. | |||||
return (0); | |||||
} | |||||
static state_func_t | |||||
reroot(void) | |||||
{ | |||||
void *buf; | |||||
Done Inline ActionsStyle. Put error declaration last. kib: Style. Put error declaration last. | |||||
size_t bufsize; | |||||
int error; | |||||
revoke_ttys(); | |||||
Done Inline ActionsI hoped that userpace part of reroot kills all user processes except init. Apparently it does not. kib: I hoped that userpace part of reroot kills all user processes except init. Apparently it does… | |||||
Done Inline ActionsI've added a call to kill(-1, SIGKILL) in reroot_phase_two(). trasz: I've added a call to kill(-1, SIGKILL) in reroot_phase_two(). | |||||
runshutdown(); | |||||
/* | /* | ||||
* Make sure nobody can interfere with our scheme. | |||||
*/ | |||||
error = kill(-1, SIGKILL); | |||||
Done Inline ActionsThis should be respecting init_path bapt: This should be respecting init_path | |||||
Done Inline ActionsActually here it should read from the path provided by: kern.proc.pathname bapt: Actually here it should read from the path provided by: kern.proc.pathname | |||||
Done Inline ActionsI'd prefer to obtain init path in a single unified way. And the problem is, the executable is exec(2)ed twice, from different paths, so the kern.proc.pathname would return "/sbin/init" only the first time, and "/dev/reroot/init" after that. Perhaps we should have a sysctl, eg. kern.init_path? An alternative would be to use kenv(2) to obtain "init_path", and use the default init path if not found. But what if the kernel chose some other value, eg. in /rescue? trasz: I'd prefer to obtain init path in a single unified way. And the problem is, the executable is… | |||||
Done Inline Actionsrespecting init_path from loader.conf/kenv is good enough for my usecase, for sure the current mechanism does not work for my use case. bapt: respecting init_path from loader.conf/kenv is good enough for my usecase, for sure the current… | |||||
Not Done Inline ActionsOk, I made it use init_path. Please test it to see if the semantics fits your use case. trasz: Ok, I made it use init_path. Please test it to see if the semantics fits your use case. | |||||
if (error != 0) { | |||||
emergency("kill(2) failed: %s", strerror(errno)); | |||||
goto out; | |||||
} | |||||
/* | |||||
* Copy the init binary into tmpfs, so that we can unmount | |||||
* the old rootfs without committing suicide. | |||||
*/ | |||||
error = read_file(_PATH_INIT, &buf, &bufsize); | |||||
if (error != 0) | |||||
goto out; | |||||
error = mount_tmpfs(_PATH_REROOT); | |||||
if (error != 0) | |||||
goto out; | |||||
error = create_file(_PATH_REROOT_INIT, buf, bufsize); | |||||
if (error != 0) | |||||
goto out; | |||||
Done Inline ActionsUnneeded empty line. kib: Unneeded empty line. | |||||
/* | |||||
* Execute the temporary init. | |||||
*/ | |||||
execl(_PATH_REROOT_INIT, _PATH_REROOT_INIT, "-r", NULL); | |||||
emergency("cannot exec %s: %s", _PATH_REROOT_INIT, strerror(errno)); | |||||
out: | |||||
emergency("reroot failed; going to single user mode"); | |||||
return (state_func_t) single_user; | |||||
} | |||||
static state_func_t | |||||
reroot_phase_two(void) | |||||
{ | |||||
int error; | |||||
/* | |||||
* Ask the kernel to mount the new rootfs. | |||||
*/ | |||||
error = reboot(RB_REROOT); | |||||
if (error != 0) { | |||||
emergency("RB_REBOOT failed: %s", strerror(errno)); | |||||
goto out; | |||||
Done Inline ActionsThat should be respecting init_path bapt: That should be respecting init_path | |||||
} | |||||
/* | |||||
* Execute init(8) from the new rootfs. | |||||
* | |||||
* Note that at this point, all this warning() stuff is useless | |||||
* anyway; we don't have stderr nor stdout. | |||||
*/ | |||||
execl(_PATH_INIT, _PATH_INIT, NULL); | |||||
emergency("cannot exec %s: %s", _PATH_INIT, strerror(errno)); | |||||
out: | |||||
emergency("reroot failed; going to single user mode"); | |||||
return (state_func_t) single_user; | |||||
} | |||||
/* | |||||
* Bring the system up single user. | * Bring the system up single user. | ||||
*/ | */ | ||||
static state_func_t | static state_func_t | ||||
single_user(void) | single_user(void) | ||||
{ | { | ||||
pid_t pid, wpid; | pid_t pid, wpid; | ||||
int status; | int status; | ||||
sigset_t mask; | sigset_t mask; | ||||
▲ Show 20 Lines • Show All 686 Lines • ▼ Show 20 Lines | else | ||||
requested_transition = death_single; | requested_transition = death_single; | ||||
break; | break; | ||||
case SIGTSTP: | case SIGTSTP: | ||||
if (current_state == runcom || current_state == read_ttys || | if (current_state == runcom || current_state == read_ttys || | ||||
current_state == clean_ttys || | current_state == clean_ttys || | ||||
current_state == multi_user || current_state == catatonia) | current_state == multi_user || current_state == catatonia) | ||||
requested_transition = catatonia; | requested_transition = catatonia; | ||||
break; | break; | ||||
case SIGEMT: | |||||
requested_transition = reroot; | |||||
break; | |||||
default: | default: | ||||
requested_transition = 0; | requested_transition = 0; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Take the system multiuser. | * Take the system multiuser. | ||||
▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Bring the system down to single user. | * Bring the system down to single user. | ||||
*/ | */ | ||||
static state_func_t | static state_func_t | ||||
death(void) | death(void) | ||||
{ | { | ||||
session_t *sp; | |||||
int block, blocked; | int block, blocked; | ||||
size_t len; | size_t len; | ||||
/* Temporarily block suspend. */ | /* Temporarily block suspend. */ | ||||
len = sizeof(blocked); | len = sizeof(blocked); | ||||
block = 1; | block = 1; | ||||
if (sysctlbyname("kern.suspend_blocked", &blocked, &len, | if (sysctlbyname("kern.suspend_blocked", &blocked, &len, | ||||
&block, sizeof(block)) == -1) | &block, sizeof(block)) == -1) | ||||
blocked = 0; | blocked = 0; | ||||
/* | /* | ||||
* Also revoke the TTY here. Because runshutdown() may reopen | * Also revoke the TTY here. Because runshutdown() may reopen | ||||
* the TTY whose getty we're killing here, there is no guarantee | * the TTY whose getty we're killing here, there is no guarantee | ||||
* runshutdown() will perform the initial open() call, causing | * runshutdown() will perform the initial open() call, causing | ||||
* the terminal attributes to be misconfigured. | * the terminal attributes to be misconfigured. | ||||
*/ | */ | ||||
for (sp = sessions; sp; sp = sp->se_next) { | revoke_ttys(); | ||||
sp->se_flags |= SE_SHUTDOWN; | |||||
kill(sp->se_process, SIGHUP); | |||||
revoke(sp->se_device); | |||||
} | |||||
/* Try to run the rc.shutdown script within a period of time */ | /* Try to run the rc.shutdown script within a period of time */ | ||||
runshutdown(); | runshutdown(); | ||||
/* Unblock suspend if we blocked it. */ | /* Unblock suspend if we blocked it. */ | ||||
if (!blocked) | if (!blocked) | ||||
sysctlbyname("kern.suspend_blocked", NULL, NULL, | sysctlbyname("kern.suspend_blocked", NULL, NULL, | ||||
&blocked, sizeof(blocked)); | &blocked, sizeof(blocked)); | ||||
Show All 27 Lines | for (i = 0; i < 2; ++i) { | ||||
if (errno == ECHILD) | if (errno == ECHILD) | ||||
return (state_func_t) single_user; | return (state_func_t) single_user; | ||||
} | } | ||||
warning("some processes would not die; ps axl advised"); | warning("some processes would not die; ps axl advised"); | ||||
return (state_func_t) single_user; | return (state_func_t) single_user; | ||||
} | |||||
static void | |||||
revoke_ttys(void) | |||||
{ | |||||
session_t *sp; | |||||
for (sp = sessions; sp; sp = sp->se_next) { | |||||
sp->se_flags |= SE_SHUTDOWN; | |||||
kill(sp->se_process, SIGHUP); | |||||
revoke(sp->se_device); | |||||
} | |||||
} | } | ||||
/* | /* | ||||
* Run the system shutdown script. | * Run the system shutdown script. | ||||
* | * | ||||
* Exit codes: XXX I should document more | * Exit codes: XXX I should document more | ||||
* -2 shutdown script terminated abnormally | * -2 shutdown script terminated abnormally | ||||
* -1 fatal error - can't run script | * -1 fatal error - can't run script | ||||
Show All 18 Lines | runshutdown(void) | ||||
* file does not exist. If the stat() here fails for other | * file does not exist. If the stat() here fails for other | ||||
* reasons, we'll let the shell complain. | * reasons, we'll let the shell complain. | ||||
*/ | */ | ||||
if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) | if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) | ||||
return 0; | return 0; | ||||
shell = get_shell(); | shell = get_shell(); | ||||
if ((pid = fork()) == 0) { | if ((pid = fork()) == 0) { | ||||
Done Inline ActionsWhy is this block moved ? kib: Why is this block moved ? | |||||
Done Inline ActionsTo make it possible to call that whole block from reroot(), so that initial part of reroot process looks to userspace just like shutdown. trasz: To make it possible to call that whole block from reroot(), so that initial part of reroot… | |||||
Done Inline ActionsOk, let me explain my question explicitely. The runshutdown() function was clean, it executed /etc/rc.shutdown script and that was all. The death() state handler brings system to the single mode, it needs to execute several steps, like shut down the getty sessions, run the rc.shutdown, switch state to single user and so on. If you want to get the functionality of shutting down getty sessions + rc.shutdown, you should move getty code into new helper, and call the new helper and runshutdown() from that place, instead of hacking runshutdown. kib: Ok, let me explain my question explicitely. The runshutdown() function was clean, it executed… | |||||
Not Done Inline ActionsAh, you're right. Fixed. trasz: Ah, you're right. Fixed. | |||||
sigemptyset(&sa.sa_mask); | sigemptyset(&sa.sa_mask); | ||||
sa.sa_flags = 0; | sa.sa_flags = 0; | ||||
sa.sa_handler = SIG_IGN; | sa.sa_handler = SIG_IGN; | ||||
sigaction(SIGTSTP, &sa, (struct sigaction *)0); | sigaction(SIGTSTP, &sa, (struct sigaction *)0); | ||||
sigaction(SIGHUP, &sa, (struct sigaction *)0); | sigaction(SIGHUP, &sa, (struct sigaction *)0); | ||||
open_console(); | open_console(); | ||||
▲ Show 20 Lines • Show All 143 Lines • Show Last 20 Lines |
Wouldn't it be somewhat more consistent to use 'R' for option ?