From 5ae89ef34774a777c5af7af48b1ad431d10a425e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Apr 2023 16:56:39 +0200 Subject: [PATCH] core/systemctl: when switching root default to /sysroot/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- man/systemctl.xml | 19 ++++++++++--------- src/core/dbus-manager.c | 24 +++++++++++++++--------- src/systemctl/systemctl-switch-root.c | 19 ++++++++++++------- src/systemctl/systemctl.c | 4 ++-- units/initrd-switch-root.service | 2 +- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 639c8881109..009aabacb9d 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1561,19 +1561,20 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - switch-root ROOT INIT + switch-root ROOT INIT 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. + 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 + /sysroot/. 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. diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 2feec0bd7df..f6c9ae69406 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -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)) { diff --git a/src/systemctl/systemctl-switch-root.c b/src/systemctl/systemctl-switch-root.c index 8cfcaf89358..b59ea701c91 100644 --- a/src/systemctl/systemctl-switch-root.c +++ b/src/systemctl/systemctl-switch-root.c @@ -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]; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 201d64a1f99..71c068b09e1 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -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 }, diff --git a/units/initrd-switch-root.service b/units/initrd-switch-root.service index b19b7bbc21e..f81b47871e5 100644 --- a/units/initrd-switch-root.service +++ b/units/initrd-switch-root.service @@ -20,4 +20,4 @@ OnFailureJobMode=replace-irreversibly [Service] Type=oneshot -ExecStart=systemctl --no-block switch-root /sysroot +ExecStart=systemctl --no-block switch-root