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) {
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,
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);
if (r < 0)
return bus_log_parse_error(r);
- return 0;
+ return n;
}
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);
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, 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;
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();
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),
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, r, c = 0;
+ dual_timestamp nw;
+
+ 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;
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);
/* 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;
(a == ACTION_HALT ||
a == ACTION_POWEROFF ||
a == ACTION_REBOOT))
- halt_now(a);
+ return halt_now(a);
if (arg_force >= 1 &&
(a == ACTION_HALT ||
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[] = " ";
/* 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]) {
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) {
"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"
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;
{ "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 },
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;
}
}
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);
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) {
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) {
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) {