Hi. In order to unmount root and properly shutdown lvm/encrypted devices/etc I've written update for sysvinit. It allows to exec into another application. It can be used to create new root filesystem in memory in that application, switch to this new root, and then unmount old root and shutdown underlying devices.
I've found similar feature request in the bugzilla: http://savannah.nongnu.org/bugs/?33517 There is even a similar patch attached. I'm attaching my patch for sysvinit-2.88 and archive with diff for inittab and test scripts. On test setup I put those scripts into /usr/local/sbin/. The scripts mostly use static busybox, and right before shutdown/reboot it invokes shell for debugging purposes. It does not shut down any lvm/encrypted device/etc, but it's not so much to write from here. In case of failing to exec into another process init goes to next record in the inittab, that's why I don't remove plain poweroff/reboot records. I think in that case it's better to shutdown at least that way compared to panicking. I'd like to have this patch merged into sysvinit. I've used gentoo linux as a test platform for this feature.
diff --git a/src/init.c b/src/init.c index 27532ad..4124f6a 100644 --- a/src/init.c +++ b/src/init.c @@ -168,6 +168,7 @@ struct actions { { "initdefault", INITDEFAULT }, { "sysinit", SYSINIT }, { "kbrequest", KBREQUEST }, + { "exec", EXEC }, { NULL, 0 }, }; @@ -924,7 +925,7 @@ void init_freeenv(char **e) * */ static -pid_t spawn(CHILD *ch, int *res) +pid_t spawn(CHILD *ch, int *res, int do_fork) { char *args[16]; /* Argv array */ char buf[136]; /* Line buffer */ @@ -933,7 +934,7 @@ pid_t spawn(CHILD *ch, int *res) time_t t; /* System time */ int oldAlarm; /* Previous alarm value */ char *proc = ch->process; /* Command line */ - pid_t pid, pgrp; /* child, console process group. */ + pid_t pid = -1, pgrp; /* child, console process group. */ sigset_t nmask, omask; /* For blocking SIGCHLD */ struct sigaction sa; @@ -1033,25 +1034,29 @@ pid_t spawn(CHILD *ch, int *res) /* * Block sigchild while forking. */ - sigemptyset(&nmask); - sigaddset(&nmask, SIGCHLD); - sigprocmask(SIG_BLOCK, &nmask, &omask); + if (do_fork) { + sigemptyset(&nmask); + sigaddset(&nmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &nmask, &omask); + } - if ((pid = fork()) == 0) { + if ((!do_fork) || ((pid = fork()) == 0)) { - close(0); - close(1); - close(2); - if (pipe_fd >= 0) close(pipe_fd); + if (do_fork) { + close(0); + close(1); + close(2); + if (pipe_fd >= 0) close(pipe_fd); - sigprocmask(SIG_SETMASK, &omask, NULL); + sigprocmask(SIG_SETMASK, &omask, NULL); + } /* * In sysinit, boot, bootwait or single user mode: * for any wait-type subprocess we _force_ the console * to be its controlling tty. */ - if (strchr("*#sS", runlevel) && ch->flags & WAITING) { + if (strchr("*#sS", runlevel) && ch->flags & WAITING && do_fork) { /* * We fork once extra. This is so that we can * wait and change the process group and session @@ -1123,8 +1128,10 @@ pid_t spawn(CHILD *ch, int *res) /* Set ioctl settings to default ones */ console_stty(); - } else { - setsid(); + } else { + if (do_fork) { + setsid(); + } if ((f = console_open(O_RDWR|O_NOCTTY)) < 0) { initlog(L_VB, "open(%s): %s", console_dev, strerror(errno)); @@ -1150,8 +1157,10 @@ pid_t spawn(CHILD *ch, int *res) * FIXME: that's for compatibility with *very* * old getties - probably it can be taken out. */ - if (ch->process[0] != '+') - write_utmp_wtmp("", ch->id, getpid(), INIT_PROCESS, ""); + if (do_fork) { + if (ch->process[0] != '+') + write_utmp_wtmp("", ch->id, getpid(), INIT_PROCESS, ""); + } /* Reset all the signals, set up environment */ for(f = 1; f < NSIG; f++) SETSIG(sa, f, SIG_DFL, SA_RESTART); @@ -1173,16 +1182,31 @@ pid_t spawn(CHILD *ch, int *res) } initlog(L_VB, "cannot execute \"%s\"", args[1]); - if (ch->process[0] != '+') - write_utmp_wtmp("", ch->id, getpid(), DEAD_PROCESS, NULL); - exit(1); + if (do_fork) { + if (ch->process[0] != '+') + write_utmp_wtmp("", ch->id, getpid(), DEAD_PROCESS, NULL); + exit(1); + } + else + { + /* + * exec() failed, continue init process + * Make sure there are some fallback records after an exec record + */ + + initlog(L_VB, "cannot execute \"%s\", continuing to next action", args[1]); + } } + *res = pid; - sigprocmask(SIG_SETMASK, &omask, NULL); - INITDBG(L_VB, "Started id %s (pid %d)", ch->id, pid); + if (do_fork) { + sigprocmask(SIG_SETMASK, &omask, NULL); - if (pid == -1) { + INITDBG(L_VB, "Started id %s (pid %d)", ch->id, pid); + } + + if (do_fork && (pid == -1)) { initlog(L_VB, "cannot fork, retry.."); do_sleep(5); continue; @@ -1220,8 +1244,11 @@ void startup(CHILD *ch) case ONDEMAND: case RESPAWN: ch->flags |= RUNNING; - (void)spawn(ch, &(ch->pid)); + (void)spawn(ch, &(ch->pid), 1); break; + case EXEC: + (void)spawn(ch, &(ch->pid), 0); + break; } } @@ -2606,7 +2633,7 @@ void init_main(void) if (emerg_shell) { pid_t rc; SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART); - if (spawn(&ch_emerg, &f) > 0) { + if (spawn(&ch_emerg, &f, 1) > 0) { while((rc = wait(&st)) != f) if (rc < 0 && errno == ECHILD) break; diff --git a/src/init.h b/src/init.h index 9763140..17a3f0d 100644 --- a/src/init.h +++ b/src/init.h @@ -78,6 +78,7 @@ void wall(const char *text, int remote); #define SYSINIT 13 #define POWERFAILNOW 14 #define KBREQUEST 15 +#define EXEC 16 /* Information about a process in the in-core inittab */ typedef struct _child_ {
sysvinit-data.tar.bz2
Description: application/bzip