core/systemctl: when switching root default to /sysroot/

We hardcode the path the initrd uses to prepare the final mount point at
so many places, let's also imply it in "systemctl switch-root" if not
specified.

This adds the fallback both to systemctl and to PID 1 (this is because
both to — different – checks on the path).
This commit is contained in:
Lennart Poettering 2023-04-28 16:56:39 +02:00 committed by Luca Boccassi
parent 77b7026668
commit 5ae89ef347
5 changed files with 40 additions and 28 deletions

View File

@ -1561,19 +1561,20 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
</varlistentry>
<varlistentry>
<term><command>switch-root</command> <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></term>
<term><command>switch-root</command> <optional><replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></optional></term>
<listitem>
<para>Switches to a different root directory and executes a new system manager process below it.
This is intended for use in the initrd, and will transition from the initrd's system manager
process (a.k.a. "init" process) to the main system manager process which is loaded from the
actual host root files system. This call takes two arguments: the directory that is to become the
new root directory, and the path to the new system manager binary below it to execute as PID 1.
If the latter is omitted or the empty string, a systemd binary will automatically be searched for
and used as init. If the system manager path is omitted, equal to the empty string or identical
to the path to the systemd binary, the state of the initrd's system manager process is passed to
the main system manager, which allows later introspection of the state of the services involved
in the initrd boot phase.</para>
process (a.k.a. "init" process, PID 1) to the main system manager process which is loaded from
the actual host root files system. This call takes two arguments: the directory that is to become
the new root directory, and the path to the new system manager binary below it to execute as PID
1. If both are omitted or the former is an empty string it defaults to
<filename>/sysroot/</filename>. If the latter is omitted or is an empty string, a systemd binary
will automatically be searched for and used as service manager. If the system manager path is
omitted, equal to the empty string or identical to the path to the systemd binary, the state of
the initrd's system manager process is passed to the main system manager, which allows later
introspection of the state of the services involved in the initrd boot phase.</para>
</listitem>
</varlistentry>

View File

@ -1738,15 +1738,21 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
if (!path_is_valid(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory must be a valid path.");
if (!path_is_absolute(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root path '%s' is not absolute.", root);
if (path_equal(root, "/"))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory cannot be the old root directory.");
if (isempty(root))
/* If path is not specified, default to "/sysroot" which is what we generally expect initrds
* to use */
root = "/sysroot";
else {
if (!path_is_valid(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory must be a valid path.");
if (!path_is_absolute(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root path '%s' is not absolute.", root);
if (path_equal(root, "/"))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory cannot be the old root directory.");
}
/* Safety check */
if (isempty(init)) {

View File

@ -42,14 +42,19 @@ int verb_switch_root(int argc, char *argv[], void *userdata) {
if (arg_transport != BUS_TRANSPORT_LOCAL)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot switch root remotely.");
if (argc < 2 || argc > 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong number of arguments.");
if (argc > 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments.");
root = argv[1];
if (!path_is_valid(root))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid root path: %s", root);
if (!path_is_absolute(root))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Root path is not absolute: %s", root);
if (argc >= 2) {
root = argv[1];
if (!path_is_valid(root))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid root path: %s", root);
if (!path_is_absolute(root))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Root path is not absolute: %s", root);
if (path_equal(root, "/"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot switch to current root directory: %s", root);
} else
root = "/sysroot";
if (argc >= 3)
init = argv[2];

View File

@ -243,7 +243,7 @@ static int systemctl_help(void) {
" reboot Shut down and reboot the system\n"
" kexec Shut down and reboot the system with kexec\n"
" exit [EXIT_CODE] Request user instance or container exit\n"
" switch-root ROOT [INIT] Change to a different root file system\n"
" switch-root [ROOT [INIT]] Change to a different root file system\n"
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
" hybrid-sleep Hibernate and suspend the system\n"
@ -1199,7 +1199,7 @@ static int systemctl_main(int argc, char *argv[]) {
{ "unmask", 2, VERB_ANY, 0, verb_enable },
{ "link", 2, VERB_ANY, 0, verb_enable },
{ "revert", 2, VERB_ANY, 0, verb_enable },
{ "switch-root", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_switch_root },
{ "switch-root", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_switch_root },
{ "list-dependencies", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_dependencies },
{ "set-default", 2, 2, 0, verb_set_default },
{ "get-default", VERB_ANY, 1, 0, verb_get_default },

View File

@ -20,4 +20,4 @@ OnFailureJobMode=replace-irreversibly
[Service]
Type=oneshot
ExecStart=systemctl --no-block switch-root /sysroot
ExecStart=systemctl --no-block switch-root