1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "bus-common-errors.h"
7 #include "bus-locator.h"
9 #include "bus-wait-for-jobs.h"
10 #include "bus-wait-for-units.h"
13 #include "string-util.h"
14 #include "systemctl-start-unit.h"
15 #include "systemctl-util.h"
16 #include "systemctl.h"
17 #include "terminal-util.h"
20 const char *verb
; /* systemctl verb */
21 const char *method
; /* Name of the specific D-Bus method */
22 const char *job_type
; /* Job type when passing to the generic EnqueueUnitJob() method */
24 { "start", "StartUnit", "start" },
25 { "stop", "StopUnit", "stop" },
26 { "condstop", "StopUnit", "stop" }, /* legacy alias */
27 { "reload", "ReloadUnit", "reload" },
28 { "restart", "RestartUnit", "restart" },
29 { "try-restart", "TryRestartUnit", "try-restart" },
30 { "condrestart", "TryRestartUnit", "try-restart" }, /* legacy alias */
31 { "reload-or-restart", "ReloadOrRestartUnit", "reload-or-restart" },
32 { "try-reload-or-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" },
33 { "reload-or-try-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
34 { "condreload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
35 { "force-reload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
38 static const char *verb_to_method(const char *verb
) {
39 for (size_t i
= 0; i
< ELEMENTSOF(unit_actions
); i
++)
40 if (streq_ptr(unit_actions
[i
].verb
, verb
))
41 return unit_actions
[i
].method
;
46 static const char *verb_to_job_type(const char *verb
) {
47 for (size_t i
= 0; i
< ELEMENTSOF(unit_actions
); i
++)
48 if (streq_ptr(unit_actions
[i
].verb
, verb
))
49 return unit_actions
[i
].job_type
;
54 static int start_unit_one(
56 const char *method
, /* When using classic per-job bus methods */
57 const char *job_type
, /* When using new-style EnqueueUnitJob() */
62 BusWaitForUnits
*wu
) {
64 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
74 log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)",
75 arg_dry_run
? "Would execute" : "Executing",
81 if (arg_show_transaction
) {
82 _cleanup_(sd_bus_error_free
) sd_bus_error enqueue_error
= SD_BUS_ERROR_NULL
;
84 /* Use the new, fancy EnqueueUnitJob() API if the user wants us to print the transaction */
92 name
, job_type
, mode
);
94 if (!sd_bus_error_has_name(&enqueue_error
, SD_BUS_ERROR_UNKNOWN_METHOD
)) {
95 (void) sd_bus_error_move(error
, &enqueue_error
);
99 /* Hmm, the API is not yet available. Let's use the classic API instead (see below). */
100 log_notice("--show-transaction not supported by this service manager, proceeding without.");
105 r
= sd_bus_message_read(reply
, "uosos", &id
, &path
, &u
, NULL
, &jt
);
107 return bus_log_parse_error(r
);
109 log_info("Enqueued anchor job %" PRIu32
" %s/%s.", id
, u
, jt
);
111 r
= sd_bus_message_enter_container(reply
, 'a', "(uosos)");
113 return bus_log_parse_error(r
);
115 r
= sd_bus_message_read(reply
, "(uosos)", &id
, NULL
, &u
, NULL
, &jt
);
117 return bus_log_parse_error(r
);
121 log_info("Enqueued auxiliary job %" PRIu32
" %s/%s.", id
, u
, jt
);
124 r
= sd_bus_message_exit_container(reply
);
126 return bus_log_parse_error(r
);
133 r
= bus_call_method(bus
, bus_systemd_mgr
, method
, error
, &reply
, "ss", name
, mode
);
137 r
= sd_bus_message_read(reply
, "o", &path
);
139 return bus_log_parse_error(r
);
142 if (need_daemon_reload(bus
, name
) > 0)
143 warn_unit_file_changed(name
);
146 log_debug("Adding %s to the set", path
);
147 r
= bus_wait_for_jobs_add(w
, path
);
149 return log_error_errno(r
, "Failed to watch job for %s: %m", name
);
153 r
= bus_wait_for_units_add_unit(wu
, name
, BUS_WAIT_FOR_INACTIVE
|BUS_WAIT_NO_JOB
, NULL
, NULL
);
155 return log_error_errno(r
, "Failed to watch unit %s: %m", name
);
161 /* There's always a fallback possible for legacy actions. */
162 if (arg_action
!= ACTION_SYSTEMCTL
)
165 if (sd_bus_error_has_name(error
, BUS_ERROR_UNIT_MASKED
) &&
166 STR_IN_SET(method
, "TryRestartUnit", "ReloadOrTryRestartUnit")) {
167 /* Ignore masked unit if try-* is requested */
169 log_debug_errno(r
, "Failed to %s %s, ignoring: %s", job_type
, name
, bus_error_message(error
, r
));
173 log_error_errno(r
, "Failed to %s %s: %s", job_type
, name
, bus_error_message(error
, r
));
175 if (!sd_bus_error_has_names(error
, BUS_ERROR_NO_SUCH_UNIT
,
176 BUS_ERROR_UNIT_MASKED
,
177 BUS_ERROR_JOB_TYPE_NOT_APPLICABLE
))
178 log_error("See %s logs and 'systemctl%s status%s %s' for details.",
179 runtime_scope_to_string(arg_runtime_scope
),
180 arg_runtime_scope
== RUNTIME_SCOPE_SYSTEM
? "" : " --user",
181 name
[0] == '-' ? " --" : "",
187 static int enqueue_marked_jobs(
191 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
192 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
195 log_debug("%s dbus call org.freedesktop.systemd1.Manager EnqueueMarkedJobs()",
196 arg_dry_run
? "Would execute" : "Executing");
201 r
= bus_call_method(bus
, bus_systemd_mgr
, "EnqueueMarkedJobs", &error
, &reply
, NULL
);
203 return log_error_errno(r
, "Failed to start jobs: %s", bus_error_message(&error
, r
));
205 _cleanup_strv_free_
char **paths
= NULL
;
206 r
= sd_bus_message_read_strv(reply
, &paths
);
208 return bus_log_parse_error(r
);
211 STRV_FOREACH(path
, paths
) {
212 log_debug("Adding %s to the set", *path
);
213 r
= bus_wait_for_jobs_add(w
, *path
);
215 return log_error_errno(r
, "Failed to watch job %s: %m", *path
);
221 const struct action_metadata action_table
[_ACTION_MAX
] = {
222 [ACTION_HALT
] = { SPECIAL_HALT_TARGET
, "halt", "replace-irreversibly" },
223 [ACTION_POWEROFF
] = { SPECIAL_POWEROFF_TARGET
, "poweroff", "replace-irreversibly" },
224 [ACTION_REBOOT
] = { SPECIAL_REBOOT_TARGET
, "reboot", "replace-irreversibly" },
225 [ACTION_KEXEC
] = { SPECIAL_KEXEC_TARGET
, "kexec", "replace-irreversibly" },
226 [ACTION_SOFT_REBOOT
] = { SPECIAL_SOFT_REBOOT_TARGET
, "soft-reboot", "replace-irreversibly" },
227 [ACTION_RUNLEVEL2
] = { SPECIAL_MULTI_USER_TARGET
, NULL
, "isolate" },
228 [ACTION_RUNLEVEL3
] = { SPECIAL_MULTI_USER_TARGET
, NULL
, "isolate" },
229 [ACTION_RUNLEVEL4
] = { SPECIAL_MULTI_USER_TARGET
, NULL
, "isolate" },
230 [ACTION_RUNLEVEL5
] = { SPECIAL_GRAPHICAL_TARGET
, NULL
, "isolate" },
231 [ACTION_RESCUE
] = { SPECIAL_RESCUE_TARGET
, "rescue", "isolate" },
232 [ACTION_EMERGENCY
] = { SPECIAL_EMERGENCY_TARGET
, "emergency", "isolate" },
233 [ACTION_DEFAULT
] = { SPECIAL_DEFAULT_TARGET
, "default", "isolate" },
234 [ACTION_EXIT
] = { SPECIAL_EXIT_TARGET
, "exit", "replace-irreversibly" },
235 [ACTION_SUSPEND
] = { SPECIAL_SUSPEND_TARGET
, "suspend", "replace-irreversibly" },
236 [ACTION_HIBERNATE
] = { SPECIAL_HIBERNATE_TARGET
, "hibernate", "replace-irreversibly" },
237 [ACTION_HYBRID_SLEEP
] = { SPECIAL_HYBRID_SLEEP_TARGET
, "hybrid-sleep", "replace-irreversibly" },
238 [ACTION_SUSPEND_THEN_HIBERNATE
] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET
, "suspend-then-hibernate", "replace-irreversibly" },
239 [ACTION_SLEEP
] = { NULL
, /* handled only by logind */ "sleep", NULL
},
242 enum action
verb_to_action(const char *verb
) {
243 for (enum action i
= 0; i
< _ACTION_MAX
; i
++)
244 if (streq_ptr(action_table
[i
].verb
, verb
))
247 return _ACTION_INVALID
;
250 static const char** make_extra_args(const char *extra_args
[static 4]) {
255 if (arg_runtime_scope
!= RUNTIME_SCOPE_SYSTEM
)
256 extra_args
[n
++] = "--user";
258 if (arg_transport
== BUS_TRANSPORT_REMOTE
) {
259 extra_args
[n
++] = "-H";
260 extra_args
[n
++] = arg_host
;
261 } else if (arg_transport
== BUS_TRANSPORT_MACHINE
) {
262 extra_args
[n
++] = "-M";
263 extra_args
[n
++] = arg_host
;
265 assert(arg_transport
== BUS_TRANSPORT_LOCAL
);
267 extra_args
[n
] = NULL
;
271 int verb_start(int argc
, char *argv
[], void *userdata
) {
272 _cleanup_(bus_wait_for_units_freep
) BusWaitForUnits
*wu
= NULL
;
273 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
274 const char *method
, *job_type
, *mode
, *one_name
, *suffix
= NULL
;
275 _cleanup_free_
char **stopped_units
= NULL
; /* Do not use _cleanup_strv_free_ */
276 _cleanup_strv_free_
char **names
= NULL
;
277 int r
, ret
= EXIT_SUCCESS
;
280 if (arg_wait
&& !STR_IN_SET(argv
[0], "start", "restart"))
281 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
282 "--wait may only be used with the 'start' or 'restart' commands.");
284 /* We cannot do sender tracking on the private bus, so we need the full one for RefUnit to implement
286 r
= acquire_bus(arg_wait
? BUS_FULL
: BUS_MANAGER
, &bus
);
290 ask_password_agent_open_maybe();
291 polkit_agent_open_maybe();
293 if (arg_action
== ACTION_SYSTEMCTL
) {
296 action
= verb_to_action(argv
[0]);
298 assert(action
!= ACTION_SLEEP
);
300 if (action
!= _ACTION_INVALID
) {
301 /* A command in style "systemctl reboot", "systemctl poweroff", … */
302 method
= "StartUnit";
304 mode
= action_table
[action
].mode
;
305 one_name
= action_table
[action
].target
;
307 if (streq(argv
[0], "isolate")) {
308 /* A "systemctl isolate <unit1> <unit2> …" command */
309 method
= "StartUnit";
313 } else if (!arg_marked
) {
314 /* A command in style of "systemctl start <unit1> <unit2> …", "systemctl stop <unit1> <unit2> …" and so on */
315 method
= verb_to_method(argv
[0]);
316 job_type
= verb_to_job_type(argv
[0]);
317 mode
= arg_job_mode();
319 method
= job_type
= mode
= NULL
;
324 /* A SysV legacy command such as "halt", "reboot", "poweroff", … */
325 assert(arg_action
>= 0 && arg_action
< _ACTION_MAX
);
326 assert(action_table
[arg_action
].target
);
327 assert(action_table
[arg_action
].mode
);
329 method
= "StartUnit";
331 mode
= action_table
[arg_action
].mode
;
332 one_name
= action_table
[arg_action
].target
;
336 names
= strv_new(one_name
);
339 } else if (!arg_marked
) {
342 r
= expand_unit_names(bus
, strv_skip(argv
, 1), suffix
, &names
, &expanded
);
344 return log_error_errno(r
, "Failed to expand names: %m");
346 if (!arg_all
&& expanded
&& streq(job_type
, "start") && !arg_quiet
) {
347 log_warning("Warning: %ssystemctl start called with a glob pattern.%s",
348 ansi_highlight_red(),
350 log_notice("Hint: unit globs expand to loaded units, so start will usually have no effect.\n"
351 " Passing --all will also load units which are pulled in by other units.\n"
352 " See systemctl(1) for more details.");
357 r
= bus_wait_for_jobs_new(bus
, &w
);
359 return log_error_errno(r
, "Could not watch jobs: %m");
363 r
= bus_call_method_async(bus
, NULL
, bus_systemd_mgr
, "Subscribe", NULL
, NULL
, NULL
);
365 return log_error_errno(r
, "Failed to enable subscription: %m");
367 r
= bus_wait_for_units_new(bus
, &wu
);
369 return log_error_errno(r
, "Failed to allocate unit watch context: %m");
373 ret
= enqueue_marked_jobs(bus
, w
);
375 STRV_FOREACH(name
, names
) {
376 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
378 r
= start_unit_one(bus
, method
, job_type
, *name
, mode
, &error
, w
, wu
);
379 if (ret
== EXIT_SUCCESS
&& r
< 0)
380 ret
= translate_bus_error_to_exit_status(r
, &error
);
382 if (r
>= 0 && streq(method
, "StopUnit")) {
383 r
= strv_push(&stopped_units
, *name
);
390 const char* extra_args
[4];
391 WaitJobsFlags flags
= 0;
393 SET_FLAG(flags
, BUS_WAIT_JOBS_LOG_ERROR
, !arg_quiet
);
394 r
= bus_wait_for_jobs(w
, flags
, make_extra_args(extra_args
));
398 /* When stopping units, warn if they can still be triggered by
399 * another active unit (socket, path, timer) */
400 if (!arg_quiet
&& !arg_no_warn
)
401 STRV_FOREACH(unit
, stopped_units
)
402 warn_triggering_units(bus
, *unit
, "Stopping", /* ignore_masked = */ true);
406 r
= bus_wait_for_units_run(wu
);
408 return log_error_errno(r
, "Failed to wait for units: %m");
409 if (r
== BUS_WAIT_FAILURE
&& ret
== EXIT_SUCCESS
)