1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "bus-error.h"
4 #include "bus-locator.h"
5 #include "parse-util.h"
6 #include "path-util.h"
7 #include "proc-cmdline.h"
8 #include "signal-util.h"
9 #include "stat-util.h"
10 #include "systemctl-switch-root.h"
11 #include "systemctl-util.h"
12 #include "systemctl.h"
13 
verb_switch_root(int argc,char * argv[],void * userdata)14 int verb_switch_root(int argc, char *argv[], void *userdata) {
15         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
16         _cleanup_free_ char *cmdline_init = NULL;
17         const char *root, *init;
18         sd_bus *bus;
19         int r;
20 
21         if (arg_transport != BUS_TRANSPORT_LOCAL)
22                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot switch root remotely.");
23 
24         if (argc < 2 || argc > 3)
25                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong number of arguments.");
26 
27         root = argv[1];
28 
29         if (argc >= 3)
30                 init = argv[2];
31         else {
32                 r = proc_cmdline_get_key("init", 0, &cmdline_init);
33                 if (r < 0)
34                         log_debug_errno(r, "Failed to parse /proc/cmdline: %m");
35 
36                 init = cmdline_init;
37         }
38 
39         init = empty_to_null(init);
40         if (init) {
41                 const char *root_systemd_path = NULL, *root_init_path = NULL;
42 
43                 root_systemd_path = prefix_roota(root, "/" SYSTEMD_BINARY_PATH);
44                 root_init_path = prefix_roota(root, init);
45 
46                 /* If the passed init is actually the same as the systemd binary, then let's suppress it. */
47                 if (files_same(root_init_path, root_systemd_path, 0) > 0)
48                         init = NULL;
49         }
50 
51         /* Instruct PID1 to exclude us from its killing spree applied during the transition. Otherwise we
52          * would exit with a failure status even though the switch to the new root has succeed. */
53         assert(saved_argv);
54         assert(saved_argv[0]);
55         saved_argv[0][0] = '@';
56 
57         r = acquire_bus(BUS_MANAGER, &bus);
58         if (r < 0)
59                 return r;
60 
61         /* If we are slow to exit after the root switch, the new systemd instance will send us a signal to
62          * terminate. Just ignore it and exit normally.  This way the unit does not end up as failed. */
63         r = ignore_signals(SIGTERM);
64         if (r < 0)
65                 log_warning_errno(r, "Failed to change disposition of SIGTERM to ignore: %m");
66 
67         log_debug("Switching root - root: %s; init: %s", root, strna(init));
68 
69         r = bus_call_method(bus, bus_systemd_mgr, "SwitchRoot", &error, NULL, "ss", root, init);
70         if (r < 0) {
71                 (void) default_signals(SIGTERM);
72 
73                 return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r));
74         }
75 
76         return 0;
77 }
78