X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=737cd67d6814b12f1706e009bbe58c0f3536b9ae;hp=36a305639e9689ae08f02bc7fff384af915b4da9;hb=6ce774fd8068d291e85b94bc630358ce86491b36;hpb=7af5a806d12beec2f4e69ef0e69eb02b315916d7 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 36a305639..737cd67d6 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -135,7 +135,7 @@ 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 void pager_open_if_enabled(void) { @@ -532,13 +532,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 +559,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 +576,7 @@ static int get_listening( if (r < 0) return bus_log_parse_error(r); - return 0; + return n; } struct socket_info { @@ -595,9 +594,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 +676,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; + n = get_unit_list(bus, &reply, &unit_infos); + if (n < 0) + return n; - cu = (unsigned) r; - - 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 +700,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 +714,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 +744,225 @@ 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; + _cleanup_bus_message_unref_ sd_bus_message *reply = 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; @@ -1204,6 +1425,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); @@ -1219,11 +1445,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; @@ -1242,10 +1469,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) { @@ -1935,11 +2164,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; @@ -2028,7 +2257,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 || @@ -2912,8 +3141,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]) { @@ -3616,7 +3848,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; @@ -3754,7 +3986,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) { @@ -3903,7 +4135,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; @@ -4214,7 +4446,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; @@ -4396,6 +4628,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" @@ -5353,7 +5586,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; @@ -5368,6 +5601,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 }, @@ -5504,14 +5738,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; } } @@ -5604,11 +5838,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); @@ -5617,30 +5849,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) { @@ -5712,9 +5944,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) { @@ -5763,13 +5996,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) {