X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=5dcefd7f18590bfd56fcc6869c515fee49507f20;hp=7a2431986dfd8b650d754e1c37f434d28927acae;hb=e4c0fbe50c9a4a26efa4ae2b2350215be29b8660;hpb=a9616cd17fb0a57ac92d39e1ad261caa1039d7b1 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 7a2431986..5dcefd7f1 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -65,12 +65,12 @@ #include "spawn-polkit-agent.h" #include "install.h" #include "logs-show.h" -#include "path-util.h" #include "socket-util.h" #include "fileio.h" #include "bus-util.h" #include "bus-message.h" #include "bus-error.h" +#include "bus-errors.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -135,7 +135,9 @@ static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; static int daemon_reload(sd_bus *bus, char **args); -static void halt_now(enum action a); +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 void pager_open_if_enabled(void) { @@ -195,7 +197,7 @@ static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) return EXIT_NOTINSTALLED; if (sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) || - sd_bus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED)) + sd_bus_error_has_name(error, SD_BUS_ERROR_NOT_SUPPORTED)) return EXIT_NOTIMPLEMENTED; if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) @@ -532,13 +534,12 @@ static int get_triggered_units( static int get_listening( sd_bus *bus, const char* unit_path, - char*** listen, - unsigned *c) { + char*** listening) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; const char *type, *path; - int r; + int r, n = 0; r = sd_bus_get_property( bus, @@ -560,15 +561,15 @@ static int get_listening( while ((r = sd_bus_message_read(reply, "(ss)", &type, &path)) > 0) { - r = strv_extend(listen, type); + r = strv_extend(listening, type); if (r < 0) return log_oom(); - r = strv_extend(listen, path); + r = strv_extend(listening, path); if (r < 0) return log_oom(); - (*c)++; + n++; } if (r < 0) return bus_log_parse_error(r); @@ -577,7 +578,7 @@ static int get_listening( if (r < 0) return bus_log_parse_error(r); - return 0; + return n; } struct socket_info { @@ -595,9 +596,12 @@ struct socket_info { bool own_triggered; }; -static int socket_info_compare(struct socket_info *a, struct socket_info *b) { +static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) { int o; + assert(a); + assert(b); + o = strcmp(a->path, b->path); if (o == 0) o = strcmp(a->type, b->type); @@ -674,21 +678,19 @@ static int list_sockets(sd_bus *bus, char **args) { struct socket_info *socket_infos = NULL; const UnitInfo *u; struct socket_info *s; - unsigned cu = 0, cs = 0; + unsigned cs = 0; size_t size = 0; - int r; + int r = 0, n; pager_open_if_enabled(); - r = get_unit_list(bus, &reply, &unit_infos); - if (r < 0) - return r; - - cu = (unsigned) r; + n = get_unit_list(bus, &reply, &unit_infos); + if (n < 0) + return n; - for (u = unit_infos; u < unit_infos + cu; u++) { - _cleanup_strv_free_ char **listen = NULL, **triggered = NULL; - unsigned c = 0, i; + for (u = unit_infos; u < unit_infos + n; u++) { + _cleanup_strv_free_ char **listening = NULL, **triggered = NULL; + int i, c; if (!output_show_unit(u)) continue; @@ -700,9 +702,11 @@ static int list_sockets(sd_bus *bus, char **args) { if (r < 0) goto cleanup; - r = get_listening(bus, u->unit_path, &listen, &c); - if (r < 0) + c = get_listening(bus, u->unit_path, &listening); + if (c < 0) { + r = c; goto cleanup; + } if (!GREEDY_REALLOC(socket_infos, size, cs + c)) { r = log_oom(); @@ -712,16 +716,16 @@ static int list_sockets(sd_bus *bus, char **args) { for (i = 0; i < c; i++) socket_infos[cs + i] = (struct socket_info) { .id = u->id, - .type = listen[i*2], - .path = listen[i*2 + 1], + .type = listening[i*2], + .path = listening[i*2 + 1], .triggered = triggered, .own_triggered = i==0, }; /* from this point on we will cleanup those socket_infos */ cs += c; - free(listen); - listen = triggered = NULL; /* avoid cleanup */ + free(listening); + listening = triggered = NULL; /* avoid cleanup */ } qsort_safe(socket_infos, cs, sizeof(struct socket_info), @@ -742,6 +746,224 @@ static int list_sockets(sd_bus *bus, char **args) { return r; } +static int get_next_elapse( + sd_bus *bus, + const char *path, + dual_timestamp *next) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + dual_timestamp t; + int r; + + assert(bus); + assert(path); + assert(next); + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Timer", + "NextElapseUSecMonotonic", + &error, + 't', + &t.monotonic); + if (r < 0) { + log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Timer", + "NextElapseUSecRealtime", + &error, + 't', + &t.realtime); + if (r < 0) { + log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r)); + return r; + } + + *next = t; + return 0; +} + +struct timer_info { + const char* id; + usec_t next_elapse; + char** triggered; +}; + +static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) { + assert(a); + assert(b); + + if (a->next_elapse < b->next_elapse) + return -1; + if (a->next_elapse > b->next_elapse) + return 1; + + return strcmp(a->id, b->id); +} + +static int output_timers_list(struct timer_info *timer_infos, unsigned n) { + struct timer_info *t; + unsigned + nextlen = sizeof("NEXT") - 1, + leftlen = sizeof("LEFT") - 1, + unitlen = sizeof("UNIT") - 1, + activatelen = sizeof("ACTIVATES") - 1; + + const char *on, *off; + + assert(timer_infos || n == 0); + + for (t = timer_infos; t < timer_infos + n; t++) { + unsigned ul = 0; + char **a; + + if (t->next_elapse > 0) { + char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = ""; + + format_timestamp(tstamp, sizeof(tstamp), t->next_elapse); + nextlen = MAX(nextlen, strlen(tstamp) + 1); + + format_timestamp_relative(trel, sizeof(trel), t->next_elapse); + leftlen = MAX(leftlen, strlen(trel)); + } + + unitlen = MAX(unitlen, strlen(t->id)); + + STRV_FOREACH(a, t->triggered) + ul += strlen(*a) + 2*(a != t->triggered); + activatelen = MAX(activatelen, ul); + } + + if (n > 0) { + if (!arg_no_legend) + printf("%-*s %-*s %-*s %s\n", + nextlen, "NEXT", + leftlen, "LEFT", + unitlen, "UNIT", + "ACTIVATES"); + + for (t = timer_infos; t < timer_infos + n; t++) { + char tstamp[FORMAT_TIMESTAMP_MAX] = "n/a", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a"; + char **a; + + format_timestamp(tstamp, sizeof(tstamp), t->next_elapse); + format_timestamp_relative(trel, sizeof(trel), t->next_elapse); + + printf("%-*s %-*s %-*s", + nextlen, tstamp, leftlen, trel, unitlen, t->id); + + STRV_FOREACH(a, t->triggered) + printf("%s %s", + a == t->triggered ? "" : ",", *a); + printf("\n"); + } + + on = ansi_highlight(); + off = ansi_highlight_off(); + if (!arg_no_legend) + printf("\n"); + } else { + on = ansi_highlight_red(); + off = ansi_highlight_off(); + } + + if (!arg_no_legend) { + printf("%s%u timers listed.%s\n", on, n, off); + if (!arg_all) + printf("Pass --all to see loaded but inactive timers, too.\n"); + } + + return 0; +} + +static int list_timers(sd_bus *bus, char **args) { + + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_free_ struct timer_info *timer_infos = NULL; + _cleanup_free_ UnitInfo *unit_infos = NULL; + struct timer_info *t; + const UnitInfo *u; + size_t size = 0; + int n, c = 0; + dual_timestamp nw; + int r = 0; + + pager_open_if_enabled(); + + n = get_unit_list(bus, &reply, &unit_infos); + if (n < 0) + return n; + + dual_timestamp_get(&nw); + + for (u = unit_infos; u < unit_infos + n; u++) { + _cleanup_strv_free_ char **triggered = NULL; + dual_timestamp next; + usec_t m; + + if (!output_show_unit(u)) + continue; + + if (!endswith(u->id, ".timer")) + continue; + + r = get_triggered_units(bus, u->unit_path, &triggered); + if (r < 0) + goto cleanup; + + r = get_next_elapse(bus, u->unit_path, &next); + if (r < 0) + goto cleanup; + + if (next.monotonic != (usec_t) -1 && next.monotonic > 0) { + usec_t converted; + + if (next.monotonic > nw.monotonic) + converted = nw.realtime + (next.monotonic - nw.monotonic); + else + converted = nw.realtime - (nw.monotonic - next.monotonic); + + if (next.realtime != (usec_t) -1 && next.realtime > 0) + m = MIN(converted, next.realtime); + else + m = converted; + } else + m = next.realtime; + + if (!GREEDY_REALLOC(timer_infos, size, c+1)) { + r = log_oom(); + goto cleanup; + } + + timer_infos[c++] = (struct timer_info) { + .id = u->id, + .next_elapse = m, + .triggered = triggered, + }; + + triggered = NULL; /* avoid cleanup */ + } + + qsort_safe(timer_infos, c, sizeof(struct timer_info), + (__compar_fn_t) timer_info_compare); + + output_timers_list(timer_infos, c); + + cleanup: + for (t = timer_infos; t < timer_infos + c; t++) + strv_free(t->triggered); + + return r; +} + static int compare_unit_file_list(const void *a, const void *b) { const char *d1, *d2; const UnitFileList *u = a, *v = b; @@ -1089,6 +1311,8 @@ static int list_dependencies_one( qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); STRV_FOREACH(c, deps) { + int state; + if (strv_contains(u, *c)) { if (!arg_plain) { r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); @@ -1098,6 +1322,12 @@ static int list_dependencies_one( continue; } + state = check_one_unit(bus, *c, "activating\0active\0reloading\0", true); + if (state > 0) + printf("%s%s%s", ansi_highlight_green(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off()); + else + printf("%s%s%s", ansi_highlight_red(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off()); + r = list_dependencies_print(*c, level, branches, c[1] == NULL); if (r < 0) return r; @@ -1181,6 +1411,100 @@ static int get_default(sd_bus *bus, char **args) { return 0; } +static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + for (i = 0; i < n_changes; i++) { + if (changes[i].type == UNIT_FILE_SYMLINK) + log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); + else + log_info("rm '%s'", changes[i].path); + } +} + +static int deserialize_and_dump_unit_file_changes(sd_bus_message *m) { + const char *type, *path, *source; + int r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { + if (!arg_quiet) { + if (streq(type, "symlink")) + log_info("ln -s '%s' '%s'", source, path); + else + log_info("rm '%s'", path); + } + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; +} + +static int set_default(sd_bus *bus, char **args) { + _cleanup_free_ char *unit = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int r; + + unit = unit_name_mangle_with_suffix(args[1], ".target"); + if (!unit) + return log_oom(); + + if (!bus || avoid_bus()) { + r = unit_file_set_default(arg_scope, arg_root, unit, arg_force, &changes, &n_changes); + if (r < 0) { + log_error("Failed to set default target: %s", strerror(-r)); + return r; + } + + if (!arg_quiet) + dump_unit_file_changes(changes, n_changes); + + r = 0; + } else { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetDefaultTarget", + &error, + &reply, + "sb", unit, arg_force); + if (r < 0) { + log_error("Failed to set default target: %s", bus_error_message(&error, -r)); + return r; + } + + r = deserialize_and_dump_unit_file_changes(reply); + if (r < 0) + return r; + + /* Try to reload if enabeld */ + if (!arg_no_reload) + r = daemon_reload(bus, args); + else + r = 0; + } + + unit_file_changes_free(changes, n_changes); + + return r; +} + struct job_info { uint32_t id; const char *name, *type, *state; @@ -1204,7 +1528,11 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n) { pager_open_if_enabled(); - id_len = unit_len = type_len = state_len = 0; + id_len = sizeof("JOB")-1; + unit_len = sizeof("UNIT")-1; + type_len = sizeof("TYPE")-1; + state_len = sizeof("STATE")-1; + for (j = jobs; j < jobs + n; j++) { uint32_t id = j->id; assert(j->name && j->type && j->state); @@ -1220,11 +1548,12 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n) { shorten = true; } - printf("%*s %-*s %-*s %-*s\n", - id_len, "JOB", - unit_len, "UNIT", - type_len, "TYPE", - state_len, "STATE"); + if (!arg_no_legend) + printf("%*s %-*s %-*s %-*s\n", + id_len, "JOB", + unit_len, "UNIT", + type_len, "TYPE", + state_len, "STATE"); for (j = jobs; j < jobs + n; j++) { _cleanup_free_ char *e = NULL; @@ -1243,10 +1572,12 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n) { on, state_len, j->state, off); } - on = ansi_highlight(); - off = ansi_highlight_off(); + if (!arg_no_legend) { + on = ansi_highlight(); + off = ansi_highlight_off(); - printf("\n%s%u jobs listed%s.\n", on, n, off); + printf("\n%s%u jobs listed%s.\n", on, n, off); + } } static int list_jobs(sd_bus *bus, char **args) { @@ -1390,7 +1721,7 @@ typedef struct WaitData { char *result; } WaitData; -static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data) { +static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data, sd_bus_error *error) { WaitData *d = data; assert(bus); @@ -1443,7 +1774,7 @@ static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data) { } #endif - log_error("Failed to parse message."); + bus_log_parse_error(r); } return 0; @@ -1899,7 +2230,6 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { static int check_inhibitors(sd_bus *bus, enum action a) { #ifdef HAVE_LOGIND - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_strv_free_ char **sessions = NULL; const char *what, *who, *why, *mode; @@ -1936,11 +2266,11 @@ static int check_inhibitors(sd_bus *bus, enum action a) { /* If logind is not around, then there are no inhibitors... */ return 0; - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "ssssuu"); + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); if (r < 0) return bus_log_parse_error(r); - while ((r = sd_bus_message_read(reply, "ssssuu", &what, &who, &why, &mode, &uid, &pid)) > 0) { + while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { _cleanup_free_ char *comm = NULL, *user = NULL; _cleanup_strv_free_ char **sv = NULL; @@ -2029,7 +2359,7 @@ static int start_special(sd_bus *bus, char **args) { (a == ACTION_HALT || a == ACTION_POWEROFF || a == ACTION_REBOOT)) - halt_now(a); + return halt_now(a); if (arg_force >= 1 && (a == ACTION_HALT || @@ -2913,8 +3243,11 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte /* This is a low-level property printer, see * print_status_info() for the nicer output */ - if (arg_properties && !strv_find(arg_properties, name)) - return 0; + if (arg_properties && !strv_find(arg_properties, name)) { + /* skip what we didn't read */ + r = sd_bus_message_skip(m, contents); + return r; + } switch (contents[0]) { @@ -3271,16 +3604,13 @@ static int show_one( return r; } -static int show_one_by_pid( - const char *verb, +static int get_unit_dbus_path_by_pid( sd_bus *bus, uint32_t pid, - bool *new_line, - bool *ellipsized) { + char **unit) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - const char *path = NULL; int r; r = sd_bus_call_method( @@ -3297,11 +3627,11 @@ static int show_one_by_pid( return r; } - r = sd_bus_message_read(reply, "o", &path); + r = sd_bus_message_read(reply, "o", unit); if (r < 0) return bus_log_parse_error(r); - return show_one(verb, bus, path, false, new_line, ellipsized); + return 0; } static int show_all( @@ -3311,7 +3641,6 @@ static int show_all( bool *new_line, bool *ellipsized) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ UnitInfo *unit_infos = NULL; const UnitInfo *u; @@ -3322,6 +3651,8 @@ static int show_all( if (r < 0) return r; + pager_open_if_enabled(); + c = (unsigned) r; qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info); @@ -3336,8 +3667,6 @@ static int show_all( if (!p) return log_oom(); - printf("%s -> '%s'\n", u->id, p); - r = show_one(verb, bus, p, show_properties, new_line, ellipsized); if (r != 0) return r; @@ -3346,6 +3675,90 @@ static int show_all( return 0; } +static int cat(sd_bus *bus, char **args) { + int r = 0; + char **name; + + _cleanup_free_ char *unit = NULL, *n = NULL; + + assert(bus); + assert(args); + + pager_open_if_enabled(); + + STRV_FOREACH(name, args+1) { + _cleanup_free_ char *fragment_path = NULL; + _cleanup_strv_free_ char **dropin_paths = NULL; + sd_bus_error error; + FILE *stdout; + char **path; + + n = unit_name_mangle(*name); + if (!n) + return log_oom(); + + unit = unit_dbus_path_from_name(n); + if (!unit) + return log_oom(); + + if (need_daemon_reload(bus, n) > 0) + log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.", + n, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "FragmentPath", + &error, + &fragment_path); + if (r < 0) { + log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r)); + continue; + } + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "DropInPaths", + &error, + &dropin_paths); + if (r < 0) { + log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r)); + continue; + } + + stdout = fdopen(STDOUT_FILENO, "a"); + + if (!isempty(fragment_path)) { + fprintf(stdout, "# %s\n", fragment_path); + fflush(stdout); + r = sendfile_full(STDOUT_FILENO, fragment_path); + if (r < 0) { + log_warning("Failed to cat %s: %s", fragment_path, strerror(-r)); + continue; + } + } + + STRV_FOREACH(path, dropin_paths) { + fprintf(stdout, "%s# %s\n", + isempty(fragment_path) && path == dropin_paths ? "" : "\n", + *path); + fflush(stdout); + r = sendfile_full(STDOUT_FILENO, *path); + if (r < 0) { + log_warning("Failed to cat %s: %s", *path, strerror(-r)); + continue; + } + } + } + + return r; +} + static int show(sd_bus *bus, char **args) { int r, ret = 0; bool show_properties, show_status, new_line = false; @@ -3370,41 +3783,34 @@ static int show(sd_bus *bus, char **args) { ret = show_all(args[0], bus, false, &new_line, &ellipsized); else STRV_FOREACH(name, args+1) { + _cleanup_free_ char *unit = NULL; uint32_t id; if (safe_atou32(*name, &id) < 0) { - _cleanup_free_ char *p = NULL, *n = NULL; + _cleanup_free_ char *n = NULL; /* Interpret as unit name */ n = unit_name_mangle(*name); if (!n) return log_oom(); - p = unit_dbus_path_from_name(n); - if (!p) + unit = unit_dbus_path_from_name(n); + if (!unit) return log_oom(); - r = show_one(args[0], bus, p, show_properties, &new_line, &ellipsized); - if (r != 0) - ret = r; - } else if (show_properties) { - _cleanup_free_ char *p = NULL; - /* Interpret as job id */ - if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) + if (asprintf(&unit, "/org/freedesktop/systemd1/job/%u", id) < 0) return log_oom(); - r = show_one(args[0], bus, p, show_properties, &new_line, &ellipsized); - if (r != 0) - ret = r; - } else { /* Interpret as PID */ - r = show_one_by_pid(args[0], bus, id, &new_line, &ellipsized); - if (r != 0) + r = get_unit_dbus_path_by_pid(bus, id, &unit); + if (r < 0) ret = r; } + + show_one(args[0], bus, unit, show_properties, &new_line, &ellipsized); } if (ellipsized && !arg_quiet) @@ -3617,7 +4023,7 @@ static int set_property(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_send_with_reply_and_block(bus, m, -1, &error, NULL); + 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; @@ -3755,7 +4161,7 @@ static int daemon_reload(sd_bus *bus, char **args) { else if (r < 0) log_error("Failed to execute operation: %s", bus_error_message(&error, r)); - return r; + return r < 0 ? r : 0; } static int reset_failed(sd_bus *bus, char **args) { @@ -3904,7 +4310,7 @@ static int set_environment(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_send_with_reply_and_block(bus, m, -1, &error, NULL); + 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; @@ -4104,12 +4510,10 @@ static int mangle_names(char **original_names, char ***mangled_names) { } static int enable_unit(sd_bus *bus, char **args) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_strv_free_ char **mangled_names = NULL; const char *verb = args[0]; UnitFileChange *changes = NULL; - unsigned n_changes = 0, i; + unsigned n_changes = 0; int carries_install_info = -1; int r; @@ -4142,8 +4546,6 @@ static int enable_unit(sd_bus *bus, char **args) { r = unit_file_mask(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, arg_runtime, arg_root, mangled_names, &changes, &n_changes); - else if (streq(verb, "set-default")) - r = unit_file_set_default(arg_scope, arg_root, args[1], &changes, &n_changes); else assert_not_reached("Unknown verb"); @@ -4152,20 +4554,16 @@ static int enable_unit(sd_bus *bus, char **args) { goto finish; } - if (!arg_quiet) { - for (i = 0; i < n_changes; i++) { - if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); - else - log_info("rm '%s'", changes[i].path); - } - } + if (!arg_quiet) + dump_unit_file_changes(changes, n_changes); r = 0; } else { - const char *method, *type, *path, *source; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int expect_carries_install_info = false; bool send_force = true; + const char *method; if (streq(verb, "enable")) { method = "EnableUnitFiles"; @@ -4186,8 +4584,6 @@ static int enable_unit(sd_bus *bus, char **args) { else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; - } else if (streq(verb, "set-default")) { - method = "SetDefaultTarget"; } else assert_not_reached("Unknown verb"); @@ -4215,7 +4611,7 @@ static int enable_unit(sd_bus *bus, char **args) { return bus_log_create_error(r); } - r = sd_bus_send_with_reply_and_block(bus, m, -0, &error, &reply); + 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; @@ -4227,24 +4623,9 @@ static int enable_unit(sd_bus *bus, char **args) { return bus_log_parse_error(r); } - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(sss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(sss)", &type, &path, &source)) > 0) { - if (!arg_quiet) { - if (streq(type, "symlink")) - log_info("ln -s '%s' '%s'", source, path); - else - log_info("rm '%s'", path); - } - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); + r = deserialize_and_dump_unit_file_changes(reply); if (r < 0) - return bus_log_parse_error(r); + return r; /* Try to reload if enabeld */ if (!arg_no_reload) @@ -4364,14 +4745,10 @@ static int systemctl_help(void) { " -a --all Show all loaded units/properties, including dead/empty\n" " ones. To list all units installed on the system, use\n" " the 'list-unit-files' command instead.\n" - " --reverse Show reverse dependencies with 'list-dependencies'\n" " -l --full Don't ellipsize unit names on output\n" - " --fail When queueing a new job, fail if conflicting jobs are\n" - " pending\n" - " --irreversible When queueing a new job, make sure it cannot be implicitly\n" - " cancelled\n" - " --ignore-dependencies\n" - " When queueing a new job, ignore all its dependencies\n" + " --reverse Show reverse dependencies with 'list-dependencies'\n" + " --job-mode=MODE Specify how to deal with already queued jobs, when\n" + " queueing a new job\n" " --show-types When showing sockets, explicitly show their type\n" " -i --ignore-inhibitors\n" " When shutting down or sleeping, ignore inhibitors\n" @@ -4397,6 +4774,7 @@ static int systemctl_help(void) { "Unit Commands:\n" " list-units List loaded units\n" " list-sockets List loaded sockets ordered by address\n" + " list-timers List loaded timers ordered by next elapse\n" " start [NAME...] Start (activate) one or more units\n" " stop [NAME...] Stop (deactivate) one or more units\n" " reload [NAME...] Reload one or more units\n" @@ -4413,6 +4791,7 @@ static int systemctl_help(void) { " status [NAME...|PID...] Show runtime status of one or more units\n" " show [NAME...|JOB...] Show properties of one or more\n" " units/jobs or the manager\n" + " cat [NAME...] Show files and drop-ins of one or more units\n" " set-property [NAME] [ASSIGNMENT...]\n" " Sets one or more properties of a unit\n" " help [NAME...|PID...] Show manual for one or more units\n" @@ -4572,7 +4951,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_RUNTIME, ARG_FORCE, ARG_PLAIN, - ARG_STATE + ARG_STATE, + ARG_JOB_MODE }; static const struct option options[] = { @@ -4587,9 +4967,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "show-types", no_argument, NULL, ARG_SHOW_TYPES }, { "failed", no_argument, NULL, ARG_FAILED }, /* compatibility only */ { "full", no_argument, NULL, 'l' }, - { "fail", no_argument, NULL, ARG_FAIL }, - { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, - { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, + { "job-mode", required_argument, NULL, ARG_JOB_MODE }, + { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */ + { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */ + { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */ { "ignore-inhibitors", no_argument, NULL, 'i' }, { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, @@ -4727,6 +5108,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_show_types = true; break; + case ARG_JOB_MODE: + arg_job_mode = optarg; + break; + case ARG_FAIL: arg_job_mode = "fail"; break; @@ -5354,7 +5739,7 @@ static int talk_initctl(void) { return 1; } -static int systemctl_main(sd_bus *bus, int argc, char *argv[], const int r) { +static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { static const struct { const char* verb; @@ -5369,6 +5754,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], const int r) { { "list-units", LESS, 1, list_units }, { "list-unit-files", EQUAL, 1, list_unit_files }, { "list-sockets", LESS, 1, list_sockets }, + { "list-timers", LESS, 1, list_timers }, { "list-jobs", EQUAL, 1, list_jobs }, { "clear-jobs", EQUAL, 1, daemon_reload }, { "cancel", MORE, 2, cancel_job }, @@ -5389,6 +5775,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], const int r) { { "check", MORE, 2, check_unit_active }, { "is-failed", MORE, 2, check_unit_failed }, { "show", MORE, 1, show }, + { "cat", MORE, 2, cat }, { "status", MORE, 1, show }, { "help", MORE, 2, show }, { "snapshot", LESS, 2, snapshot }, @@ -5420,8 +5807,8 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], const int r) { { "link", MORE, 2, enable_unit }, { "switch-root", MORE, 2, switch_root }, { "list-dependencies", LESS, 2, list_dependencies }, - { "set-default", EQUAL, 2, enable_unit }, - { "get-default", LESS, 1, get_default }, + { "set-default", EQUAL, 2, set_default }, + { "get-default", EQUAL, 1, get_default }, { "set-property", MORE, 3, set_property }, }; @@ -5505,14 +5892,14 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], const int r) { if (((!streq(verbs[i].verb, "reboot") && !streq(verbs[i].verb, "halt") && !streq(verbs[i].verb, "poweroff")) || arg_force <= 0) && !bus) { - log_error("Failed to get D-Bus connection: %s", strerror (-r)); + log_error("Failed to get D-Bus connection: %s", strerror (-bus_error)); return -EIO; } } else { if (!bus && !avoid_bus()) { - log_error("Failed to get D-Bus connection: %s", strerror (-r)); + log_error("Failed to get D-Bus connection: %s", strerror (-bus_error)); return -EIO; } } @@ -5605,11 +5992,9 @@ done: return 0; } -static _noreturn_ void halt_now(enum action a) { +static int halt_now(enum action a) { - _cleanup_free_ char *param = NULL; - - /* Make sure C-A-D is handled by the kernel from this +/* Make sure C-A-D is handled by the kernel from this * point on... */ reboot(RB_ENABLE_CAD); @@ -5618,30 +6003,30 @@ static _noreturn_ void halt_now(enum action a) { case ACTION_HALT: log_info("Halting."); reboot(RB_HALT_SYSTEM); - break; + return -errno; case ACTION_POWEROFF: log_info("Powering off."); reboot(RB_POWER_OFF); - break; + return -errno; - case ACTION_REBOOT: + case ACTION_REBOOT: { + _cleanup_free_ char *param = NULL; - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) == 0) { - log_info("Rebooting with arg '%s'.", param); + 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); - } else { - log_info("Rebooting."); - reboot(RB_AUTOBOOT); } - break; - default: - assert_not_reached("Unknown halt action."); + log_info("Rebooting."); + reboot(RB_AUTOBOOT); + return -errno; } - assert_not_reached("Uh? This shouldn't happen."); + default: + assert_not_reached("Unknown action."); + } } static int halt_main(sd_bus *bus) { @@ -5713,9 +6098,10 @@ static int halt_main(sd_bus *bus) { if (arg_dry) return 0; - halt_now(arg_action); - /* We should never reach this. */ - return -ENOSYS; + r = halt_now(arg_action); + log_error("Failed to reboot: %s", strerror(-r)); + + return r; } static int runlevel_main(void) { @@ -5764,13 +6150,11 @@ int main(int argc, char*argv[]) { goto finish; } - if (!avoid_bus()) { - r = bus_open_transport(arg_transport, arg_host, arg_scope != UNIT_FILE_SYSTEM, &bus); - if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); - goto finish; - } - } + if (!avoid_bus()) + r = bus_open_transport_systemd(arg_transport, arg_host, arg_scope != UNIT_FILE_SYSTEM, &bus); + + /* systemctl_main() will print an error message for the bus + * connection, but only if it needs to */ switch (arg_action) {