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=7c2d71f68523136776a8357e50e1d17ebe6f6de1;hb=e4c0fbe50c9a4a26efa4ae2b2350215be29b8660;hpb=f459b6025f9368116d8c410376546c157314c205 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 7c2d71f68..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) { @@ -155,6 +157,9 @@ static void ask_password_agent_open_if_enabled(void) { if (arg_scope != UNIT_FILE_SYSTEM) return; + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + ask_password_agent_open(); } @@ -192,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)) @@ -319,18 +324,23 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) { if (!arg_full && original_stdout_is_tty) { unsigned basic_len; + id_len = MIN(max_id_len, 25u); basic_len = 5 + id_len + 5 + active_len + sub_len; + if (job_count) basic_len += job_len + 1; + if (basic_len < (unsigned) columns()) { unsigned extra_len, incr; extra_len = columns() - basic_len; + /* Either UNIT already got 25, or is fully satisfied. * Grant up to 25 to DESC now. */ incr = MIN(extra_len, 25u); desc_len += incr; extra_len -= incr; + /* split the remaining space between UNIT and DESC, * but do not give UNIT more than it needs. */ if (extra_len > 0) { @@ -351,10 +361,15 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) { continue; if (!n_shown && !arg_no_legend) { - printf("%-*s %-*s %-*s %-*s ", id_len, "UNIT", load_len, "LOAD", - active_len, "ACTIVE", sub_len, "SUB"); + printf("%-*s %-*s %-*s %-*s ", + id_len, "UNIT", + load_len, "LOAD", + active_len, "ACTIVE", + sub_len, "SUB"); + if (job_count) printf("%-*s ", job_len, "JOB"); + if (!arg_full && arg_no_pager) printf("%.*s\n", desc_len, "DESCRIPTION"); else @@ -384,6 +399,7 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) { on_active, active_len, u->active_state, sub_len, u->sub_state, off_active, job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); + if (desc_len > 0) printf("%.*s\n", desc_len, u->description); else @@ -518,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, @@ -546,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); @@ -563,7 +578,7 @@ static int get_listening( if (r < 0) return bus_log_parse_error(r); - return 0; + return n; } struct socket_info { @@ -581,10 +596,16 @@ struct socket_info { bool own_triggered; }; -static int socket_info_compare(struct socket_info *a, struct socket_info *b) { - int o = strcmp(a->path, b->path); +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); + return o; } @@ -597,8 +618,8 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { const char *on, *off; for (s = socket_infos; s < socket_infos + cs; s++) { - char **a; unsigned tmp = 0; + char **a; socklen = MAX(socklen, strlen(s->id)); if (arg_show_types) @@ -657,36 +678,35 @@ 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++) { - const char *dot; - _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; - if ((dot = strrchr(u->id, '.')) && !streq(dot+1, "socket")) + if (!endswith(u->id, ".socket")) continue; r = get_triggered_units(bus, u->unit_path, &triggered); 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(); @@ -696,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), @@ -726,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; @@ -756,6 +994,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { max_id_len = sizeof("UNIT FILE")-1; state_cols = sizeof("STATE")-1; + for (u = units; u < units + c; u++) { if (!output_show_unit_file(u)) continue; @@ -766,6 +1005,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (!arg_full) { unsigned basic_cols; + id_cols = MIN(max_id_len, 25u); basic_cols = 1 + id_cols + state_cols; if (basic_cols < (unsigned) columns()) @@ -774,7 +1014,9 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { id_cols = max_id_len; if (!arg_no_legend) - printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE"); + printf("%-*s %-*s\n", + id_cols, "UNIT FILE", + state_cols, "STATE"); for (u = units; u < units + c; u++) { _cleanup_free_ char *e = NULL; @@ -901,12 +1143,13 @@ static int list_unit_files(sd_bus *bus, char **args) { } static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) { - int i; _cleanup_free_ char *n = NULL; - size_t len = 0; size_t max_len = MAX(columns(),20u); + size_t len = 0; + int i; if (!arg_plain) { + for (i = level - 1; i >= 0; i--) { len += 2; if(len > max_len - 3 && !arg_full) { @@ -916,14 +1159,16 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERT : DRAW_TREE_SPACE)); } len += 2; + if(len > max_len - 3 && !arg_full) { printf("%s...\n",max_len % 2 ? "" : " "); return 0; } + printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); } - if(arg_full){ + if (arg_full){ printf("%s\n", name); return 0; } @@ -1031,10 +1276,12 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha static int list_dependencies_compare(const void *_a, const void *_b) { const char **a = (const char**) _a, **b = (const char**) _b; + if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) return 1; if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET) return -1; + return strcasecmp(*a, *b); } @@ -1049,6 +1296,10 @@ static int list_dependencies_one( char **c; int r = 0; + assert(bus); + assert(name); + assert(units); + u = strv_append(*units, name); if (!u) return log_oom(); @@ -1060,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); @@ -1069,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; @@ -1152,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; @@ -1175,6 +1528,11 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n) { pager_open_if_enabled(); + 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); @@ -1190,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; @@ -1213,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) { @@ -1360,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); @@ -1413,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; @@ -1676,10 +2037,8 @@ static int start_unit_one( return log_oom(); r = set_consume(s, p); - if (r < 0) { - log_error("Failed to add path to set."); - return r; - } + if (r < 0) + return log_oom(); } return 0; @@ -1742,8 +2101,7 @@ static int start_unit(sd_bus *bus, char **args) { streq(args[0], "reload-or-restart") ? "ReloadOrRestartUnit" : streq(args[0], "reload-or-try-restart") || - streq(args[0], "condreload") || - + streq(args[0], "condreload") || streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" : "StartUnit"; action = verb_to_action(args[0]); @@ -1752,7 +2110,6 @@ static int start_unit(sd_bus *bus, char **args) { action_table[action].mode ?: arg_job_mode; one_name = action_table[action].target; - } else { assert(arg_action < ELEMENTSOF(action_table)); assert(action_table[arg_action].target); @@ -1873,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; @@ -1910,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; @@ -2003,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 || @@ -2500,7 +2856,7 @@ static void print_status_info( printf(" CGroup: %s\n", i->control_group); - if (arg_transport == BUS_TRANSPORT_LOCAL) { + if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) { unsigned k = 0; pid_t extra[2]; char prefix[] = " "; @@ -2556,11 +2912,11 @@ static void show_unit_help(UnitStatusInfo *i) { STRV_FOREACH(p, i->documentation) { if (startswith(*p, "man:")) { - size_t k; - char *e = NULL; - _cleanup_free_ char *page = NULL, *section = NULL; const char *args[4] = { "man", NULL, NULL, NULL }; + _cleanup_free_ char *page = NULL, *section = NULL; + char *e = NULL; pid_t pid; + size_t k; k = strlen(*p); @@ -2887,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]) { @@ -3245,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( @@ -3271,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( @@ -3285,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; @@ -3296,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); @@ -3310,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; @@ -3320,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; @@ -3344,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) @@ -3591,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; @@ -3729,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) { @@ -3878,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; @@ -4078,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; @@ -4116,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"); @@ -4126,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"; @@ -4160,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"); @@ -4189,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; @@ -4201,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) @@ -4268,9 +4675,10 @@ static int unit_is_enabled(sd_bus *bus, char **args) { UnitFileState state; state = unit_file_get_state(arg_scope, arg_root, *name); - - if (state < 0) + if (state < 0) { + log_error("Failed to get unit file state for %s: %s", *name, strerror(-state)); return state; + } if (state == UNIT_FILE_ENABLED || state == UNIT_FILE_ENABLED_RUNTIME || @@ -4337,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" @@ -4370,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" @@ -4386,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" @@ -4545,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[] = { @@ -4560,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 }, @@ -4651,7 +5059,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* Make sure that if the empty property list was specified, we won't show any properties. */ if (isempty(optarg) && !arg_properties) { - arg_properties = strv_new(NULL, NULL); + arg_properties = new0(char*, 1); if (!arg_properties) return log_oom(); } else { @@ -4700,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; @@ -5291,18 +5703,21 @@ _pure_ static int action_to_runlevel(void) { } static int talk_initctl(void) { - struct init_request request = {}; - int r; + + struct init_request request = { + .magic = INIT_MAGIC, + .sleeptime = 0, + .cmd = INIT_CMD_RUNLVL + }; + _cleanup_close_ int fd = -1; char rl; + int r; rl = action_to_runlevel(); if (!rl) return 0; - request.magic = INIT_MAGIC; - request.sleeptime = 0; - request.cmd = INIT_CMD_RUNLVL; request.runlevel = rl; fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY); @@ -5324,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; @@ -5339,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 }, @@ -5359,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 }, @@ -5390,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 }, }; @@ -5475,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; } } @@ -5491,22 +5908,24 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], const int r) { } static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) { - _cleanup_close_ int fd; + struct sd_shutdown_command c = { .usec = t, .mode = mode, .dry_run = dry_run, .warn_wall = warn, }; + union sockaddr_union sockaddr = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/shutdownd", }; - struct iovec iovec[2] = { - {.iov_base = (char*) &c, + + struct iovec iovec[2] = {{ + .iov_base = (char*) &c, .iov_len = offsetof(struct sd_shutdown_command, wall_message), - } - }; + }}; + struct msghdr msghdr = { .msg_name = &sockaddr, .msg_namelen = offsetof(struct sockaddr_un, sun_path) @@ -5515,6 +5934,8 @@ static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const ch .msg_iovlen = 1, }; + _cleanup_close_ int fd; + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; @@ -5571,11 +5992,9 @@ done: return 0; } -static _noreturn_ void halt_now(enum action a) { - - _cleanup_free_ char *param = NULL; +static int halt_now(enum action a) { - /* 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); @@ -5584,29 +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: - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) == 0) { - log_info("Rebooting with arg '%s'.", param); + case ACTION_REBOOT: { + _cleanup_free_ char *param = NULL; + + 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) { @@ -5639,6 +6059,9 @@ static int halt_main(sd_bus *bus) { _cleanup_free_ char *m; m = strv_join(arg_wall, " "); + if (!m) + return log_oom(); + r = send_shutdownd(arg_when, arg_action == ACTION_HALT ? 'H' : arg_action == ACTION_POWEROFF ? 'P' : @@ -5675,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) { @@ -5726,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) {