diff options
Diffstat (limited to 'src/systemctl/systemctl.c')
-rw-r--r-- | src/systemctl/systemctl.c | 1567 |
1 files changed, 778 insertions, 789 deletions
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 34e4751b94..e0a69867d2 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -37,7 +37,6 @@ #include "sd-daemon.h" #include "sd-login.h" -#include "build.h" #include "bus-common-errors.h" #include "bus-error.h" #include "bus-message.h" @@ -74,6 +73,7 @@ #include "unit-name.h" #include "util.h" #include "utmp-wtmp.h" +#include "verbs.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -134,23 +134,61 @@ static enum action { _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; +static const char *arg_host = NULL; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; static bool arg_firmware_setup = false; static bool arg_now = false; -static bool original_stdout_is_tty; - -static int daemon_reload(sd_bus *bus, char **args); +static int daemon_reload(int argc, char *argv[], void* userdata); static int halt_now(enum action a); static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet); -static char** strv_skip_first(char **strv) { - if (strv_length(strv) > 0) - return strv + 1; - return NULL; +static bool original_stdout_is_tty; + +typedef enum BusFocus { + BUS_FULL, /* The full bus indicated via --system or --user */ + BUS_MANAGER, /* The manager itself, possibly directly, possibly via the bus */ + _BUS_FOCUS_MAX +} BusFocus; + +static sd_bus *busses[_BUS_FOCUS_MAX] = {}; + +static int acquire_bus(BusFocus focus, sd_bus **ret) { + int r; + + assert(focus < _BUS_FOCUS_MAX); + assert(ret); + + /* We only go directly to the manager, if we are using a local transport */ + if (arg_transport != BUS_TRANSPORT_LOCAL) + focus = BUS_FULL; + + if (!busses[focus]) { + bool user; + + user = arg_scope != UNIT_FILE_SYSTEM; + + if (focus == BUS_MANAGER) + r = bus_connect_transport_systemd(arg_transport, arg_host, user, &busses[focus]); + else + r = bus_connect_transport(arg_transport, arg_host, user, &busses[focus]); + if (r < 0) + return log_error_errno(r, "Failed to connect to bus: %m"); + + (void) sd_bus_set_allow_interactive_authorization(busses[focus], arg_ask_password); + } + + *ret = busses[focus]; + return 0; +} + +static void release_busses(void) { + BusFocus w; + + for (w = 0; w < _BUS_FOCUS_MAX; w++) + busses[w] = sd_bus_flush_close_unref(busses[w]); } static void pager_open_if_enabled(void) { @@ -230,42 +268,10 @@ static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) return EXIT_FAILURE; } -static void warn_wall(enum action a) { - static const char *table[_ACTION_MAX] = { - [ACTION_HALT] = "The system is going down for system halt NOW!", - [ACTION_REBOOT] = "The system is going down for reboot NOW!", - [ACTION_POWEROFF] = "The system is going down for power-off NOW!", - [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!", - [ACTION_RESCUE] = "The system is going down to rescue mode NOW!", - [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!", - [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!" - }; - - if (arg_no_wall) - return; - - if (arg_wall) { - _cleanup_free_ char *p; - - p = strv_join(arg_wall, " "); - if (!p) { - log_oom(); - return; - } - - if (*p) { - utmp_wall(p, NULL, NULL, NULL, NULL); - return; - } - } +static bool install_client_side(void) { - if (!table[a]) - return; - - utmp_wall(table[a], NULL, NULL, NULL, NULL); -} - -static bool avoid_bus(void) { + /* Decides when to execute enable/disable/... operations + * client-side rather than server-side. */ if (running_in_chroot() > 0) return true; @@ -652,15 +658,20 @@ static int get_unit_list_recursive( return c; } -static int list_units(sd_bus *bus, char **args) { +static int list_units(int argc, char *argv[], void *userdata) { _cleanup_free_ UnitInfo *unit_infos = NULL; _cleanup_(message_set_freep) Set *replies = NULL; _cleanup_strv_free_ char **machines = NULL; + sd_bus *bus; int r; pager_open_if_enabled(); - r = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); if (r < 0) return r; @@ -676,6 +687,10 @@ static int get_triggered_units( _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; + assert(bus); + assert(path); + assert(ret); + r = sd_bus_get_property_strv( bus, "org.freedesktop.systemd1", @@ -684,9 +699,8 @@ static int get_triggered_units( "Triggers", &error, ret); - if (r < 0) - log_error("Failed to determine triggers: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to determine triggers: %s", bus_error_message(&error, r)); return 0; } @@ -710,10 +724,8 @@ static int get_listening( &error, &reply, "a(ss)"); - if (r < 0) { - log_error("Failed to get list of listening sockets: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get list of listening sockets: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)"); if (r < 0) @@ -853,7 +865,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { return 0; } -static int list_sockets(sd_bus *bus, char **args) { +static int list_sockets(int argc, char *argv[], void *userdata) { _cleanup_(message_set_freep) Set *replies = NULL; _cleanup_strv_free_ char **machines = NULL; _cleanup_free_ UnitInfo *unit_infos = NULL; @@ -863,10 +875,15 @@ static int list_sockets(sd_bus *bus, char **args) { unsigned cs = 0; size_t size = 0; int r = 0, n; + sd_bus *bus; pager_open_if_enabled(); - n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); if (n < 0) return n; @@ -947,10 +964,8 @@ static int get_next_elapse( &error, 't', &t.monotonic); - if (r < 0) { - log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r)); r = sd_bus_get_property_trivial( bus, @@ -961,10 +976,8 @@ static int get_next_elapse( &error, 't', &t.realtime); - if (r < 0) { - log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r)); *next = t; return 0; @@ -991,10 +1004,8 @@ static int get_last_trigger( &error, 't', last); - if (r < 0) { - log_error("Failed to get last trigger time: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get last trigger time: %s", bus_error_message(&error, r)); return 0; } @@ -1160,7 +1171,7 @@ static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) { return next_elapse; } -static int list_timers(sd_bus *bus, char **args) { +static int list_timers(int argc, char *argv[], void *userdata) { _cleanup_(message_set_freep) Set *replies = NULL; _cleanup_strv_free_ char **machines = NULL; _cleanup_free_ struct timer_info *timer_infos = NULL; @@ -1170,11 +1181,16 @@ static int list_timers(sd_bus *bus, char **args) { size_t size = 0; int n, c = 0; dual_timestamp nw; + sd_bus *bus; int r = 0; pager_open_if_enabled(); - n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); if (n < 0) return n; @@ -1326,7 +1342,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { printf("\n%u unit files listed.\n", c); } -static int list_unit_files(sd_bus *bus, char **args) { +static int list_unit_files(int argc, char *argv[], void *userdata) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ UnitFileList *units = NULL; UnitFileList *unit; @@ -1338,7 +1354,7 @@ static int list_unit_files(sd_bus *bus, char **args) { pager_open_if_enabled(); - if (avoid_bus()) { + if (install_client_side()) { Hashmap *h; UnitFileList *u; Iterator i; @@ -1351,8 +1367,7 @@ static int list_unit_files(sd_bus *bus, char **args) { r = unit_file_get_list(arg_scope, arg_root, h); if (r < 0) { unit_file_list_free(h); - log_error_errno(r, "Failed to get unit file list: %m"); - return r; + return log_error_errno(r, "Failed to get unit file list: %m"); } n_units = hashmap_size(h); @@ -1364,7 +1379,7 @@ static int list_unit_files(sd_bus *bus, char **args) { } HASHMAP_FOREACH(u, h, i) { - if (!output_show_unit_file(u, strv_skip_first(args))) + if (!output_show_unit_file(u, strv_skip(argv, 1))) continue; units[c++] = *u; @@ -1375,6 +1390,11 @@ static int list_unit_files(sd_bus *bus, char **args) { hashmap_free(h); } else { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; r = sd_bus_call_method( bus, @@ -1385,10 +1405,8 @@ static int list_unit_files(sd_bus *bus, char **args) { &error, &reply, NULL); - if (r < 0) { - log_error("Failed to list unit files: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)"); if (r < 0) @@ -1404,7 +1422,7 @@ static int list_unit_files(sd_bus *bus, char **args) { unit_file_state_from_string(state) }; - if (output_show_unit_file(&units[c], strv_skip_first(args))) + if (output_show_unit_file(&units[c], strv_skip(argv, 1))) c ++; } @@ -1419,7 +1437,7 @@ static int list_unit_files(sd_bus *bus, char **args) { qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list); output_unit_file_list(units, c); - if (avoid_bus()) { + if (install_client_side()) { for (unit = units; unit < units + c; unit++) free(unit->path); } @@ -1511,10 +1529,8 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha &error, &reply, "s", "org.freedesktop.systemd1.Unit"); - if (r < 0) { - log_error("Failed to get properties of %s: %s", name, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); if (r < 0) @@ -1638,16 +1654,15 @@ static int list_dependencies_one( return 0; } -static int list_dependencies(sd_bus *bus, char **args) { +static int list_dependencies(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **units = NULL; _cleanup_free_ char *unit = NULL; const char *u; + sd_bus *bus; int r; - assert(bus); - - if (args[1]) { - r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &unit); + if (argv[1]) { + r = unit_name_mangle(argv[1], UNIT_NAME_NOGLOB, &unit); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); @@ -1657,6 +1672,10 @@ static int list_dependencies(sd_bus *bus, char **args) { pager_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + puts(u); return list_dependencies_one(bus, u, 0, &units, 0); @@ -1872,12 +1891,11 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n) printf("\n%u machines listed.\n", n); } -static int list_machines(sd_bus *bus, char **args) { +static int list_machines(int argc, char *argv[], void *userdata) { struct machine_info *machine_infos = NULL; + sd_bus *bus; int r; - assert(bus); - if (geteuid() != 0) { log_error("Must be root."); return -EPERM; @@ -1885,7 +1903,11 @@ static int list_machines(sd_bus *bus, char **args) { pager_open_if_enabled(); - r = get_machine_list(bus, &machine_infos, strv_skip_first(args)); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = get_machine_list(bus, &machine_infos, strv_skip(argv, 1)); if (r < 0) return r; @@ -1896,13 +1918,13 @@ static int list_machines(sd_bus *bus, char **args) { return 0; } -static int get_default(sd_bus *bus, char **args) { +static int get_default(int argc, char *argv[], void *userdata) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ char *_path = NULL; const char *path; int r; - if (!bus || avoid_bus()) { + if (install_client_side()) { r = unit_file_get_default(arg_scope, arg_root, &_path); if (r < 0) return log_error_errno(r, "Failed to get default target: %m"); @@ -1910,6 +1932,11 @@ static int get_default(sd_bus *bus, char **args) { } else { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; r = sd_bus_call_method( bus, @@ -1920,10 +1947,8 @@ static int get_default(sd_bus *bus, char **args) { &error, &reply, NULL); - if (r < 0) { - log_error("Failed to get default target: %s", bus_error_message(&error, -r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get default target: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &path); if (r < 0) @@ -1949,17 +1974,21 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha } } -static int set_default(sd_bus *bus, char **args) { +static int set_default(int argc, char *argv[], void *userdata) { _cleanup_free_ char *unit = NULL; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; int r; - r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &unit); + assert(argc >= 2); + assert(argv); + + r = unit_name_mangle_with_suffix(argv[1], UNIT_NAME_NOGLOB, ".target", &unit); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); - if (!bus || avoid_bus()) { + if (install_client_side()) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); if (r < 0) return log_error_errno(r, "Failed to set default target: %m"); @@ -1967,13 +1996,19 @@ static int set_default(sd_bus *bus, char **args) { if (!arg_quiet) dump_unit_file_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); r = 0; } else { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus; polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -1983,10 +2018,8 @@ static int set_default(sd_bus *bus, char **args) { &error, &reply, "sb", unit, 1); - if (r < 0) { - log_error("Failed to set default target: %s", bus_error_message(&error, -r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set default target: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) @@ -1994,13 +2027,11 @@ static int set_default(sd_bus *bus, char **args) { /* Try to reload if enabled */ if (!arg_no_reload) - r = daemon_reload(bus, args); + r = daemon_reload(argc, argv, userdata); else r = 0; } - unit_file_changes_free(changes, n_changes); - return r; } @@ -2085,17 +2116,24 @@ static bool output_show_job(struct job_info *job, char **patterns) { return strv_fnmatch_or_empty(patterns, job->name, FNM_NOESCAPE); } -static int list_jobs(sd_bus *bus, char **args) { +static int list_jobs(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; const char *name, *type, *state, *job_path, *unit_path; _cleanup_free_ struct job_info *jobs = NULL; size_t size = 0; unsigned c = 0; + sd_bus *bus; uint32_t id; int r; bool skipped = false; + pager_open_if_enabled(); + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -2105,10 +2143,8 @@ static int list_jobs(sd_bus *bus, char **args) { &error, &reply, NULL); - if (r < 0) { - log_error("Failed to list jobs: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to list jobs: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, 'a', "(usssoo)"); if (r < 0) @@ -2117,7 +2153,7 @@ static int list_jobs(sd_bus *bus, char **args) { while ((r = sd_bus_message_read(reply, "(usssoo)", &id, &name, &type, &state, &job_path, &unit_path)) > 0) { struct job_info job = { id, name, type, state }; - if (!output_show_job(&job, strv_skip_first(args))) { + if (!output_show_job(&job, strv_skip(argv, 1))) { skipped = true; continue; } @@ -2138,18 +2174,21 @@ static int list_jobs(sd_bus *bus, char **args) { return r; } -static int cancel_job(sd_bus *bus, char **args) { +static int cancel_job(int argc, char *argv[], void *userdata) { + sd_bus *bus; char **name; int r = 0; - assert(args); - - if (strv_length(args) <= 1) - return daemon_reload(bus, args); + if (argc <= 1) + return daemon_reload(argc, argv, userdata); polkit_agent_open_if_enabled(); - STRV_FOREACH(name, args+1) { + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + STRV_FOREACH(name, strv_skip(argv, 1)) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; uint32_t id; int q; @@ -2168,7 +2207,7 @@ static int cancel_job(sd_bus *bus, char **args) { NULL, "u", id); if (q < 0) { - log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q)); + log_error_errno(q, "Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q)); if (r == 0) r = q; } @@ -2253,7 +2292,6 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un static int unit_find_paths( sd_bus *bus, const char *unit_name, - bool avoid_bus_cache, LookupPaths *lp, char **fragment_path, char ***dropin_paths) { @@ -2274,7 +2312,7 @@ static int unit_find_paths( assert(fragment_path); assert(lp); - if (!avoid_bus_cache && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { + if (!install_client_side() && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *unit_load_error = NULL; _cleanup_free_ char *unit = NULL; @@ -2471,10 +2509,8 @@ static int check_triggering_units( "LoadState", &error, &state); - if (r < 0) { - log_error("Failed to get load state of %s: %s", n, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get load state of %s: %s", n, bus_error_message(&error, r)); if (streq(state, "masked")) return 0; @@ -2487,10 +2523,8 @@ static int check_triggering_units( "TriggeredBy", &error, &triggered_by); - if (r < 0) { - log_error("Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r)); STRV_FOREACH(i, triggered_by) { r = check_one_unit(bus, *i, "active\0reloading\0", true); @@ -2586,8 +2620,7 @@ static int start_unit_one( verb = method_to_verb(method); - log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r)); - return r; + return log_error_errno(r, "Failed to %s %s: %s", verb, name, bus_error_message(error, r)); } r = sd_bus_message_read(reply, "o", &path); @@ -2608,11 +2641,13 @@ static int start_unit_one( } static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) { - _cleanup_strv_free_ char **mangled = NULL, **globs = NULL; char **name; int r, i; + assert(bus); + assert(ret); + STRV_FOREACH(name, names) { char *t; @@ -2637,9 +2672,6 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ UnitInfo *unit_infos = NULL; - if (!bus) - return log_error_errno(EOPNOTSUPP, "Unit name globbing without bus is not implemented."); - r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply); if (r < 0) return r; @@ -2687,24 +2719,28 @@ static enum action verb_to_action(const char *verb) { return _ACTION_INVALID; } -static int start_unit(sd_bus *bus, char **args) { +static int start_unit(int argc, char *argv[], void *userdata) { _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; const char *method, *mode, *one_name, *suffix = NULL; _cleanup_strv_free_ char **names = NULL; + sd_bus *bus; char **name; int r = 0; - assert(bus); - ask_password_agent_open_if_enabled(); polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + if (arg_action == ACTION_SYSTEMCTL) { enum action action; - method = verb_to_method(args[0]); - action = verb_to_action(args[0]); - if (streq(args[0], "isolate")) { + method = verb_to_method(argv[0]); + action = verb_to_action(argv[0]); + + if (streq(argv[0], "isolate")) { mode = "isolate"; suffix = ".target"; } else @@ -2724,9 +2760,9 @@ static int start_unit(sd_bus *bus, char **args) { if (one_name) names = strv_new(one_name, NULL); else { - r = expand_names(bus, args + 1, suffix, &names); + r = expand_names(bus, strv_skip(argv, 1), suffix, &names); if (r < 0) - log_error_errno(r, "Failed to expand names: %m"); + return log_error_errno(r, "Failed to expand names: %m"); } if (!arg_no_block) { @@ -2761,18 +2797,55 @@ static int start_unit(sd_bus *bus, char **args) { return r; } +static int logind_set_wall_message(void) { +#ifdef HAVE_LOGIND + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + _cleanup_free_ char *m = NULL; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + m = strv_join(arg_wall, " "); + if (!m) + return log_oom(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); + + if (r < 0) + return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); + +#endif + return 0; +} + /* Ask systemd-logind, which might grant access to unprivileged users * through PolicyKit */ -static int reboot_with_logind(sd_bus *bus, enum action a) { +static int logind_reboot(enum action a) { #ifdef HAVE_LOGIND _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *method, *description; + sd_bus *bus; int r; - if (!bus) - return -EIO; - polkit_agent_open_if_enabled(); + (void) logind_set_wall_message(); + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; switch (a) { @@ -2805,33 +2878,6 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { return -EINVAL; } - if (!strv_isempty(arg_wall)) { - _cleanup_free_ char *m; - - m = strv_join(arg_wall, " "); - if (!m) - return log_oom(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetWallMessage", - &error, - NULL, - "sb", - m, - !arg_no_wall); - - if (r < 0) { - log_warning_errno(r, "Failed to set wall message, ignoring: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } - } - - r = sd_bus_call_method( bus, "org.freedesktop.login1", @@ -2842,27 +2888,25 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { NULL, "b", arg_ask_password); if (r < 0) - log_error("Failed to %s via logind: %s", description, bus_error_message(&error, r)); + return log_error_errno(r, "Failed to %s via logind: %s", description, bus_error_message(&error, r)); - return r; + return 0; #else return -ENOSYS; #endif } -static int check_inhibitors(sd_bus *bus, enum action a) { +static int logind_check_inhibitors(enum action a) { #ifdef HAVE_LOGIND _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_strv_free_ char **sessions = NULL; const char *what, *who, *why, *mode; uint32_t uid, pid; + sd_bus *bus; unsigned c = 0; char **s; int r; - if (!bus) - return 0; - if (arg_ignore_inhibitors || arg_force > 0) return 0; @@ -2875,6 +2919,10 @@ static int check_inhibitors(sd_bus *bus, enum action a) { if (!on_tty()) return 0; + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.login1", @@ -2964,10 +3012,36 @@ static int check_inhibitors(sd_bus *bus, enum action a) { #endif } -static int prepare_firmware_setup(sd_bus *bus) { +static int logind_prepare_firmware_setup(void) { #ifdef HAVE_LOGIND _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToFirmwareSetup", + &error, + NULL, + "b", true); + if (r < 0) + return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r)); + + return 0; +#else + log_error("Cannot remotely indicate to EFI to boot into setup mode."); + return -ENOSYS; #endif +} + +static int prepare_firmware_setup(void) { int r; if (!arg_firmware_setup) @@ -2982,38 +3056,42 @@ static int prepare_firmware_setup(sd_bus *bus) { return r; } -#ifdef HAVE_LOGIND + return logind_prepare_firmware_setup(); +} + +static int set_exit_code(uint8_t code) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_call_method( bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetRebootToFirmwareSetup", + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetExitCode", &error, NULL, - "b", true); - if (r < 0) { - log_error("Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r)); - return r; - } + "y", code); + if (r < 0) + return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); return 0; -#else - log_error("Cannot remotely indicate to EFI to boot into setup mode."); - return -EINVAL; -#endif } -static int start_special(sd_bus *bus, char **args) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; +static int start_special(int argc, char *argv[], void *userdata) { enum action a; int r; - assert(args); + assert(argv); - a = verb_to_action(args[0]); + a = verb_to_action(argv[0]); - r = check_inhibitors(bus, a); + r = logind_check_inhibitors(a); if (r < 0) return r; @@ -3022,39 +3100,29 @@ static int start_special(sd_bus *bus, char **args) { return -EPERM; } - r = prepare_firmware_setup(bus); + r = prepare_firmware_setup(); if (r < 0) return r; - if (a == ACTION_REBOOT && args[1]) { - r = update_reboot_param_file(args[1]); + if (a == ACTION_REBOOT && argc > 1) { + r = update_reboot_param_file(argv[1]); if (r < 0) return r; - } else if (a == ACTION_EXIT && strv_length(args) > 1) { - /* If the exit code is not given on the command line, don't - * reset it to zero: just keep it as it might have been set - * previously. */ + + } else if (a == ACTION_EXIT && argc > 1) { uint8_t code = 0; - r = safe_atou8(args[1], &code); - if (r < 0) { - log_error("Invalid exit code."); - return -EINVAL; - } + /* If the exit code is not given on the command line, + * don't reset it to zero: just keep it as it might + * have been set previously. */ - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetExitCode", - &error, - NULL, - "y", code); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + r = safe_atou8(argv[1], &code); + if (r < 0) + return log_error_errno(r, "Invalid exit code."); + + r = set_exit_code(code); + if (r < 0) return r; - } } if (arg_force >= 2 && @@ -3071,39 +3139,37 @@ static int start_special(sd_bus *bus, char **args) { ACTION_REBOOT, ACTION_KEXEC, ACTION_EXIT)) - return daemon_reload(bus, args); + return daemon_reload(argc, argv, userdata); - /* first try logind, to allow authentication with polkit */ - if (geteuid() != 0 && - IN_SET(a, + /* First try logind, to allow authentication with polkit */ + if (IN_SET(a, ACTION_POWEROFF, ACTION_REBOOT, ACTION_SUSPEND, ACTION_HIBERNATE, ACTION_HYBRID_SLEEP)) { - r = reboot_with_logind(bus, a); + r = logind_reboot(a); if (r >= 0) return r; if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) /* requested operation is not supported or already in progress */ return r; - /* on all other errors, try low-level operation */ - } - r = start_unit(bus, args); - if (r == EXIT_SUCCESS) - warn_wall(a); + /* On all other errors, try low-level operation */ + } - return r; + return start_unit(argc, argv, userdata); } -static int check_unit_generic(sd_bus *bus, int code, const char *good_states, char **args) { +static int check_unit_generic(int code, const char *good_states, char **args) { _cleanup_strv_free_ char **names = NULL; + sd_bus *bus; char **name; int r; - assert(bus); - assert(args); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; r = expand_names(bus, args, NULL, &names); if (r < 0) @@ -3122,25 +3188,27 @@ static int check_unit_generic(sd_bus *bus, int code, const char *good_states, ch return r; } -static int check_unit_active(sd_bus *bus, char **args) { +static int check_unit_active(int argc, char *argv[], void *userdata) { /* According to LSB: 3, "program is not running" */ - return check_unit_generic(bus, 3, "active\0reloading\0", args + 1); + return check_unit_generic(3, "active\0reloading\0", strv_skip(argv, 1)); } -static int check_unit_failed(sd_bus *bus, char **args) { - return check_unit_generic(bus, 1, "failed\0", args + 1); +static int check_unit_failed(int argc, char *argv[], void *userdata) { + return check_unit_generic(1, "failed\0", strv_skip(argv, 1)); } -static int kill_unit(sd_bus *bus, char **args) { +static int kill_unit(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; char *kill_who = NULL, **name; + sd_bus *bus; int r, q; - assert(bus); - assert(args); - polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + if (!arg_kill_who) arg_kill_who = "all"; @@ -3148,9 +3216,9 @@ static int kill_unit(sd_bus *bus, char **args) { if (streq(arg_job_mode, "fail")) kill_who = strjoina(arg_kill_who, "-fail", NULL); - r = expand_names(bus, args + 1, NULL, &names); + r = expand_names(bus, strv_skip(argv, 1), NULL, &names); if (r < 0) - log_error_errno(r, "Failed to expand names: %m"); + return log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; @@ -3165,7 +3233,7 @@ static int kill_unit(sd_bus *bus, char **args) { NULL, "ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal); if (q < 0) { - log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q)); + log_error_errno(q, "Failed to kill unit %s: %s", *names, bus_error_message(&error, q)); if (r == 0) r = q; } @@ -3861,13 +3929,13 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * info->name = strdup(name); if (!info->name) - log_oom(); + return log_oom(); LIST_PREPEND(exec, i->exec, info); info = new0(ExecStatusInfo, 1); if (!info) - log_oom(); + return log_oom(); } if (r < 0) @@ -4439,10 +4507,8 @@ static int get_unit_dbus_path_by_pid( &error, &reply, "u", pid); - if (r < 0) { - log_error("Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r)); r = sd_bus_message_read(reply, "o", &u); if (r < 0) @@ -4551,16 +4617,22 @@ static int show_system_status(sd_bus *bus) { return 0; } -static int show(sd_bus *bus, char **args) { - bool show_properties, show_status, new_line = false; +static int show(int argc, char *argv[], void *userdata) { + bool show_properties, show_status, show_help, new_line = false; bool ellipsized = false; int r, ret = 0; + sd_bus *bus; - assert(bus); - assert(args); + assert(argv); - show_properties = streq(args[0], "show"); - show_status = streq(args[0], "status"); + show_properties = streq(argv[0], "show"); + show_status = streq(argv[0], "status"); + show_help = streq(argv[0], "help"); + + if (show_help && argc <= 1) { + log_error("This command expects one or more unit names. Did you mean --help?"); + return -EINVAL; + } if (show_properties) pager_open_if_enabled(); @@ -4571,24 +4643,27 @@ static int show(sd_bus *bus, char **args) { * be split up into many files. */ setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); - /* If no argument is specified inspect the manager itself */ + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; - if (show_properties && strv_length(args) <= 1) - return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized); + /* If no argument is specified inspect the manager itself */ + if (show_properties && argc <= 1) + return show_one(argv[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized); - if (show_status && strv_length(args) <= 1) { + if (show_status && argc <= 1) { pager_open_if_enabled(); show_system_status(bus); new_line = true; if (arg_all) - ret = show_all(args[0], bus, false, &new_line, &ellipsized); + ret = show_all(argv[0], bus, false, &new_line, &ellipsized); } else { _cleanup_free_ char **patterns = NULL; char **name; - STRV_FOREACH(name, args + 1) { + STRV_FOREACH(name, strv_skip(argv, 1)) { _cleanup_free_ char *unit = NULL; uint32_t id; @@ -4611,8 +4686,7 @@ static int show(sd_bus *bus, char **args) { } } - r = show_one(args[0], bus, unit, show_properties, - &new_line, &ellipsized); + r = show_one(argv[0], bus, unit, show_properties, &new_line, &ellipsized); if (r < 0) return r; else if (r > 0 && ret == 0) @@ -4624,7 +4698,7 @@ static int show(sd_bus *bus, char **args) { r = expand_names(bus, patterns, NULL, &names); if (r < 0) - log_error_errno(r, "Failed to expand names: %m"); + return log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_free_ char *unit; @@ -4633,8 +4707,7 @@ static int show(sd_bus *bus, char **args) { if (!unit) return log_oom(); - r = show_one(args[0], bus, unit, show_properties, - &new_line, &ellipsized); + r = show_one(argv[0], bus, unit, show_properties, &new_line, &ellipsized); if (r < 0) return r; else if (r > 0 && ret == 0) @@ -4694,19 +4767,18 @@ static int cat_file(const char *filename, bool newline) { return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false); } -static int cat(sd_bus *bus, char **args) { +static int cat(int argc, char *argv[], void *userdata) { _cleanup_free_ char *user_home = NULL; _cleanup_free_ char *user_runtime = NULL; _cleanup_lookup_paths_free_ LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; char **name; - bool first = true, avoid_bus_cache; + sd_bus *bus; + bool first = true; int r; - assert(args); - if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot remotely cat units"); + log_error("Cannot remotely cat units."); return -EINVAL; } @@ -4714,11 +4786,13 @@ static int cat(sd_bus *bus, char **args) { if (r < 0) return r; - r = expand_names(bus, args + 1, NULL, &names); + r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) - return log_error_errno(r, "Failed to expand names: %m"); + return r; - avoid_bus_cache = !bus || avoid_bus(); + r = expand_names(bus, strv_skip(argv, 1), NULL, &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); pager_open_if_enabled(); @@ -4727,7 +4801,7 @@ static int cat(sd_bus *bus, char **args) { _cleanup_strv_free_ char **dropin_paths = NULL; char **path; - r = unit_find_paths(bus, *name, avoid_bus_cache, &lp, &fragment_path, &dropin_paths); + r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths); if (r < 0) return r; else if (r == 0) @@ -4754,15 +4828,20 @@ static int cat(sd_bus *bus, char **args) { return 0; } -static int set_property(sd_bus *bus, char **args) { +static int set_property(int argc, char *argv[], void *userdata) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *n = NULL; + sd_bus *bus; char **i; int r; polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_message_new_method_call( bus, &m, @@ -4773,7 +4852,7 @@ static int set_property(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - r = unit_name_mangle(args[1], UNIT_NAME_NOGLOB, &n); + r = unit_name_mangle(argv[1], UNIT_NAME_NOGLOB, &n); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); @@ -4785,7 +4864,7 @@ static int set_property(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - STRV_FOREACH(i, args + 2) { + STRV_FOREACH(i, strv_skip(argv, 2)) { r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); if (r < 0) return bus_log_create_error(r); @@ -4804,25 +4883,24 @@ static int set_property(sd_bus *bus, char **args) { return bus_log_create_error(r); r = sd_bus_call(bus, m, 0, &error, NULL); - if (r < 0) { - log_error("Failed to set unit properties on %s: %s", n, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set unit properties on %s: %s", n, bus_error_message(&error, r)); return 0; } -static int snapshot(sd_bus *bus, char **args) { +static int snapshot(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ char *n = NULL, *id = NULL; const char *path; + sd_bus *bus; int r; polkit_agent_open_if_enabled(); - if (strv_length(args) > 1) { - r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".snapshot", &n); + if (argc > 1) { + r = unit_name_mangle_with_suffix(argv[1], UNIT_NAME_NOGLOB, ".snapshot", &n); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); } else { @@ -4831,6 +4909,10 @@ static int snapshot(sd_bus *bus, char **args) { return log_oom(); } + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -4840,10 +4922,8 @@ static int snapshot(sd_bus *bus, char **args) { &error, &reply, "sb", n, false); - if (r < 0) { - log_error("Failed to create snapshot: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create snapshot: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "o", &path); if (r < 0) @@ -4857,10 +4937,8 @@ static int snapshot(sd_bus *bus, char **args) { "Id", &error, &id); - if (r < 0) { - log_error("Failed to get ID of snapshot: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get ID of snapshot: %s", bus_error_message(&error, r)); if (!arg_quiet) puts(id); @@ -4868,18 +4946,21 @@ static int snapshot(sd_bus *bus, char **args) { return 0; } -static int delete_snapshot(sd_bus *bus, char **args) { +static int delete_snapshot(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; + sd_bus *bus; char **name; int r; - assert(args); - polkit_agent_open_if_enabled(); - r = expand_names(bus, args + 1, ".snapshot", &names); + r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) - log_error_errno(r, "Failed to expand names: %m"); + return r; + + r = expand_names(bus, strv_skip(argv, 1), ".snapshot", &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; @@ -4895,7 +4976,7 @@ static int delete_snapshot(sd_bus *bus, char **args) { NULL, "s", *name); if (q < 0) { - log_error("Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q)); + log_error_errno(q, "Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q)); if (r == 0) r = q; } @@ -4904,13 +4985,18 @@ static int delete_snapshot(sd_bus *bus, char **args) { return r; } -static int daemon_reload(sd_bus *bus, char **args) { +static int daemon_reload(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *method; + sd_bus *bus; int r; polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + if (arg_action == ACTION_RELOAD) method = "Reload"; else if (arg_action == ACTION_REEXEC) @@ -4919,15 +5005,15 @@ static int daemon_reload(sd_bus *bus, char **args) { assert(arg_action == ACTION_SYSTEMCTL); method = - streq(args[0], "clear-jobs") || - streq(args[0], "cancel") ? "ClearJobs" : - streq(args[0], "daemon-reexec") ? "Reexecute" : - streq(args[0], "reset-failed") ? "ResetFailed" : - streq(args[0], "halt") ? "Halt" : - streq(args[0], "poweroff") ? "PowerOff" : - streq(args[0], "reboot") ? "Reboot" : - streq(args[0], "kexec") ? "KExec" : - streq(args[0], "exit") ? "Exit" : + streq(argv[0], "clear-jobs") || + streq(argv[0], "cancel") ? "ClearJobs" : + streq(argv[0], "daemon-reexec") ? "Reexecute" : + streq(argv[0], "reset-failed") ? "ResetFailed" : + streq(argv[0], "halt") ? "Halt" : + streq(argv[0], "poweroff") ? "PowerOff" : + streq(argv[0], "reboot") ? "Reboot" : + streq(argv[0], "kexec") ? "KExec" : + streq(argv[0], "exit") ? "Exit" : /* "daemon-reload" */ "Reload"; } @@ -4949,24 +5035,29 @@ static int daemon_reload(sd_bus *bus, char **args) { * reply */ r = 0; else if (r < 0) - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); return r < 0 ? r : 0; } -static int reset_failed(sd_bus *bus, char **args) { +static int reset_failed(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; + sd_bus *bus; char **name; int r, q; - if (strv_length(args) <= 1) - return daemon_reload(bus, args); + if (argc <= 1) + return daemon_reload(argc, argv, userdata); polkit_agent_open_if_enabled(); - r = expand_names(bus, args + 1, NULL, &names); + r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) - log_error_errno(r, "Failed to expand names: %m"); + return r; + + r = expand_names(bus, strv_skip(argv, 1), NULL, &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; @@ -4981,7 +5072,7 @@ static int reset_failed(sd_bus *bus, char **args) { NULL, "s", *name); if (q < 0) { - log_error("Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q)); + log_error_errno(q, "Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q)); if (r == 0) r = q; } @@ -4990,14 +5081,19 @@ static int reset_failed(sd_bus *bus, char **args) { return r; } -static int show_environment(sd_bus *bus, char **args) { +static int show_environment(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; const char *text; + sd_bus *bus; int r; pager_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_get_property( bus, "org.freedesktop.systemd1", @@ -5007,10 +5103,8 @@ static int show_environment(sd_bus *bus, char **args) { &error, &reply, "as"); - if (r < 0) { - log_error("Failed to get environment: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get environment: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s"); if (r < 0) @@ -5028,23 +5122,27 @@ static int show_environment(sd_bus *bus, char **args) { return 0; } -static int switch_root(sd_bus *bus, char **args) { +static int switch_root(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *cmdline_init = NULL; const char *root, *init; - unsigned l; + sd_bus *bus; int r; - l = strv_length(args); - if (l < 2 || l > 3) { + if (arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Cannot switch root remotely."); + return -EINVAL; + } + + if (argc < 2 || argc > 3) { log_error("Wrong number of arguments."); return -EINVAL; } - root = args[1]; + root = argv[1]; - if (l >= 3) - init = args[2]; + if (argc >= 3) + init = argv[2]; else { r = parse_env_file("/proc/cmdline", WHITESPACE, "init", &cmdline_init, @@ -5070,6 +5168,10 @@ static int switch_root(sd_bus *bus, char **args) { init = NULL; } + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + log_debug("Switching root - root: %s; init: %s", root, strna(init)); r = sd_bus_call_method( @@ -5081,26 +5183,29 @@ static int switch_root(sd_bus *bus, char **args) { &error, NULL, "ss", root, init); - if (r < 0) { - log_error("Failed to switch root: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r)); return 0; } -static int set_environment(sd_bus *bus, char **args) { +static int set_environment(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL; const char *method; + sd_bus *bus; int r; - assert(bus); - assert(args); + assert(argc > 1); + assert(argv); polkit_agent_open_if_enabled(); - method = streq(args[0], "set-environment") + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + method = streq(argv[0], "set-environment") ? "SetEnvironment" : "UnsetEnvironment"; @@ -5114,29 +5219,29 @@ static int set_environment(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, args + 1); + r = sd_bus_message_append_strv(m, strv_skip(argv, 1)); if (r < 0) return bus_log_create_error(r); r = sd_bus_call(bus, m, 0, &error, NULL); - if (r < 0) { - log_error("Failed to set environment: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set environment: %s", bus_error_message(&error, r)); return 0; } -static int import_environment(sd_bus *bus, char **args) { +static int import_environment(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + sd_bus *bus; int r; - assert(bus); - assert(args); - polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_message_new_method_call( bus, &m, @@ -5147,7 +5252,7 @@ static int import_environment(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - if (strv_isempty(args + 1)) + if (argc < 2) r = sd_bus_message_append_strv(m, environ); else { char **a, **b; @@ -5156,7 +5261,7 @@ static int import_environment(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - STRV_FOREACH(a, args + 1) { + STRV_FOREACH(a, strv_skip(argv, 1)) { if (!env_name_is_valid(*a)) { log_error("Not a valid environment variable name: %s", *a); @@ -5184,10 +5289,8 @@ static int import_environment(sd_bus *bus, char **args) { return bus_log_create_error(r); r = sd_bus_call(bus, m, 0, &error, NULL); - if (r < 0) { - log_error("Failed to import environment: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to import environment: %s", bus_error_message(&error, r)); return 0; } @@ -5290,13 +5393,13 @@ static int enable_sysv_units(const char *verb, char **args) { (void) reset_signal_mask(); execv(argv[0], (char**) argv); - log_error("Failed to execute %s: %m", argv[0]); + log_error_errno(r, "Failed to execute %s: %m", argv[0]); _exit(EXIT_FAILURE); } j = wait_for_terminate(pid, &status); if (j < 0) { - log_error_errno(r, "Failed to wait for child: %m"); + log_error_errno(j, "Failed to wait for child: %m"); return j; } @@ -5366,18 +5469,18 @@ static int mangle_names(char **original_names, char ***mangled_names) { return 0; } -static int enable_unit(sd_bus *bus, char **args) { +static int enable_unit(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; - const char *verb = args[0]; + const char *verb = argv[0]; UnitFileChange *changes = NULL; unsigned n_changes = 0; int carries_install_info = -1; int r; - if (!args[1]) + if (!argv[1]) return 0; - r = mangle_names(args+1, &names); + r = mangle_names(strv_skip(argv, 1), &names); if (r < 0) return r; @@ -5390,7 +5493,7 @@ static int enable_unit(sd_bus *bus, char **args) { if (strv_isempty(names)) return 0; - if (!bus || avoid_bus()) { + if (install_client_side()) { if (streq(verb, "enable")) { r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); carries_install_info = r; @@ -5426,9 +5529,14 @@ static int enable_unit(sd_bus *bus, char **args) { int expect_carries_install_info = false; bool send_force = true, send_preset_mode = false; const char *method; + sd_bus *bus; polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + if (streq(verb, "enable")) { method = "EnableUnitFiles"; expect_carries_install_info = true; @@ -5488,10 +5596,8 @@ static int enable_unit(sd_bus *bus, char **args) { } r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); if (expect_carries_install_info) { r = sd_bus_message_read(reply, "b", &carries_install_info); @@ -5505,7 +5611,7 @@ static int enable_unit(sd_bus *bus, char **args) { /* Try to reload if enabled */ if (!arg_no_reload) - r = daemon_reload(bus, args); + r = daemon_reload(argc, argv, userdata); else r = 0; } @@ -5521,16 +5627,21 @@ static int enable_unit(sd_bus *bus, char **args) { "3) A unit may be started when needed via activation (socket, path, timer,\n" " D-Bus, udev, scripted systemctl call, ...).\n"); - if (arg_now && n_changes > 0 && STR_IN_SET(args[0], "enable", "disable", "mask")) { + if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) { char *new_args[n_changes + 2]; + sd_bus *bus; unsigned i; - new_args[0] = streq(args[0], "enable") ? (char *)"start" : (char *)"stop"; + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop"); for (i = 0; i < n_changes; i++) new_args[i + 1] = basename(changes[i].path); new_args[i + 1] = NULL; - r = start_unit(bus, new_args); + r = start_unit(strv_length(new_args), new_args, userdata); } finish: @@ -5539,21 +5650,21 @@ finish: return r; } -static int add_dependency(sd_bus *bus, char **args) { +static int add_dependency(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; _cleanup_free_ char *target = NULL; - const char *verb = args[0]; + const char *verb = argv[0]; UnitDependency dep; int r = 0; - if (!args[1]) + if (!argv[1]) return 0; - r = unit_name_mangle_with_suffix(args[1], UNIT_NAME_NOGLOB, ".target", &target); + r = unit_name_mangle_with_suffix(argv[1], UNIT_NAME_NOGLOB, ".target", &target); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); - r = mangle_names(args+2, &names); + r = mangle_names(strv_skip(argv, 2), &names); if (r < 0) return r; @@ -5564,7 +5675,7 @@ static int add_dependency(sd_bus *bus, char **args) { else assert_not_reached("Unknown verb"); - if (!bus || avoid_bus()) { + if (install_client_side()) { UnitFileChange *changes = NULL; unsigned n_changes = 0; @@ -5581,9 +5692,14 @@ static int add_dependency(sd_bus *bus, char **args) { } else { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_message_new_method_call( bus, &m, @@ -5603,17 +5719,15 @@ static int add_dependency(sd_bus *bus, char **args) { return bus_log_create_error(r); r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) return r; if (!arg_no_reload) - r = daemon_reload(bus, args); + r = daemon_reload(argc, argv, userdata); else r = 0; } @@ -5621,12 +5735,12 @@ static int add_dependency(sd_bus *bus, char **args) { return r; } -static int preset_all(sd_bus *bus, char **args) { +static int preset_all(int argc, char *argv[], void *userdata) { UnitFileChange *changes = NULL; unsigned n_changes = 0; int r; - if (!bus || avoid_bus()) { + if (install_client_side()) { r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes); if (r < 0) { @@ -5642,9 +5756,14 @@ static int preset_all(sd_bus *bus, char **args) { } else { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + sd_bus *bus; polkit_agent_open_if_enabled(); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -5657,17 +5776,15 @@ static int preset_all(sd_bus *bus, char **args) { unit_file_preset_mode_to_string(arg_preset_mode), arg_runtime, arg_force); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) return r; if (!arg_no_reload) - r = daemon_reload(bus, args); + r = daemon_reload(argc, argv, userdata); else r = 0; } @@ -5678,25 +5795,24 @@ finish: return r; } -static int unit_is_enabled(sd_bus *bus, char **args) { +static int unit_is_enabled(int argc, char *argv[], void *userdata) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_strv_free_ char **names = NULL; bool enabled; char **name; int r; - r = mangle_names(args+1, &names); + r = mangle_names(strv_skip(argv, 1), &names); if (r < 0) return r; - r = enable_sysv_units(args[0], names); + r = enable_sysv_units(argv[0], names); if (r < 0) return r; enabled = r > 0; - if (!bus || avoid_bus()) { + if (install_client_side()) { STRV_FOREACH(name, names) { UnitFileState state; @@ -5717,6 +5833,13 @@ static int unit_is_enabled(sd_bus *bus, char **args) { } } else { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + STRV_FOREACH(name, names) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; const char *s; @@ -5730,10 +5853,8 @@ static int unit_is_enabled(sd_bus *bus, char **args) { &error, &reply, "s", *name); - if (r < 0) { - log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &s); if (r < 0) @@ -5750,10 +5871,21 @@ static int unit_is_enabled(sd_bus *bus, char **args) { return !enabled; } -static int is_system_running(sd_bus *bus, char **args) { +static int is_system_running(int argc, char *argv[], void *userdata) { _cleanup_free_ char *state = NULL; + sd_bus *bus; int r; + if (arg_transport == BUS_TRANSPORT_LOCAL && !sd_booted()) { + if (!arg_quiet) + puts("offline"); + return EXIT_FAILURE; + } + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + r = sd_bus_get_property_string( bus, "org.freedesktop.systemd1", @@ -5788,9 +5920,8 @@ static int create_edit_temp_file(const char *new_path, const char *original_path r = mkdir_parents(new_path, 0755); if (r < 0) { - log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path); free(t); - return r; + return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path); } r = copy_file(original_path, t, 0, 0644, 0); @@ -5845,12 +5976,16 @@ static int get_file_to_edit(const char *name, const char *user_home, const char return log_oom(); if (arg_runtime) { - if (access(path, F_OK) >= 0) - return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", - run, path); - if (path2 && access(path2, F_OK) >= 0) - return log_error_errno(EEXIST, "Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", - run, path2); + if (access(path, F_OK) >= 0) { + log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path); + return -EEXIST; + } + + if (path2 && access(path2, F_OK) >= 0) { + log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path2); + return -EEXIST; + } + *ret_path = run; run = NULL; } else { @@ -5943,10 +6078,8 @@ static int run_editor(char **paths) { assert(paths); pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return -errno; - } + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); if (pid == 0) { const char **args; @@ -6004,7 +6137,7 @@ static int run_editor(char **paths) { * failing. */ if (errno != ENOENT) { - log_error("Failed to execute %s: %m", editor); + log_error_errno(errno, "Failed to execute %s: %m", editor); _exit(EXIT_FAILURE); } } @@ -6024,7 +6157,6 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { _cleanup_free_ char *user_home = NULL; _cleanup_free_ char *user_runtime = NULL; _cleanup_lookup_paths_free_ LookupPaths lp = {}; - bool avoid_bus_cache; char **name; int r; @@ -6035,13 +6167,11 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { if (r < 0) return r; - avoid_bus_cache = !bus || avoid_bus(); - STRV_FOREACH(name, names) { _cleanup_free_ char *path = NULL; char *new_path, *tmp_path; - r = unit_find_paths(bus, *name, avoid_bus_cache, &lp, &path, NULL); + r = unit_find_paths(bus, *name, &lp, &path, NULL); if (r < 0) return r; else if (r == 0) @@ -6067,25 +6197,28 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { return 0; } -static int edit(sd_bus *bus, char **args) { +static int edit(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; _cleanup_strv_free_ char **paths = NULL; char **original, **tmp; + sd_bus *bus; int r; - assert(args); - if (!on_tty()) { - log_error("Cannot edit units if not on a tty"); + log_error("Cannot edit units if not on a tty."); return -EINVAL; } if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot remotely edit units"); + log_error("Cannot edit units remotely."); return -EINVAL; } - r = expand_names(bus, args + 1, NULL, &names); + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = expand_names(bus, strv_skip(argv, 1), NULL, &names); if (r < 0) return log_error_errno(r, "Failed to expand names: %m"); @@ -6115,8 +6248,8 @@ static int edit(sd_bus *bus, char **args) { } } - if (!arg_no_reload && bus && !avoid_bus()) - r = daemon_reload(bus, args); + if (!arg_no_reload && bus && !install_client_side()) + r = daemon_reload(argc, argv, userdata); end: STRV_FOREACH_PAIR(original, tmp, paths) @@ -6422,9 +6555,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return 0; case ARG_VERSION: - puts(PACKAGE_STRING); - puts(SYSTEMD_FEATURES); - return 0; + return version(); case 't': { const char *word, *state; @@ -6598,7 +6729,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case 's': - if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) { + arg_signal = signal_from_string_try_harder(optarg); + if (arg_signal < 0) { log_error("Failed to parse signal string %s.", optarg); return -EINVAL; } @@ -6800,7 +6932,7 @@ static int halt_parse_argv(int argc, char *argv[]) { return 1; } -static int parse_time_spec(const char *t, usec_t *_u) { +static int parse_shutdown_time_spec(const char *t, usec_t *_u) { assert(t); assert(_u); @@ -6866,6 +6998,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { {} }; + char **wall = NULL; int c, r; assert(argc >= 0); @@ -6929,7 +7062,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { } if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) { - r = parse_time_spec(argv[optind], &arg_when); + r = parse_shutdown_time_spec(argv[optind], &arg_when); if (r < 0) { log_error("Failed to parse time specification: %s", argv[optind]); return r; @@ -6939,10 +7072,16 @@ static int shutdown_parse_argv(int argc, char *argv[]) { if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN) /* No time argument for shutdown cancel */ - arg_wall = argv + optind; + wall = argv + optind; else if (argc > optind + 1) /* We skip the time argument */ - arg_wall = argv + optind + 1; + wall = argv + optind + 1; + + if (wall) { + arg_wall = strv_copy(wall); + if (!arg_wall) + return log_oom(); + } optind = argc; @@ -7006,8 +7145,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { } if (optind >= argc) { - log_error("%s: required argument missing.", - program_invocation_short_name); + log_error("%s: required argument missing.", program_invocation_short_name); return -EINVAL; } @@ -7142,7 +7280,7 @@ _pure_ static int action_to_runlevel(void) { } static int talk_initctl(void) { - +#ifdef HAVE_SYSV_COMPAT struct init_request request = { .magic = INIT_MAGIC, .sleeptime = 0, @@ -7164,8 +7302,7 @@ static int talk_initctl(void) { if (errno == ENOENT) return 0; - log_error_errno(errno, "Failed to open "INIT_FIFO": %m"); - return -errno; + return log_error_errno(errno, "Failed to open "INIT_FIFO": %m"); } r = loop_write(fd, &request, sizeof(request), false); @@ -7173,177 +7310,91 @@ static int talk_initctl(void) { return log_error_errno(r, "Failed to write to "INIT_FIFO": %m"); return 1; +#else + return 0; +#endif } -static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { - - static const struct { - const char* verb; - const enum { - MORE, - LESS, - EQUAL - } argc_cmp; - const int argc; - int (* const dispatch)(sd_bus *bus, char **args); - const enum { - NOBUS = 1, - FORCE, - } bus; - } verbs[] = { - { "list-units", MORE, 0, list_units }, - { "list-unit-files", MORE, 1, list_unit_files, NOBUS }, - { "list-sockets", MORE, 1, list_sockets }, - { "list-timers", MORE, 1, list_timers }, - { "list-jobs", MORE, 1, list_jobs }, - { "list-machines", MORE, 1, list_machines }, - { "clear-jobs", EQUAL, 1, daemon_reload }, - { "cancel", MORE, 2, cancel_job }, - { "start", MORE, 2, start_unit }, - { "stop", MORE, 2, start_unit }, - { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */ - { "reload", MORE, 2, start_unit }, - { "restart", MORE, 2, start_unit }, - { "try-restart", MORE, 2, start_unit }, - { "reload-or-restart", MORE, 2, start_unit }, - { "reload-or-try-restart", MORE, 2, start_unit }, - { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */ - { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */ - { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */ - { "isolate", EQUAL, 2, start_unit }, - { "kill", MORE, 2, kill_unit }, - { "is-active", MORE, 2, check_unit_active }, - { "check", MORE, 2, check_unit_active }, - { "is-failed", MORE, 2, check_unit_failed }, - { "show", MORE, 1, show }, - { "cat", MORE, 2, cat, NOBUS }, - { "status", MORE, 1, show }, - { "help", MORE, 2, show }, - { "snapshot", LESS, 2, snapshot }, - { "delete", MORE, 2, delete_snapshot }, - { "daemon-reload", EQUAL, 1, daemon_reload }, - { "daemon-reexec", EQUAL, 1, daemon_reload }, - { "show-environment", EQUAL, 1, show_environment }, - { "set-environment", MORE, 2, set_environment }, - { "unset-environment", MORE, 2, set_environment }, - { "import-environment", MORE, 1, import_environment}, - { "halt", EQUAL, 1, start_special, FORCE }, - { "poweroff", EQUAL, 1, start_special, FORCE }, - { "reboot", MORE, 1, start_special, FORCE }, - { "kexec", EQUAL, 1, start_special }, - { "suspend", EQUAL, 1, start_special }, - { "hibernate", EQUAL, 1, start_special }, - { "hybrid-sleep", EQUAL, 1, start_special }, - { "default", EQUAL, 1, start_special }, - { "rescue", EQUAL, 1, start_special }, - { "emergency", EQUAL, 1, start_special }, - { "exit", LESS, 2, start_special }, - { "reset-failed", MORE, 1, reset_failed }, - { "enable", MORE, 2, enable_unit, NOBUS }, - { "disable", MORE, 2, enable_unit, NOBUS }, - { "is-enabled", MORE, 2, unit_is_enabled, NOBUS }, - { "reenable", MORE, 2, enable_unit, NOBUS }, - { "preset", MORE, 2, enable_unit, NOBUS }, - { "preset-all", EQUAL, 1, preset_all, NOBUS }, - { "mask", MORE, 2, enable_unit, NOBUS }, - { "unmask", MORE, 2, enable_unit, NOBUS }, - { "link", MORE, 2, enable_unit, NOBUS }, - { "switch-root", MORE, 2, switch_root }, - { "list-dependencies", LESS, 2, list_dependencies }, - { "set-default", EQUAL, 2, set_default, NOBUS }, - { "get-default", EQUAL, 1, get_default, NOBUS }, - { "set-property", MORE, 3, set_property }, - { "is-system-running", EQUAL, 1, is_system_running }, - { "add-wants", MORE, 3, add_dependency, NOBUS }, - { "add-requires", MORE, 3, add_dependency, NOBUS }, - { "edit", MORE, 2, edit, NOBUS }, +static int systemctl_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "list-units", VERB_ANY, 1, VERB_DEFAULT, list_units }, + { "list-unit-files", VERB_ANY, 1, 0, list_unit_files }, + { "list-sockets", VERB_ANY, 1, 0, list_sockets }, + { "list-timers", VERB_ANY, 1, 0, list_timers }, + { "list-jobs", VERB_ANY, 1, 0, list_jobs }, + { "list-machines", VERB_ANY, 1, 0, list_machines }, + { "clear-jobs", VERB_ANY, 1, 0, daemon_reload }, + { "cancel", 2, VERB_ANY, 0, cancel_job }, + { "start", 2, VERB_ANY, 0, start_unit }, + { "stop", 2, VERB_ANY, 0, start_unit }, + { "condstop", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */ + { "reload", 2, VERB_ANY, 0, start_unit }, + { "restart", 2, VERB_ANY, 0, start_unit }, + { "try-restart", 2, VERB_ANY, 0, start_unit }, + { "reload-or-restart", 2, VERB_ANY, 0, start_unit }, + { "reload-or-try-restart", 2, VERB_ANY, 0, start_unit }, + { "force-reload", 2, VERB_ANY, 0, start_unit }, /* For compatibility with SysV */ + { "condreload", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */ + { "condrestart", 2, VERB_ANY, 0, start_unit }, /* For compatibility with RH */ + { "isolate", 2, 2, 0, start_unit }, + { "kill", 2, VERB_ANY, 0, kill_unit }, + { "is-active", 2, VERB_ANY, 0, check_unit_active }, + { "check", 2, VERB_ANY, 0, check_unit_active }, + { "is-failed", 2, VERB_ANY, 0, check_unit_failed }, + { "show", VERB_ANY, VERB_ANY, 0, show }, + { "cat", 2, VERB_ANY, 0, cat }, + { "status", VERB_ANY, VERB_ANY, 0, show }, + { "help", VERB_ANY, VERB_ANY, 0, show }, + { "snapshot", VERB_ANY, 2, 0, snapshot }, + { "delete", 2, VERB_ANY, 0, delete_snapshot }, + { "daemon-reload", VERB_ANY, 1, 0, daemon_reload }, + { "daemon-reexec", VERB_ANY, 1, 0, daemon_reload }, + { "show-environment", VERB_ANY, 1, 0, show_environment }, + { "set-environment", 2, VERB_ANY, 0, set_environment }, + { "unset-environment", 2, VERB_ANY, 0, set_environment }, + { "import-environment", VERB_ANY, VERB_ANY, 0, import_environment}, + { "halt", VERB_ANY, 1, 0, start_special }, + { "poweroff", VERB_ANY, 1, 0, start_special }, + { "reboot", VERB_ANY, 2, 0, start_special }, + { "kexec", VERB_ANY, 1, 0, start_special }, + { "suspend", VERB_ANY, 1, 0, start_special }, + { "hibernate", VERB_ANY, 1, 0, start_special }, + { "hybrid-sleep", VERB_ANY, 1, 0, start_special }, + { "default", VERB_ANY, 1, 0, start_special }, + { "rescue", VERB_ANY, 1, 0, start_special }, + { "emergency", VERB_ANY, 1, 0, start_special }, + { "exit", VERB_ANY, 2, 0, start_special }, + { "reset-failed", VERB_ANY, VERB_ANY, 0, reset_failed }, + { "enable", 2, VERB_ANY, 0, enable_unit }, + { "disable", 2, VERB_ANY, 0, enable_unit }, + { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled }, + { "reenable", 2, VERB_ANY, 0, enable_unit }, + { "preset", 2, VERB_ANY, 0, enable_unit }, + { "preset-all", VERB_ANY, 1, 0, preset_all }, + { "mask", 2, VERB_ANY, 0, enable_unit }, + { "unmask", 2, VERB_ANY, 0, enable_unit }, + { "link", 2, VERB_ANY, 0, enable_unit }, + { "switch-root", 2, VERB_ANY, 0, switch_root }, + { "list-dependencies", VERB_ANY, 2, 0, list_dependencies }, + { "set-default", 2, 2, 0, set_default }, + { "get-default", VERB_ANY, 1, 0, get_default, }, + { "set-property", 3, VERB_ANY, 0, set_property }, + { "is-system-running", VERB_ANY, 1, 0, is_system_running }, + { "add-wants", 3, VERB_ANY, 0, add_dependency }, + { "add-requires", 3, VERB_ANY, 0, add_dependency }, + { "edit", 2, VERB_ANY, 0, edit }, {} - }, *verb = verbs; - - int left; - - assert(argc >= 0); - assert(argv); - - left = argc - optind; - - /* Special rule: no arguments (left == 0) means "list-units" */ - if (left > 0) { - if (streq(argv[optind], "help") && !argv[optind+1]) { - log_error("This command expects one or more " - "unit names. Did you mean --help?"); - return -EINVAL; - } - - for (; verb->verb; verb++) - if (streq(argv[optind], verb->verb)) - goto found; - - log_error("Unknown operation '%s'.", argv[optind]); - return -EINVAL; - } -found: - - switch (verb->argc_cmp) { - - case EQUAL: - if (left != verb->argc) { - log_error("Invalid number of arguments."); - return -EINVAL; - } - - break; - - case MORE: - if (left < verb->argc) { - log_error("Too few arguments."); - return -EINVAL; - } - - break; - - case LESS: - if (left > verb->argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - break; - - default: - assert_not_reached("Unknown comparison operator."); - } - - /* Require a bus connection for all operations but - * enable/disable */ - if (verb->bus == NOBUS) { - if (!bus && !avoid_bus()) { - log_error_errno(bus_error, "Failed to get D-Bus connection: %m"); - return -EIO; - } - - } else { - if (running_in_chroot() > 0) { - log_info("Running in chroot, ignoring request."); - return 0; - } - - if ((verb->bus != FORCE || arg_force <= 0) && !bus) { - log_error_errno(bus_error, "Failed to get D-Bus connection: %m"); - return -EIO; - } - } + }; - return verb->dispatch(bus, argv + optind); + return dispatch_verb(argc, argv, verbs, NULL); } -static int reload_with_fallback(sd_bus *bus) { +static int reload_with_fallback(void) { - if (bus) { - /* First, try systemd via D-Bus. */ - if (daemon_reload(bus, NULL) >= 0) - return 0; - } + /* First, try systemd via D-Bus. */ + if (daemon_reload(0, NULL, NULL) >= 0) + return 0; /* Nothing else worked, so let's try signals */ assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC); @@ -7354,25 +7405,19 @@ static int reload_with_fallback(sd_bus *bus) { return 0; } -static int start_with_fallback(sd_bus *bus) { +static int start_with_fallback(void) { - if (bus) { - /* First, try systemd via D-Bus. */ - if (start_unit(bus, NULL) >= 0) - goto done; - } + /* First, try systemd via D-Bus. */ + if (start_unit(0, NULL, NULL) >= 0) + return 0; /* Nothing else worked, so let's try * /dev/initctl */ if (talk_initctl() > 0) - goto done; + return 0; log_error("Failed to talk to init daemon."); return -EIO; - -done: - warn_wall(arg_action); - return 0; } static int halt_now(enum action a) { @@ -7380,22 +7425,22 @@ static int halt_now(enum action a) { /* The kernel will automaticall flush ATA disks and suchlike * on reboot(), but the file systems need to be synce'd * explicitly in advance. */ - sync(); + (void) sync(); /* Make sure C-A-D is handled by the kernel from this point * on... */ - reboot(RB_ENABLE_CAD); + (void) reboot(RB_ENABLE_CAD); switch (a) { case ACTION_HALT: log_info("Halting."); - reboot(RB_HALT_SYSTEM); + (void) reboot(RB_HALT_SYSTEM); return -errno; case ACTION_POWEROFF: log_info("Powering off."); - reboot(RB_POWER_OFF); + (void) reboot(RB_POWER_OFF); return -errno; case ACTION_KEXEC: @@ -7404,12 +7449,11 @@ static int halt_now(enum action a) { if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { log_info("Rebooting with argument '%s'.", param); - syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, - LINUX_REBOOT_CMD_RESTART2, param); + (void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); } log_info("Rebooting."); - reboot(RB_AUTOBOOT); + (void) reboot(RB_AUTOBOOT); return -errno; } @@ -7418,17 +7462,73 @@ static int halt_now(enum action a) { } } -static int halt_main(sd_bus *bus) { +static int logind_schedule_shutdown(void) { + +#ifdef HAVE_LOGIND + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + char date[FORMAT_TIMESTAMP_MAX]; + const char *action; + sd_bus *bus; int r; - r = check_inhibitors(bus, arg_action); + (void) logind_set_wall_message(); + + r = acquire_bus(BUS_FULL, &bus); if (r < 0) return r; + switch (arg_action) { + case ACTION_HALT: + action = "halt"; + break; + case ACTION_POWEROFF: + action = "poweroff"; + break; + case ACTION_KEXEC: + action = "kexec"; + break; + default: + action = "reboot"; + break; + } + + if (arg_dry) + action = strjoina("dry-", action); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ScheduleShutdown", + &error, + NULL, + "st", + action, + arg_when); + if (r < 0) + return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r)); + + log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.", format_timestamp(date, sizeof(date), arg_when)); + return 0; +#else + log_error("Cannot schedule shutdown without logind support, proceeding with immediate shutdown."); + return -ENOSYS; +#endif +} + +static int halt_main(void) { + int r; + + r = logind_check_inhibitors(arg_action); + if (r < 0) + return r; + + if (arg_when > 0) + return logind_schedule_shutdown(); + if (geteuid() != 0) { - if (arg_when > 0 || - arg_dry || - arg_force > 0) { + if (arg_dry || arg_force > 0) { log_error("Must be root."); return -EPERM; } @@ -7436,101 +7536,21 @@ static int halt_main(sd_bus *bus) { /* Try logind if we are a normal user and no special * mode applies. Maybe PolicyKit allows us to shutdown * the machine. */ - if (IN_SET(arg_action, - ACTION_POWEROFF, - ACTION_REBOOT)) { - r = reboot_with_logind(bus, arg_action); + if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT)) { + r = logind_reboot(arg_action); if (r >= 0) return r; if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) - /* requested operation is not supported or already in progress */ + /* requested operation is not + * supported on the local system or + * already in progress */ return r; /* on all other errors, try low-level operation */ } } - if (arg_when > 0) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_flush_close_unref_ sd_bus *b = NULL; - _cleanup_free_ char *m = NULL; - const char *action; - - assert(geteuid() == 0); - - if (avoid_bus()) { - log_error("Unable to perform operation without bus connection."); - return -ENOSYS; - } - - r = sd_bus_open_system(&b); - if (r < 0) - return log_error_errno(r, "Unable to open system bus: %m"); - - m = strv_join(arg_wall, " "); - if (!m) - return log_oom(); - - r = sd_bus_call_method( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetWallMessage", - &error, - NULL, - "sb", - m, - !arg_no_wall); - - if (r < 0) { - log_warning_errno(r, "Failed to set wall message, ignoring: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } - - switch (arg_action) { - case ACTION_HALT: - action = "halt"; - break; - case ACTION_POWEROFF: - action = "poweroff"; - break; - case ACTION_KEXEC: - action = "kexec"; - break; - default: - action = "reboot"; - break; - } - - if (arg_dry) - action = strjoina("dry-", action); - - r = sd_bus_call_method( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ScheduleShutdown", - &error, - NULL, - "st", - action, - arg_when); - if (r < 0) - log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", - bus_error_message(&error, r)); - else { - char date[FORMAT_TIMESTAMP_MAX]; - - log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.", - format_timestamp(date, sizeof(date), arg_when)); - return 0; - } - } - if (!arg_dry && !arg_force) - return start_with_fallback(bus); + return start_with_fallback(); assert(geteuid() == 0); @@ -7548,9 +7568,7 @@ static int halt_main(sd_bus *bus) { return 0; r = halt_now(arg_action); - log_error_errno(r, "Failed to reboot: %m"); - - return r; + return log_error_errno(r, "Failed to reboot: %m"); } static int runlevel_main(void) { @@ -7569,8 +7587,37 @@ static int runlevel_main(void) { return 0; } +static int logind_cancel_shutdown(void) { +#ifdef HAVE_LOGIND + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + (void) logind_set_wall_message(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "CancelScheduledShutdown", + &error, + NULL, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r)); + + return 0; +#else + log_error("Not compiled with logind support, cannot cancel scheduled shutdowns."); + return -ENOSYS; +#endif +} + int main(int argc, char*argv[]) { - _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -7586,39 +7633,26 @@ int main(int argc, char*argv[]) { if (r <= 0) goto finish; - /* /sbin/runlevel doesn't need to communicate via D-Bus, so - * let's shortcut this */ - if (arg_action == ACTION_RUNLEVEL) { - r = runlevel_main(); - goto finish; - } - if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) { log_info("Running in chroot, ignoring request."); r = 0; goto finish; } - if (!avoid_bus()) - r = bus_open_transport_systemd(arg_transport, arg_host, arg_scope != UNIT_FILE_SYSTEM, &bus); - - if (bus) - sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); - /* systemctl_main() will print an error message for the bus * connection, but only if it needs to */ switch (arg_action) { case ACTION_SYSTEMCTL: - r = systemctl_main(bus, argc, argv, r); + r = systemctl_main(argc, argv); break; case ACTION_HALT: case ACTION_POWEROFF: case ACTION_REBOOT: case ACTION_KEXEC: - r = halt_main(bus); + r = halt_main(); break; case ACTION_RUNLEVEL2: @@ -7628,69 +7662,22 @@ int main(int argc, char*argv[]) { case ACTION_RESCUE: case ACTION_EMERGENCY: case ACTION_DEFAULT: - r = start_with_fallback(bus); + r = start_with_fallback(); break; case ACTION_RELOAD: case ACTION_REEXEC: - r = reload_with_fallback(bus); + r = reload_with_fallback(); break; - case ACTION_CANCEL_SHUTDOWN: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_flush_close_unref_ sd_bus *b = NULL; - _cleanup_free_ char *m = NULL; - - if (avoid_bus()) { - log_error("Unable to perform operation without bus connection."); - return -ENOSYS; - } - - r = sd_bus_open_system(&b); - if (r < 0) - return log_error_errno(r, "Unable to open system bus: %m"); - - if (arg_wall) { - m = strv_join(arg_wall, " "); - if (!m) { - r = log_oom(); - goto finish; - } - } - - r = sd_bus_call_method( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetWallMessage", - &error, - NULL, - "sb", - m, - !arg_no_wall); - - if (r < 0) { - log_warning_errno(r, "Failed to set wall message, ignoring: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } - - r = sd_bus_call_method( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "CancelScheduledShutdown", - &error, - NULL, NULL); - if (r < 0) - log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", - bus_error_message(&error, r)); + case ACTION_CANCEL_SHUTDOWN: + r = logind_cancel_shutdown(); break; - } case ACTION_RUNLEVEL: + r = runlevel_main(); + break; + case _ACTION_INVALID: default: assert_not_reached("Unknown action"); @@ -7705,7 +7692,9 @@ finish: strv_free(arg_states); strv_free(arg_properties); - sd_bus_default_flush_close(); + strv_free(arg_wall); + + release_busses(); return r < 0 ? EXIT_FAILURE : r; } |