mirror of
https://github.com/troglobit/finit.git
synced 2026-10-16 10:39:35 +00:00
Implement switch_root functionality allowing Finit to serve as the init
in an initramfs, then transition to the real root filesystem. Useful for systems requiring early boot tasks like LUKS unlock, LVM activation, or network boot before mounting the real root. Adds INIT_CMD_SWITCH_ROOT API command, `initctl switch-root` subcommand, and HOOK_SWITCH_ROOT plugin hook point. The implementation gracefully stops services, moves virtual filesystems (/dev, /proc, /sys, /run) to the new root, deletes initramfs contents to free memory, then execs the new init as PID 1. See GitHub Discussion #292 for background.
This commit is contained in:
1 parent
e6d3eb2526
commit
373738f3d1
15 files changed
+654
-1
No files matched your search
@@ -42,6 +42,7 @@
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "initramfs.h"
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
@@ -245,6 +246,46 @@ static void bypass_shutdown(void *unused)
|
||||
do_shutdown(halt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle switch_root API command.
|
||||
* Parses data: "newroot\0newinit\0"
|
||||
* Sends ACK before attempting switch_root since it doesn't return on success.
|
||||
* Returns: result from do_switch_root() on failure, doesn't return on success.
|
||||
*/
|
||||
static int do_switch_root_api(int sd, struct init_request *rq)
|
||||
{
|
||||
char *newroot, *newinit = NULL;
|
||||
char *ptr;
|
||||
int result;
|
||||
|
||||
dbg("switch-root %s", rq->data);
|
||||
strterm(rq->data, sizeof(rq->data));
|
||||
|
||||
newroot = rq->data;
|
||||
ptr = strchr(newroot, '\0');
|
||||
if (ptr && ptr < rq->data + sizeof(rq->data) - 1) {
|
||||
ptr++;
|
||||
if (*ptr)
|
||||
newinit = ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send ACK first, since we won't return from
|
||||
* do_switch_root() on success.
|
||||
*/
|
||||
rq->cmd = INIT_CMD_ACK;
|
||||
if (write(sd, rq, sizeof(*rq)) != sizeof(*rq))
|
||||
dbg("Failed sending ACK to client");
|
||||
close(sd);
|
||||
|
||||
/* This does not return on success */
|
||||
result = do_switch_root(newroot, newinit);
|
||||
if (result)
|
||||
logit(LOG_ERR, "switch_root failed: %s", strerror(errno));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int do_reboot(int cmd, int timeout, char *buf, size_t len)
|
||||
{
|
||||
int rc = 1;
|
||||
@@ -403,6 +444,15 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
rq.cmd, rq.data);
|
||||
goto leave;
|
||||
}
|
||||
break;
|
||||
|
||||
case INIT_CMD_SWITCH_ROOT:
|
||||
if (runlevel != INIT_LEVEL && runlevel != 1) {
|
||||
warnx("switch-root only allowed in runlevel S or 1");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -503,6 +553,10 @@ static void api_cb(uev_t *w, void *arg, int events)
|
||||
result = do_reboot(rq.cmd, rq.sleeptime, rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_SWITCH_ROOT:
|
||||
do_switch_root_api(sd, &rq);
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
dbg("Client failed reading ACK");
|
||||
goto leave;
|
||||
|
||||
Reference in new issue
Block a user