X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=ddf46b66d53a9c7305127bc32266fd3642f0e8a6;hp=0e6b2ec30c33aefb4b87b4e530ff3f72e8ae66fc;hb=a1074881b43416018aef2fb8f62ef62f92f0bae7;hpb=222d0348f97aa132cc24ed3a38f18463e9b0e8c9 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 0e6b2ec30..b75f335ae 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -20,8 +20,11 @@ ***/ #include +#include +#include #include #include +#include #include #include #include @@ -37,6 +40,7 @@ #include #include +#include #include "log.h" #include "util.h" @@ -63,29 +67,38 @@ #include "install.h" #include "logs-show.h" #include "path-util.h" +#include "socket-util.h" +#include "fileio.h" -static const char *arg_type = NULL; -static char **arg_property = NULL; +static char **arg_types = NULL; +static char **arg_states = NULL; +static char **arg_properties = NULL; static bool arg_all = false; +static bool original_stdout_is_tty; +static enum dependency { + DEPENDENCY_FORWARD, + DEPENDENCY_REVERSE, + DEPENDENCY_AFTER, + DEPENDENCY_BEFORE, +} arg_dependency = DEPENDENCY_FORWARD; static const char *arg_job_mode = "replace"; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; static bool arg_no_block = false; static bool arg_no_legend = false; static bool arg_no_pager = false; static bool arg_no_wtmp = false; -static bool arg_no_sync = false; static bool arg_no_wall = false; static bool arg_no_reload = false; +static bool arg_show_types = false; +static bool arg_ignore_inhibitors = false; static bool arg_dry = false; static bool arg_quiet = false; static bool arg_full = false; static int arg_force = 0; static bool arg_ask_password = true; -static bool arg_failed = false; static bool arg_runtime = false; static char **arg_wall = NULL; static const char *arg_kill_who = NULL; -static const char *arg_kill_mode = NULL; static int arg_signal = SIGTERM; static const char *arg_root = NULL; static usec_t arg_when = 0; @@ -99,6 +112,7 @@ static enum action { ACTION_EXIT, ACTION_SUSPEND, ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, ACTION_RUNLEVEL2, ACTION_RUNLEVEL3, ACTION_RUNLEVEL4, @@ -112,50 +126,28 @@ static enum action { ACTION_CANCEL_SHUTDOWN, _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; -static enum dot { - DOT_ALL, - DOT_ORDER, - DOT_REQUIRE -} arg_dot = DOT_ALL; static enum transport { TRANSPORT_NORMAL, TRANSPORT_SSH, TRANSPORT_POLKIT } arg_transport = TRANSPORT_NORMAL; -static const char *arg_host = NULL; -static bool arg_follow = false; +static char *arg_host = NULL; +static char *arg_user = NULL; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; +static bool arg_plain = false; static bool private_bus = false; static int daemon_reload(DBusConnection *bus, char **args); static void halt_now(enum action a); -static bool on_tty(void) { - static int t = -1; - - /* Note that this is invoked relatively early, before we start - * the pager. That means the value we return reflects whether - * we originally were started on a tty, not if we currently - * are. But this is intended, since we want colour and so on - * when run in our own pager. */ - - if (_unlikely_(t < 0)) - t = isatty(STDOUT_FILENO) > 0; - - return t; -} - static void pager_open_if_enabled(void) { - /* Cache result before we open the pager */ - on_tty(); - if (arg_no_pager) return; - pager_open(); + pager_open(false); } static void ask_password_agent_open_if_enabled(void) { @@ -186,37 +178,6 @@ static void polkit_agent_open_if_enabled(void) { } #endif -static const char *ansi_highlight_red(bool b) { - - if (!on_tty()) - return ""; - - return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF; -} - -static const char *ansi_highlight_green(bool b) { - - if (!on_tty()) - return ""; - - return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF; -} - -static bool error_is_no_service(const DBusError *error) { - assert(error); - - if (!dbus_error_is_set(error)) - return false; - - if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER)) - return true; - - if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN)) - return true; - - return startswith(error->name, "org.freedesktop.DBus.Error.Spawn."); -} - static int translate_bus_error_to_exit_status(int r, const DBusError *error) { assert(error); @@ -247,33 +208,31 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) { static void warn_wall(enum action a) { static const char *table[_ACTION_MAX] = { - [ACTION_HALT] = "The system is going down for system halt NOW!", - [ACTION_REBOOT] = "The system is going down for reboot NOW!", - [ACTION_POWEROFF] = "The system is going down for power-off NOW!", - [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!", - [ACTION_RESCUE] = "The system is going down to rescue mode NOW!", - [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!" + [ACTION_HALT] = "The system is going down for system halt NOW!", + [ACTION_REBOOT] = "The system is going down for reboot NOW!", + [ACTION_POWEROFF] = "The system is going down for power-off NOW!", + [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!", + [ACTION_RESCUE] = "The system is going down to rescue mode NOW!", + [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!", + [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!" }; if (arg_no_wall) return; if (arg_wall) { - char *p; + _cleanup_free_ char *p; p = strv_join(arg_wall, " "); if (!p) { - log_error("Failed to join strings."); + log_oom(); return; } if (*p) { utmp_wall(p, NULL); - free(p); return; } - - free(p); } if (!table[a]) @@ -299,19 +258,6 @@ static bool avoid_bus(void) { return false; } -struct unit_info { - const char *id; - const char *description; - const char *load_state; - const char *active_state; - const char *sub_state; - const char *following; - const char *unit_path; - uint32_t job_id; - const char *job_type; - const char *job_path; -}; - static int compare_unit_info(const void *a, const void *b) { const char *d1, *d2; const struct unit_info *u = a, *v = b; @@ -322,7 +268,8 @@ static int compare_unit_info(const void *a, const void *b) { if (d1 && d2) { int r; - if ((r = strcasecmp(d1, d2)) != 0) + r = strcasecmp(d1, d2); + if (r != 0) return r; } @@ -332,22 +279,26 @@ static int compare_unit_info(const void *a, const void *b) { static bool output_show_unit(const struct unit_info *u) { const char *dot; - if (arg_failed) - return streq(u->active_state, "failed"); + if (!strv_isempty(arg_states)) + return strv_contains(arg_states, u->load_state) || strv_contains(arg_states, u->sub_state) || strv_contains(arg_states, u->active_state); - return (!arg_type || ((dot = strrchr(u->id, '.')) && - streq(dot+1, arg_type))) && - (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0); + return (!arg_types || ((dot = strrchr(u->id, '.')) && + strv_find(arg_types, dot+1))) && + (arg_all || !(streq(u->active_state, "inactive") + || u->following[0]) || u->job_id > 0); } static void output_units_list(const struct unit_info *unit_infos, unsigned c) { - unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0; + unsigned id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len; + unsigned n_shown = 0; const struct unit_info *u; + int job_count = 0; - max_id_len = sizeof("UNIT")-1; - active_len = sizeof("ACTIVE")-1; - sub_len = sizeof("SUB")-1; - job_len = sizeof("JOB")-1; + max_id_len = strlen("UNIT"); + load_len = strlen("LOAD"); + active_len = strlen("ACTIVE"); + sub_len = strlen("SUB"); + job_len = strlen("JOB"); desc_len = 0; for (u = unit_infos; u < unit_infos + c; u++) { @@ -355,22 +306,27 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { continue; max_id_len = MAX(max_id_len, strlen(u->id)); + load_len = MAX(load_len, strlen(u->load_state)); active_len = MAX(active_len, strlen(u->active_state)); sub_len = MAX(sub_len, strlen(u->sub_state)); - if (u->job_id != 0) + if (u->job_id != 0) { job_len = MAX(job_len, strlen(u->job_type)); + job_count++; + } } - if (!arg_full) { + if (!arg_full && original_stdout_is_tty) { unsigned basic_len; - id_len = MIN(max_id_len, 25); - basic_len = 5 + id_len + 6 + active_len + sub_len + job_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, 25); + incr = MIN(extra_len, 25u); desc_len += incr; extra_len -= incr; /* split the remaining space between UNIT and DESC, @@ -384,169 +340,418 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { } else id_len = max_id_len; - if (!arg_no_legend) { - printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD", - active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB"); - if (!arg_full && arg_no_pager) - printf("%.*s\n", desc_len, "DESCRIPTION"); - else - printf("%s\n", "DESCRIPTION"); - } - for (u = unit_infos; u < unit_infos + c; u++) { - char *e; - const char *on_loaded, *off_loaded; - const char *on_active, *off_active; + _cleanup_free_ char *e = NULL; + const char *on_loaded, *off_loaded, *on = ""; + const char *on_active, *off_active, *off = ""; if (!output_show_unit(u)) continue; + if (!n_shown && !arg_no_legend) { + 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 + printf("%s\n", "DESCRIPTION"); + } + n_shown++; - if (streq(u->load_state, "error")) { - on_loaded = ansi_highlight_red(true); - off_loaded = ansi_highlight_red(false); + if (streq(u->load_state, "error") || + streq(u->load_state, "not-found")) { + on_loaded = on = ansi_highlight_red(); + off_loaded = off = ansi_highlight_off(); } else on_loaded = off_loaded = ""; if (streq(u->active_state, "failed")) { - on_active = ansi_highlight_red(true); - off_active = ansi_highlight_red(false); + on_active = on = ansi_highlight_red(); + off_active = off = ansi_highlight_off(); } else on_active = off_active = ""; e = arg_full ? NULL : ellipsize(u->id, id_len, 33); - printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ", - id_len, e ? e : u->id, - on_loaded, u->load_state, off_loaded, + printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s", + on, id_len, e ? e : u->id, off, + on_loaded, load_len, u->load_state, off_loaded, on_active, active_len, u->active_state, sub_len, u->sub_state, off_active, - job_len, u->job_id ? u->job_type : ""); - if (!arg_full && arg_no_pager) + job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); + if (desc_len > 0) printf("%.*s\n", desc_len, u->description); else printf("%s\n", u->description); - - free(e); } if (!arg_no_legend) { - printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n" - "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" - "SUB = The low-level unit activation state, values depend on unit type.\n" - "JOB = Pending job for the unit.\n"); + const char *on, *off; + + if (n_shown) { + printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n" + "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" + "SUB = The low-level unit activation state, values depend on unit type.\n"); + if (job_count) + printf("JOB = Pending job for the unit.\n"); + puts(""); + on = ansi_highlight(); + off = ansi_highlight_off(); + } else { + on = ansi_highlight_red(); + off = ansi_highlight_off(); + } if (arg_all) - printf("\n%u units listed.\n", n_shown); + printf("%s%u loaded units listed.%s\n" + "To show all installed unit files use 'systemctl list-unit-files'.\n", + on, n_shown, off); else - printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown); + printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n" + "To show all installed unit files use 'systemctl list-unit-files'.\n", + on, n_shown, off); } } -static int list_units(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int r; - DBusMessageIter iter, sub, sub2; - unsigned c = 0, n_units = 0; - struct unit_info *unit_infos = NULL; +static int get_unit_list( + DBusConnection *bus, + DBusMessage **reply, + struct unit_info **unit_infos, + unsigned *c) { - dbus_error_init(&error); + DBusMessageIter iter, sub; + size_t size = 0; + int r; assert(bus); + assert(unit_infos); + assert(c); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(*reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + if (!GREEDY_REALLOC(*unit_infos, size, *c + 1)) + return log_oom(); + + bus_parse_unit_info(&sub, *unit_infos + *c); + (*c)++; + + dbus_message_iter_next(&sub); + } + + return 0; +} + +static int list_units(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ struct unit_info *unit_infos = NULL; + unsigned c = 0; + int r; pager_open_if_enabled(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits"))) { - log_error("Could not allocate message."); - return -ENOMEM; + r = get_unit_list(bus, &reply, &unit_infos, &c); + if (r < 0) + return r; + + qsort_safe(unit_infos, c, sizeof(struct unit_info), compare_unit_info); + + output_units_list(unit_infos, c); + + return 0; +} + +static int get_triggered_units( + DBusConnection *bus, + const char* unit_path, + char*** triggered) { + + const char *interface = "org.freedesktop.systemd1.Unit", + *triggers_property = "Triggers"; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + int r; + + r = bus_method_call_with_reply(bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &triggers_property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EBADMSG; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_recurse(&sub, &iter); + sub = iter; + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *unit; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); + return -EBADMSG; + } + + dbus_message_iter_get_basic(&sub, &unit); + r = strv_extend(triggered, unit); + if (r < 0) + return r; + + dbus_message_iter_next(&sub); } + return 0; +} + +static int get_listening(DBusConnection *bus, const char* unit_path, + char*** listen, unsigned *c) +{ + const char *interface = "org.freedesktop.systemd1.Socket", + *listen_property = "Listen"; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + int r; + + r = bus_method_call_with_reply(bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &listen_property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + if (!dbus_message_iter_init(reply, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EBADMSG; } dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_recurse(&sub, &iter); + sub = iter; while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - struct unit_info *u; + DBusMessageIter sub2; + const char *type, *path; if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EBADMSG; } - if (c >= n_units) { - struct unit_info *w; + dbus_message_iter_recurse(&sub, &sub2); - n_units = MAX(2*c, 16); - w = realloc(unit_infos, sizeof(struct unit_info) * n_units); + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0) { + r = strv_extend(listen, type); + if (r < 0) + return r; - if (!w) { - log_error("Failed to allocate unit array."); - r = -ENOMEM; - goto finish; - } + r = strv_extend(listen, path); + if (r < 0) + return r; - unit_infos = w; + (*c) ++; } - u = unit_infos+c; + dbus_message_iter_next(&sub); + } - dbus_message_iter_recurse(&sub, &sub2); + return 0; +} - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; +struct socket_info { + const char* id; + + char* type; + char* path; + + /* Note: triggered is a list here, although it almost certainly + * will always be one unit. Nevertheless, dbus API allows for multiple + * values, so let's follow that.*/ + char** triggered; + + /* The strv above is shared. free is set only in the first one. */ + bool own_triggered; +}; + +static int socket_info_compare(struct socket_info *a, struct socket_info *b) { + int o = strcmp(a->path, b->path); + if (o == 0) + o = strcmp(a->type, b->type); + return o; +} + +static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { + struct socket_info *s; + unsigned pathlen = sizeof("LISTEN") - 1, + typelen = (sizeof("TYPE") - 1) * arg_show_types, + socklen = sizeof("UNIT") - 1, + servlen = sizeof("ACTIVATES") - 1; + const char *on, *off; + + for (s = socket_infos; s < socket_infos + cs; s++) { + char **a; + unsigned tmp = 0; + + socklen = MAX(socklen, strlen(s->id)); + if (arg_show_types) + typelen = MAX(typelen, strlen(s->type)); + pathlen = MAX(pathlen, strlen(s->path)); + + STRV_FOREACH(a, s->triggered) + tmp += strlen(*a) + 2*(a != s->triggered); + servlen = MAX(servlen, tmp); + } + + if (cs) { + if (!arg_no_legend) + printf("%-*s %-*.*s%-*s %s\n", + pathlen, "LISTEN", + typelen + arg_show_types, typelen + arg_show_types, "TYPE ", + socklen, "UNIT", + "ACTIVATES"); + + for (s = socket_infos; s < socket_infos + cs; s++) { + char **a; + + if (arg_show_types) + printf("%-*s %-*s %-*s", + pathlen, s->path, typelen, s->type, socklen, s->id); + else + printf("%-*s %-*s", + pathlen, s->path, socklen, s->id); + STRV_FOREACH(a, s->triggered) + printf("%s %s", + a == s->triggered ? "" : ",", *a); + printf("\n"); } - dbus_message_iter_next(&sub); - c++; + on = ansi_highlight(); + off = ansi_highlight_off(); + if (!arg_no_legend) + printf("\n"); + } else { + on = ansi_highlight_red(); + off = ansi_highlight_off(); } - if (c > 0) { - qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); - output_units_list(unit_infos, c); + if (!arg_no_legend) { + printf("%s%u sockets listed.%s\n", on, cs, off); + if (!arg_all) + printf("Pass --all to see loaded but inactive sockets, too.\n"); } - r = 0; + return 0; +} -finish: - if (m) - dbus_message_unref(m); +static int list_sockets(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ struct unit_info *unit_infos = NULL; + struct socket_info *socket_infos = NULL; + const struct unit_info *u; + struct socket_info *s; + unsigned cu = 0, cs = 0; + size_t size = 0; + int r; + + pager_open_if_enabled(); - if (reply) - dbus_message_unref(reply); + r = get_unit_list(bus, &reply, &unit_infos, &cu); + if (r < 0) + return r; - free(unit_infos); + for (u = unit_infos; u < unit_infos + cu; u++) { + const char *dot; + _cleanup_strv_free_ char **listen = NULL, **triggered = NULL; + unsigned c = 0, i; - dbus_error_free(&error); + if (!output_show_unit(u)) + continue; + + if ((dot = strrchr(u->id, '.')) && !streq(dot+1, "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) + goto cleanup; + + if (!GREEDY_REALLOC(socket_infos, size, cs + c)) { + r = log_oom(); + goto cleanup; + } + + 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], + .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 */ + } + + qsort_safe(socket_infos, cs, sizeof(struct socket_info), + (__compar_fn_t) socket_info_compare); + + output_sockets_list(socket_infos, cs); + + cleanup: + assert(cs == 0 || socket_infos); + for (s = socket_infos; s < socket_infos + cs; s++) { + free(s->type); + free(s->path); + if (s->own_triggered) + strv_free(s->triggered); + } + free(socket_infos); return r; } @@ -572,7 +777,7 @@ static int compare_unit_file_list(const void *a, const void *b) { static bool output_show_unit_file(const UnitFileList *u) { const char *dot; - return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type)); + return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1)); } static void output_unit_file_list(const UnitFileList *units, unsigned c) { @@ -591,7 +796,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (!arg_full) { unsigned basic_cols; - id_cols = MIN(max_id_len, 25); + id_cols = MIN(max_id_len, 25u); basic_cols = 1 + id_cols + state_cols; if (basic_cols < (unsigned) columns()) id_cols += MIN(columns() - basic_cols, max_id_len - id_cols); @@ -602,7 +807,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE"); for (u = units; u < units + c; u++) { - char *e; + _cleanup_free_ char *e = NULL; const char *on, *off; const char *id; @@ -613,12 +818,13 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (u->state == UNIT_FILE_MASKED || u->state == UNIT_FILE_MASKED_RUNTIME || - u->state == UNIT_FILE_DISABLED) { - on = ansi_highlight_red(true); - off = ansi_highlight_red(false); + u->state == UNIT_FILE_DISABLED || + u->state == UNIT_FILE_INVALID) { + on = ansi_highlight_red(); + off = ansi_highlight_off(); } else if (u->state == UNIT_FILE_ENABLED) { - on = ansi_highlight_green(true); - off = ansi_highlight_green(false); + on = ansi_highlight_green(); + off = ansi_highlight_off(); } else on = off = ""; @@ -629,8 +835,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { printf("%-*s %s%-*s%s\n", id_cols, e ? e : id, on, state_cols, unit_file_state_to_string(u->state), off); - - free(e); } if (!arg_no_legend) @@ -638,14 +842,11 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } static int list_unit_files(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int r; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ UnitFileList *units = NULL; DBusMessageIter iter, sub, sub2; unsigned c = 0, n_units = 0; - UnitFileList *units = NULL; - - dbus_error_init(&error); + int r; pager_open_if_enabled(); @@ -655,10 +856,8 @@ static int list_unit_files(DBusConnection *bus, char **args) { Iterator i; h = hashmap_new(string_hash_func, string_compare_func); - if (!h) { - log_error("Out of memory"); - return -ENOMEM; - } + if (!h) + return log_oom(); r = unit_file_get_list(arg_scope, arg_root, h); if (r < 0) { @@ -671,8 +870,7 @@ static int list_unit_files(DBusConnection *bus, char **args) { units = new(UnitFileList, n_units); if (!units) { unit_file_list_free(h); - log_error("Out of memory"); - return -ENOMEM; + return log_oom(); } HASHMAP_FOREACH(u, h, i) { @@ -682,31 +880,23 @@ static int list_unit_files(DBusConnection *bus, char **args) { hashmap_free(h); } else { - assert(bus); - - m = dbus_message_new_method_call( + r = bus_method_call_with_reply( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "ListUnitFiles"); - if (!m) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + "ListUnitFiles", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&iter, &sub); @@ -715,36 +905,27 @@ static int list_unit_files(DBusConnection *bus, char **args) { UnitFileList *u; const char *state; - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT); if (c >= n_units) { UnitFileList *w; - n_units = MAX(2*c, 16); + n_units = MAX(2*c, 16u); w = realloc(units, sizeof(struct UnitFileList) * n_units); - - if (!w) { - log_error("Failed to allocate unit array."); - r = -ENOMEM; - goto finish; - } + if (!w) + return log_oom(); units = w; } - u = units+c; + u = units + c; dbus_message_iter_recurse(&sub, &sub2); if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } u->state = unit_file_state_from_string(state); @@ -759,119 +940,95 @@ static int list_unit_files(DBusConnection *bus, char **args) { output_unit_file_list(units, c); } - r = 0; - -finish: - if (m) - dbus_message_unref(m); + return 0; +} - if (reply) - dbus_message_unref(reply); +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); + + if (!arg_plain) { + for (i = level - 1; i >= 0; i--) { + len += 2; + if(len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; + } + 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)); + } - free(units); + if(arg_full){ + printf("%s\n", name); + return 0; + } - dbus_error_free(&error); + n = ellipsize(name, max_len-len, 100); + if(!n) + return log_oom(); - return r; + printf("%s\n", n); + return 0; } -static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) { - static const char * const colors[] = { - "Requires", "[color=\"black\"]", - "RequiresOverridable", "[color=\"black\"]", - "Requisite", "[color=\"darkblue\"]", - "RequisiteOverridable", "[color=\"darkblue\"]", - "Wants", "[color=\"darkgrey\"]", - "Conflicts", "[color=\"red\"]", - "ConflictedBy", "[color=\"red\"]", - "After", "[color=\"green\"]" +static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) { + static const char *dependencies[] = { + [DEPENDENCY_FORWARD] = "Requires\0" + "RequiresOverridable\0" + "Requisite\0" + "RequisiteOverridable\0" + "Wants\0", + [DEPENDENCY_REVERSE] = "RequiredBy\0" + "RequiredByOverridable\0" + "WantedBy\0" + "PartOf\0", + [DEPENDENCY_AFTER] = "After\0", + [DEPENDENCY_BEFORE] = "Before\0", }; - const char *c = NULL; - unsigned i; + _cleanup_free_ char *path; + const char *interface = "org.freedesktop.systemd1.Unit"; - assert(name); - assert(prop); - assert(iter); - - for (i = 0; i < ELEMENTSOF(colors); i += 2) - if (streq(colors[i], prop)) { - c = colors[i+1]; - break; - } - - if (!c) - return 0; - - if (arg_dot != DOT_ALL) - if ((arg_dot == DOT_ORDER) != streq(prop, "After")) - return 0; - - switch (dbus_message_iter_get_arg_type(iter)) { - - case DBUS_TYPE_ARRAY: - - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; - - dbus_message_iter_recurse(iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); - dbus_message_iter_get_basic(&sub, &s); - printf("\t\"%s\"->\"%s\" %s;\n", name, s, c); - - dbus_message_iter_next(&sub); - } - - return 0; - } - } - - return 0; -} - -static int dot_one(DBusConnection *bus, const char *name, const char *path) { - DBusMessage *m = NULL, *reply = NULL; - const char *interface = "org.freedesktop.systemd1.Unit"; - int r; - DBusError error; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2, sub3; - assert(bus); - assert(path); - - dbus_error_init(&error); + int r = 0; + char **ret = NULL; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + assert(bus); + assert(name); + assert(deps); - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; + path = unit_dbus_path_from_name(name); + if (path == NULL) { + r = -EINVAL; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { log_error("Failed to parse reply."); r = -EIO; goto finish; @@ -882,12 +1039,7 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) { while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { const char *prop; - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } - + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY); dbus_message_iter_recurse(&sub, &sub2); if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) { @@ -896,179 +1048,283 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) { goto finish; } - if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { log_error("Failed to parse reply."); r = -EIO; goto finish; } dbus_message_iter_recurse(&sub2, &sub3); - - if (dot_one_property(name, prop, &sub3)) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } - dbus_message_iter_next(&sub); - } - r = 0; + assert(arg_dependency < ELEMENTSOF(dependencies)); + if (!nulstr_contains(dependencies[arg_dependency], prop)) + continue; -finish: - if (m) - dbus_message_unref(m); + if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) { + if (dbus_message_iter_get_element_type(&sub3) == DBUS_TYPE_STRING) { + DBusMessageIter sub4; + dbus_message_iter_recurse(&sub3, &sub4); - if (reply) - dbus_message_unref(reply); + while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) { + const char *s; - dbus_error_free(&error); + assert(dbus_message_iter_get_arg_type(&sub4) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub4, &s); + + r = strv_extend(&ret, s); + if (r < 0) { + log_oom(); + goto finish; + } + dbus_message_iter_next(&sub4); + } + } + } + } +finish: + if (r < 0) + strv_free(ret); + else + *deps = ret; return r; } -static int dot(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int r; - DBusMessageIter iter, sub, sub2; +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); +} - dbus_error_init(&error); +static int list_dependencies_one(DBusConnection *bus, const char *name, int level, char ***units, unsigned int branches) { + _cleanup_strv_free_ char **deps = NULL, **u; + char **c; + int r = 0; - assert(bus); + u = strv_append(*units, name); + if (!u) + return log_oom(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + return r; - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); - if (!dbus_message_iter_init(reply, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; + STRV_FOREACH(c, deps) { + if (strv_contains(u, *c)) { + if (!arg_plain) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); + if (r < 0) + return r; + } + continue; + } + + r = list_dependencies_print(*c, level, branches, c[1] == NULL); + if (r < 0) + return r; + + if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) { + r = list_dependencies_one(bus, *c, level + 1, &u, (branches << 1) | (c[1] == NULL ? 0 : 1)); + if(r < 0) + return r; + } + } + if (arg_plain) { + strv_free(*units); + *units = u; + u = NULL; } + return 0; +} - printf("digraph systemd {\n"); +static int list_dependencies(DBusConnection *bus, char **args) { + _cleanup_free_ char *unit = NULL; + _cleanup_strv_free_ char **units = NULL; + const char *u; - dbus_message_iter_recurse(&iter, &sub); - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path; + assert(bus); - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } + if (args[1]) { + unit = unit_name_mangle(args[1]); + if (!unit) + return log_oom(); + u = unit; + } else + u = SPECIAL_DEFAULT_TARGET; - dbus_message_iter_recurse(&sub, &sub2); + pager_open_if_enabled(); - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 || - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) { - log_error("Failed to parse reply."); - r = -EIO; + puts(u); + + return list_dependencies_one(bus, u, 0, &units, 0); +} + +static int get_default(DBusConnection *bus, char **args) { + char *path = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + int r; + _cleanup_dbus_error_free_ DBusError error; + + dbus_error_init(&error); + + if (!bus || avoid_bus()) { + r = unit_file_get_default(arg_scope, arg_root, &path); + + if (r < 0) { + log_error("Operation failed: %s", strerror(-r)); goto finish; } - if ((r = dot_one(bus, id, unit_path)) < 0) + r = 0; + } else { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetDefaultTarget", + &reply, + NULL, + DBUS_TYPE_INVALID); + + if (r < 0) { + log_error("Operation failed: %s", strerror(-r)); goto finish; + } - /* printf("\t\"%s\";\n", id); */ - dbus_message_iter_next(&sub); + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } } - printf("}\n"); + if (path) + printf("%s\n", path); - log_info(" Color legend: black = Requires\n" - " dark blue = Requisite\n" - " dark grey = Wants\n" - " red = Conflicts\n" - " green = After\n"); +finish: + if ((!bus || avoid_bus()) && path) + free(path); - if (on_tty()) - log_notice("-- You probably want to process this output with graphviz' dot tool.\n" - "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n"); + return r; - r = 0; +} -finish: - if (m) - dbus_message_unref(m); +struct job_info { + uint32_t id; + char *name, *type, *state; +}; - if (reply) - dbus_message_unref(reply); +static void list_jobs_print(struct job_info* jobs, size_t n) { + size_t i; + struct job_info *j; + const char *on, *off; + bool shorten = false; - dbus_error_free(&error); + assert(n == 0 || jobs); - return r; -} + if (n == 0) { + on = ansi_highlight_green(); + off = ansi_highlight_off(); -static int list_jobs(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int r; - DBusMessageIter iter, sub, sub2; - unsigned k = 0; + printf("%sNo jobs running.%s\n", on, off); + return; + } - dbus_error_init(&error); + pager_open_if_enabled(); - assert(bus); + { + /* JOB UNIT TYPE STATE */ + unsigned l0 = 3, l1 = 4, l2 = 4, l3 = 5; - pager_open_if_enabled(); + for (i = 0, j = jobs; i < n; i++, j++) { + assert(j->name && j->type && j->state); + l0 = MAX(l0, DECIMAL_STR_WIDTH(j->id)); + l1 = MAX(l1, strlen(j->name)); + l2 = MAX(l2, strlen(j->type)); + l3 = MAX(l3, strlen(j->state)); + } - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListJobs"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } + if (!arg_full && l0 + 1 + l1 + l2 + 1 + l3 > columns()) { + l1 = MAX(33u, columns() - l0 - l2 - l3 - 3); + shorten = true; + } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + if (on_tty()) + printf("%*s %-*s %-*s %-*s\n", + l0, "JOB", + l1, "UNIT", + l2, "TYPE", + l3, "STATE"); + + for (i = 0, j = jobs; i < n; i++, j++) { + _cleanup_free_ char *e = NULL; + + if (streq(j->state, "running")) { + on = ansi_highlight(); + off = ansi_highlight_off(); + } else + on = off = ""; + + e = shorten ? ellipsize(j->name, l1, 33) : NULL; + printf("%*u %s%-*s%s %-*s %s%-*s%s\n", + l0, j->id, + on, l1, e ? e : j->name, off, + l2, j->type, + on, l3, j->state, off); + } } + on = ansi_highlight(); + off = ansi_highlight_off(); + + if (on_tty()) + printf("\n%s%zu jobs listed%s.\n", on, n, off); +} + +static int list_jobs(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub, sub2; + int r; + struct job_info *jobs = NULL; + size_t size = 0, used = 0; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListJobs", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&iter, &sub); - if (on_tty()) - printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE"); - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { const char *name, *type, *state, *job_path, *unit_path; uint32_t id; - char *e; if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&sub, &sub2); @@ -1084,266 +1340,155 @@ static int list_jobs(DBusConnection *bus, char **args) { goto finish; } - e = arg_full ? NULL : ellipsize(name, 25, 33); - printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state); - free(e); - - k++; - - dbus_message_iter_next(&sub); - } - - if (on_tty()) - printf("\n%u jobs listed.\n", k); - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; -} - -static int load_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL; - DBusError error; - int r; - char **name; - - dbus_error_init(&error); - - assert(bus); - assert(args); - - STRV_FOREACH(name, args+1) { - DBusMessage *reply; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LoadUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; + if (!GREEDY_REALLOC(jobs, size, used + 1)) { + r = log_oom(); goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; + jobs[used++] = (struct job_info) { id, + strdup(name), + strdup(type), + strdup(state) }; + if (!jobs[used-1].name || !jobs[used-1].type || !jobs[used-1].state) { + r = log_oom(); goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - dbus_message_unref(reply); - - m = reply = NULL; + dbus_message_iter_next(&sub); } - r = 0; + list_jobs_print(jobs, used); -finish: - if (m) - dbus_message_unref(m); - - dbus_error_free(&error); + finish: + while (used--) { + free(jobs[used].name); + free(jobs[used].type); + free(jobs[used].state); + } + free(jobs); - return r; + return 0; } static int cancel_job(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int r; char **name; - dbus_error_init(&error); - - assert(bus); assert(args); if (strv_length(args) <= 1) return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { - unsigned id; - const char *path; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetJob"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + uint32_t id; + int r; - if ((r = safe_atou(*name, &id)) < 0) { + r = safe_atou32(*name, &id); + if (r < 0) { log_error("Failed to parse job id: %s", strerror(-r)); - goto finish; - } - - assert_cc(sizeof(uint32_t) == sizeof(id)); - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &id, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Job", - "Cancel"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + return r; } - dbus_message_unref(m); - dbus_message_unref(reply); - m = reply = NULL; + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "CancelJob", + NULL, + NULL, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_INVALID); + if (r < 0) + return r; } - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } -static bool need_daemon_reload(DBusConnection *bus, const char *unit) { - DBusMessage *m = NULL, *reply = NULL; +static int need_daemon_reload(DBusConnection *bus, const char *unit) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_dbus_error_free_ DBusError error; dbus_bool_t b = FALSE; DBusMessageIter iter, sub; const char *interface = "org.freedesktop.systemd1.Unit", *property = "NeedDaemonReload", *path; + _cleanup_free_ char *n = NULL; + int r; - /* We ignore all errors here, since this is used to show a warning only */ + dbus_error_init(&error); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"))) - goto finish; + /* We ignore all errors here, since this is used to show a warning only */ - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &unit, - DBUS_TYPE_INVALID)) - goto finish; + n = unit_name_mangle(unit); + if (!n) + return log_oom(); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL))) - goto finish; + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + &reply, + &error, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) - goto finish; - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"))) - goto finish; - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - goto finish; - } + return -EIO; dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL))) - goto finish; + reply = NULL; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) - goto finish; + return -EIO; dbus_message_iter_recurse(&iter, &sub); - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) - goto finish; + return -EIO; dbus_message_iter_get_basic(&sub, &b); - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - return b; } typedef struct WaitData { Set *set; + + char *name; char *result; } WaitData; static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) { - DBusError error; + _cleanup_dbus_error_free_ DBusError error; WaitData *d = data; + dbus_error_init(&error); + assert(connection); assert(message); assert(d); - dbus_error_init(&error); - log_debug("Got D-Bus request: %s.%s() on %s", dbus_message_get_interface(message), dbus_message_get_member(message), @@ -1356,7 +1501,7 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) { uint32_t id; const char *path, *result, *unit; - dbus_bool_t success = true; + char *r; if (dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &id, @@ -1364,64 +1509,47 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me DBUS_TYPE_STRING, &unit, DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID)) { - char *p; - p = set_remove(d->set, (char*) path); - free(p); + r = set_remove(d->set, (char*) path); + if (!r) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (*result) + free(r); + + if (!isempty(result)) d->result = strdup(result); - goto finish; + if (!isempty(unit)) + d->name = strdup(unit); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -#ifndef LEGACY +#ifndef NOLEGACY dbus_error_free(&error); if (dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &id, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID)) { - char *p; - /* Compatibility with older systemd versions < * 183 during upgrades. This should be dropped * one day. */ - p = set_remove(d->set, (char*) path); - free(p); + r = set_remove(d->set, (char*) path); + if (!r) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + free(r); if (*result) d->result = strdup(result); - goto finish; - } - - dbus_error_free(&error); - if (dbus_message_get_args(message, &error, - DBUS_TYPE_UINT32, &id, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_BOOLEAN, &success, - DBUS_TYPE_INVALID)) { - char *p; - - /* Compatibility with older systemd versions < - * 19 during upgrades. This should be dropped - * one day */ - - p = set_remove(d->set, (char*) path); - free(p); - - if (!success) - d->result = strdup("failed"); - - goto finish; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } #endif log_error("Failed to parse message: %s", bus_error_message(&error)); } -finish: - dbus_error_free(&error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1453,216 +1581,130 @@ static int enable_wait_for_jobs(DBusConnection *bus) { } static int wait_for_jobs(DBusConnection *bus, Set *s) { - int r; - WaitData d; + int r = 0; + WaitData d = { .set = s }; assert(bus); assert(s); - zero(d); - d.set = s; - - if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) { - log_error("Failed to add filter."); - r = -ENOMEM; - goto finish; - } - - while (!set_isempty(s) && - dbus_connection_read_write_dispatch(bus, -1)) - ; - - if (!arg_quiet && d.result) { - if (streq(d.result, "timeout")) - log_error("Job timed out."); - else if (streq(d.result, "canceled")) - log_error("Job canceled."); - else if (streq(d.result, "dependency")) - log_error("A dependency job failed. See system journal for details."); - else if (!streq(d.result, "done") && !streq(d.result, "skipped")) - log_error("Job failed. See system journal and 'systemctl status' for details."); - } - - if (streq_ptr(d.result, "timeout")) - r = -ETIME; - else if (streq_ptr(d.result, "canceled")) - r = -ECANCELED; - else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped")) - r = -EIO; - else - r = 0; - - free(d.result); - -finish: - /* This is slightly dirty, since we don't undo the filter registration. */ - - return r; -} - -static int get_unit_path( - DBusConnection *bus, - const char *name, - char **unit_path) { - - DBusError error; - DBusMessage *m = NULL, *reply = NULL; - char *path; - int r = 0; + if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) + return log_oom(); - assert(bus); - assert(name); - assert(unit_path); + while (!set_isempty(s)) { - dbus_error_init(&error); + if (!dbus_connection_read_write_dispatch(bus, -1)) { + log_error("Disconnected from bus."); + return -ECONNREFUSED; + } - m = dbus_message_new_method_call("org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + if (!d.result) + goto free_name; - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + if (!arg_quiet) { + if (streq(d.result, "timeout")) + log_error("Job for %s timed out.", strna(d.name)); + else if (streq(d.result, "canceled")) + log_error("Job for %s canceled.", strna(d.name)); + else if (streq(d.result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d.name)); + else if (!streq(d.result, "done") && !streq(d.result, "skipped")) + log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d.name), strna(d.name)); + } - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - if (streq(error.name, BUS_ERROR_NO_SUCH_UNIT)) - r = -EINVAL; - else { - log_error("Failed to issue method call: %s", bus_error_message(&error)); + if (streq_ptr(d.result, "timeout")) + r = -ETIME; + else if (streq_ptr(d.result, "canceled")) + r = -ECANCELED; + else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped")) r = -EIO; - } - goto finish; - } - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + free(d.result); + d.result = NULL; - *unit_path = strdup(path); - if (!(*unit_path)) { - log_error("Failed to duplicate unit path"); - r = -ENOMEM; + free_name: + free(d.name); + d.name = NULL; } -finish: - if (m) - dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); + dbus_connection_remove_filter(bus, wait_filter, &d); return r; } -static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; +static int check_one_unit(DBusConnection *bus, const char *name, char **check_states, bool quiet) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *n = NULL; DBusMessageIter iter, sub; const char *interface = "org.freedesktop.systemd1.Unit", *property = "ActiveState"; - const char *path = NULL; - const char *state; - int r = 3; /* According to LSB: "program is not running" */ + const char *state, *path; + DBusError error; + int r; - assert(bus); assert(name); dbus_error_init(&error); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + n = unit_name_mangle(name); + if (!n) + return log_oom(); - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + &reply, + &error, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_INVALID); + if (r < 0) { + dbus_error_free(&error); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - /* Hmm, cannot figure out anything about this unit... */ if (!quiet) puts("unknown"); - - goto finish; + return 0; } - if (!dbus_message_get_args(reply, &error, + if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; + log_error("Failed to parse reply."); + return -EIO; } dbus_message_unref(reply); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + reply = NULL; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r < 0) { + if (!quiet) + puts("unknown"); + return 0; } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return r; } dbus_message_iter_recurse(&iter, &sub); if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return r; } dbus_message_iter_get_basic(&sub, &state); @@ -1670,68 +1712,88 @@ static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { if (!quiet) puts(state); - if (streq(state, "active") || streq(state, "reloading")) - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return strv_find(check_states, state) ? 1 : 0; } -static void check_listening_sockets( +static void check_triggering_units( DBusConnection *bus, const char *unit_name) { - DBusError error; - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub; - char *service_trigger = NULL; const char *interface = "org.freedesktop.systemd1.Unit", - *triggered_by_property = "TriggeredBy"; - - char *unit_path = NULL; + *load_state_property = "LoadState", + *triggered_by_property = "TriggeredBy", + *state; + _cleanup_free_ char *unit_path = NULL, *n = NULL; bool print_warning_label = true; + int r; - dbus_error_init(&error); - - if (get_unit_path(bus, unit_name, &unit_path) < 0) - goto finish; + n = unit_name_mangle(unit_name); + if (!n) { + log_oom(); + return; + } - m = dbus_message_new_method_call("org.freedesktop.systemd1", - unit_path, - "org.freedesktop.DBus.Properties", - "Get"); - if (!m) { - log_error("Could not allocate message."); - goto finish; + unit_path = unit_dbus_path_from_name(n); + if (!unit_path) { + log_oom(); + return; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &triggered_by_property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - goto finish; + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &load_state_property, + DBUS_TYPE_INVALID); + if (r < 0) + return; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return; } - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - goto finish; + dbus_message_iter_recurse(&iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); + return; } + dbus_message_iter_get_basic(&sub, &state); + + if (streq(state, "masked")) + return; + + dbus_message_unref(reply); + reply = NULL; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &triggered_by_property, + DBUS_TYPE_INVALID); + if (r < 0) + return; + if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - goto finish; - + log_error("Failed to parse reply."); + return; } dbus_message_iter_recurse(&iter, &sub); @@ -1739,41 +1801,34 @@ static void check_listening_sockets( sub = iter; while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - int r; + const char * const check_states[] = { + "active", + "reloading", + NULL + }; + const char *service_trigger; if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - goto finish; + log_error("Failed to parse reply."); + return; } dbus_message_iter_get_basic(&sub, &service_trigger); - if (!endswith(service_trigger, ".socket")) - goto next; - - r = check_one_unit(bus, service_trigger, true); + r = check_one_unit(bus, service_trigger, (char**) check_states, true); if (r < 0) - goto finish; - if (r == 0) { + return; + if (r > 0) { if (print_warning_label) { - log_warning("There are listening sockets associated with %s :", unit_name); + log_warning("Warning: Stopping %s, but it can still be activated by:", unit_name); print_warning_label = false; } - log_warning("%s", service_trigger); + + log_warning(" %s", service_trigger); } -next: + dbus_message_iter_next(&sub); } -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - free(unit_path); } static int start_unit_one( @@ -1784,142 +1839,107 @@ static int start_unit_one( DBusError *error, Set *s) { - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *n; const char *path; int r; - assert(bus); assert(method); assert(name); assert(mode); assert(error); - assert(arg_no_block || s); - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &mode, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) { + n = unit_name_mangle(name); + if (!n) + return log_oom(); - if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + &reply, + error, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID); + if (r) { + if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL) /* There's always a fallback possible for * legacy actions. */ r = -EADDRNOTAVAIL; - goto finish; - } + else + log_error("Failed to issue method call: %s", bus_error_message(error)); - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; + return r; } if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { log_error("Failed to parse reply: %s", bus_error_message(error)); - r = -EIO; - goto finish; + return -EIO; } - if (need_daemon_reload(bus, name)) - log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.", - arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); + if (need_daemon_reload(bus, n) > 0) + log_warning("Warning: Unit file of %s changed on disk, 'systemctl %sdaemon-reload' recommended.", + n, arg_scope == UNIT_FILE_SYSTEM ? "" : "--user "); - if (!arg_no_block) { + if (s) { char *p; - if (!(p = strdup(path))) { - log_error("Failed to duplicate path."); - r = -ENOMEM; - goto finish; - } - - if ((r = set_put(s, p)) < 0) { - free(p); - log_error("Failed to add path to set."); - goto finish; - } - } - - /* When stopping unit check if we have some listening sockets active */ - if (streq(method, "StopUnit") && !arg_quiet) - check_listening_sockets(bus, name); - - r = 0; - -finish: - if (m) - dbus_message_unref(m); + p = strdup(path); + if (!p) + return log_oom(); - if (reply) - dbus_message_unref(reply); + r = set_consume(s, p); + if (r < 0) { + log_error("Failed to add path to set."); + return r; + } + } - return r; + return 0; } +static const struct { + const char *target; + const char *verb; + const char *mode; +} action_table[_ACTION_MAX] = { + [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, + [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, + [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, + [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, + [ACTION_RUNLEVEL2] = { SPECIAL_RUNLEVEL2_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL3] = { SPECIAL_RUNLEVEL3_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL4] = { SPECIAL_RUNLEVEL4_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL5] = { SPECIAL_RUNLEVEL5_TARGET, NULL, "isolate" }, + [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, + [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, + [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, + [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, + [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, + [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, + [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, +}; + static enum action verb_to_action(const char *verb) { - if (streq(verb, "halt")) - return ACTION_HALT; - else if (streq(verb, "poweroff")) - return ACTION_POWEROFF; - else if (streq(verb, "reboot")) - return ACTION_REBOOT; - else if (streq(verb, "kexec")) - return ACTION_KEXEC; - else if (streq(verb, "rescue")) - return ACTION_RESCUE; - else if (streq(verb, "emergency")) - return ACTION_EMERGENCY; - else if (streq(verb, "default")) - return ACTION_DEFAULT; - else if (streq(verb, "exit")) - return ACTION_EXIT; - else if (streq(verb, "suspend")) - return ACTION_SUSPEND; - else if (streq(verb, "hibernate")) - return ACTION_HIBERNATE; - else - return ACTION_INVALID; + enum action i; + + for (i = ACTION_INVALID; i < _ACTION_MAX; i++) + if (action_table[i].verb && streq(verb, action_table[i].verb)) + return i; + return ACTION_INVALID; } static int start_unit(DBusConnection *bus, char **args) { - static const char * const table[_ACTION_MAX] = { - [ACTION_HALT] = SPECIAL_HALT_TARGET, - [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET, - [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET, - [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET, - [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET, - [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET, - [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET, - [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET, - [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET, - [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET, - [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET, - [ACTION_EXIT] = SPECIAL_EXIT_TARGET, - [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET - }; - int r, ret = 0; const char *method, *mode, *one_name; - Set *s = NULL; - DBusError error; + _cleanup_set_free_free_ Set *s = NULL; + _cleanup_dbus_error_free_ DBusError error; char **name; dbus_error_init(&error); @@ -1929,6 +1949,7 @@ static int start_unit(DBusConnection *bus, char **args) { ask_password_agent_open_if_enabled(); if (arg_action == ACTION_SYSTEMCTL) { + enum action action; method = streq(args[0], "stop") || streq(args[0], "condstop") ? "StopUnit" : @@ -1945,65 +1966,64 @@ static int start_unit(DBusConnection *bus, char **args) { streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" : "StartUnit"; + action = verb_to_action(args[0]); - mode = - (streq(args[0], "isolate") || - streq(args[0], "rescue") || - streq(args[0], "emergency")) ? "isolate" : arg_job_mode; + mode = streq(args[0], "isolate") ? "isolate" : + action_table[action].mode ?: arg_job_mode; - one_name = table[verb_to_action(args[0])]; + one_name = action_table[action].target; } else { - assert(arg_action < ELEMENTSOF(table)); - assert(table[arg_action]); + assert(arg_action < ELEMENTSOF(action_table)); + assert(action_table[arg_action].target); method = "StartUnit"; - mode = (arg_action == ACTION_EMERGENCY || - arg_action == ACTION_RESCUE || - arg_action == ACTION_RUNLEVEL2 || - arg_action == ACTION_RUNLEVEL3 || - arg_action == ACTION_RUNLEVEL4 || - arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace"; - - one_name = table[arg_action]; + mode = action_table[arg_action].mode; + one_name = action_table[arg_action].target; } if (!arg_no_block) { - if ((ret = enable_wait_for_jobs(bus)) < 0) { + ret = enable_wait_for_jobs(bus); + if (ret < 0) { log_error("Could not watch jobs: %s", strerror(-ret)); - goto finish; + return ret; } - if (!(s = set_new(string_hash_func, string_compare_func))) { - log_error("Failed to allocate set."); - ret = -ENOMEM; - goto finish; - } + s = set_new(string_hash_func, string_compare_func); + if (!s) + return log_oom(); } if (one_name) { - if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0) - goto finish; + ret = start_unit_one(bus, method, one_name, mode, &error, s); + if (ret < 0) + ret = translate_bus_error_to_exit_status(ret, &error); } else { - STRV_FOREACH(name, args+1) - if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) { + STRV_FOREACH(name, args+1) { + r = start_unit_one(bus, method, *name, mode, &error, s); + if (r < 0) { ret = translate_bus_error_to_exit_status(r, &error); dbus_error_free(&error); } - } - - if (!arg_no_block) - if ((r = wait_for_jobs(bus, s)) < 0) { - ret = r; - goto finish; } + } -finish: - if (s) - set_free_free(s); + if (!arg_no_block) { + r = wait_for_jobs(bus, s); + if (r < 0) + return r; - dbus_error_free(&error); + /* When stopping units, warn if they can still be triggered by + * another active unit (socket, path, timer) */ + if (!arg_quiet && streq(method, "StopUnit")) { + if (one_name) + check_triggering_units(bus, one_name); + else + STRV_FOREACH(name, args+1) + check_triggering_units(bus, *name); + } + } return ret; } @@ -2013,12 +2033,10 @@ finish: static int reboot_with_logind(DBusConnection *bus, enum action a) { #ifdef HAVE_LOGIND const char *method; - DBusMessage *m = NULL, *reply = NULL; - DBusError error; dbus_bool_t interactive = true; - int r; - dbus_error_init(&error); + if (!bus) + return -EIO; polkit_agent_open_if_enabled(); @@ -2040,62 +2058,156 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) { method = "Hibernate"; break; + case ACTION_HYBRID_SLEEP: + method = "HybridSleep"; + break; + default: return -EINVAL; } - m = dbus_message_new_method_call( - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - method); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + return bus_method_call_with_reply( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + method, + NULL, + NULL, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +#else + return -ENOSYS; +#endif +} - if (!dbus_message_append_args(m, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; +static int check_inhibitors(DBusConnection *bus, enum action a) { +#ifdef HAVE_LOGIND + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub, sub2; + int r; + unsigned c = 0; + _cleanup_strv_free_ char **sessions = NULL; + char **s; + + if (!bus) + return 0; + + if (arg_ignore_inhibitors || arg_force > 0) + return 0; + + if (arg_when > 0) + return 0; + + if (geteuid() == 0) + return 0; + + if (!on_tty()) + return 0; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + /* If logind is not around, then there are no inhibitors... */ + return 0; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; } - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - if (error_is_no_service(&error)) { - log_debug("Failed to issue method call: %s", bus_error_message(&error)); - r = -ENOENT; - goto finish; + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *what, *who, *why, *mode; + uint32_t uid, pid; + _cleanup_strv_free_ char **sv = NULL; + _cleanup_free_ char *comm = NULL, *user = NULL; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; } - if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) { - log_debug("Failed to issue method call: %s", bus_error_message(&error)); - r = -EACCES; - goto finish; + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) { + log_error("Failed to parse reply."); + return -EIO; } - log_info("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + if (!streq(mode, "block")) + goto next; + + sv = strv_split(what, ":"); + if (!sv) + return log_oom(); + + if (!strv_contains(sv, + a == ACTION_HALT || + a == ACTION_POWEROFF || + a == ACTION_REBOOT || + a == ACTION_KEXEC ? "shutdown" : "sleep")) + goto next; + + get_process_comm(pid, &comm); + user = uid_to_name(uid); + log_warning("Operation inhibited by \"%s\" (PID %lu \"%s\", user %s), reason is \"%s\".", + who, (unsigned long) pid, strna(comm), strna(user), why); + c++; + + next: + dbus_message_iter_next(&sub); } - r = 0; + dbus_message_iter_recurse(&iter, &sub); -finish: - if (m) - dbus_message_unref(m); + /* Check for current sessions */ + sd_get_sessions(&sessions); + STRV_FOREACH(s, sessions) { + uid_t uid; + _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL; - if (reply) - dbus_message_unref(reply); + if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid()) + continue; - dbus_error_free(&error); + if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) + continue; - return r; + if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty"))) + continue; + + sd_session_get_tty(*s, &tty); + sd_session_get_seat(*s, &seat); + sd_session_get_service(*s, &service); + user = uid_to_name(uid); + + log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty); + c++; + } + + if (c <= 0) + return 0; + + log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.", + action_table[a].verb); + + return -EPERM; #else - return -ENOSYS; + return 0; #endif } @@ -2107,6 +2219,10 @@ static int start_special(DBusConnection *bus, char **args) { a = verb_to_action(args[0]); + r = check_inhibitors(bus, a); + if (r < 0) + return r; + if (arg_force >= 2 && geteuid() != 0) { log_error("Must be root."); return -EPERM; @@ -2131,20 +2247,27 @@ static int start_special(DBusConnection *bus, char **args) { (a == ACTION_POWEROFF || a == ACTION_REBOOT || a == ACTION_SUSPEND || - a == ACTION_HIBERNATE)) { + a == ACTION_HIBERNATE || + a == ACTION_HYBRID_SLEEP)) { r = reboot_with_logind(bus, a); if (r >= 0) return r; } r = start_unit(bus, args); - if (r >= 0) + if (r == EXIT_SUCCESS) warn_wall(a); return r; } -static int check_unit(DBusConnection *bus, char **args) { +static int check_unit_active(DBusConnection *bus, char **args) { + const char * const check_states[] = { + "active", + "reloading", + NULL + }; + char **name; int r = 3; /* According to LSB: "program is not running" */ @@ -2152,77 +2275,76 @@ static int check_unit(DBusConnection *bus, char **args) { assert(args); STRV_FOREACH(name, args+1) { - int state = check_one_unit(bus, *name, arg_quiet); + int state; + + state = check_one_unit(bus, *name, (char**) check_states, arg_quiet); if (state < 0) return state; - if (state == 0) + if (state > 0) r = 0; } return r; } -static int kill_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL; - int r = 0; - DBusError error; +static int check_unit_failed(DBusConnection *bus, char **args) { + const char * const check_states[] = { + "failed", + NULL + }; + char **name; + int r = 1; assert(bus); assert(args); - dbus_error_init(&error); - - if (!arg_kill_who) - arg_kill_who = "all"; - - if (!arg_kill_mode) - arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process"; - STRV_FOREACH(name, args+1) { - DBusMessage *reply; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + int state; - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_STRING, &arg_kill_who, - DBUS_TYPE_STRING, &arg_kill_mode, - DBUS_TYPE_INT32, &arg_signal, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + state = check_one_unit(bus, *name, (char**) check_states, arg_quiet); + if (state < 0) + return state; + if (state > 0) + r = 0; + } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - dbus_error_free(&error); - r = -EIO; - } + return r; +} - dbus_message_unref(m); +static int kill_unit(DBusConnection *bus, char **args) { + char **name; + int r = 0; - if (reply) - dbus_message_unref(reply); - m = reply = NULL; - } + assert(bus); + assert(args); -finish: - if (m) - dbus_message_unref(m); + if (!arg_kill_who) + arg_kill_who = "all"; - dbus_error_free(&error); + STRV_FOREACH(name, args+1) { + _cleanup_free_ char *n = NULL; - return r; + n = unit_name_mangle(*name); + if (!n) + return log_oom(); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + NULL, + NULL, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_STRING, &arg_kill_who, + DBUS_TYPE_INT32, &arg_signal, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + } + return 0; } typedef struct ExecStatusInfo { @@ -2271,7 +2393,8 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0) return -EIO; - if (!(i->path = strdup(path))) + i->path = strdup(path); + if (!i->path) return -ENOMEM; if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY || @@ -2286,8 +2409,8 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) n++; } - - if (!(i->argv = new0(char*, n+1))) + i->argv = new0(char*, n+1); + if (!i->argv) return -ENOMEM; n = 0; @@ -2299,8 +2422,11 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) dbus_message_iter_get_basic(&sub3, &s); dbus_message_iter_next(&sub3); - if (!(i->argv[n++] = strdup(s))) + i->argv[n] = strdup(s); + if (!i->argv[n]) return -ENOMEM; + + n++; } if (!dbus_message_iter_next(&sub2) || @@ -2338,7 +2464,9 @@ typedef struct UnitStatusInfo { const char *fragment_path; const char *source_path; - const char *default_control_group; + const char *control_group; + + char **dropin_paths; const char *load_error; const char *result; @@ -2355,6 +2483,7 @@ typedef struct UnitStatusInfo { pid_t main_pid; pid_t control_pid; const char *status_text; + const char *pid_file; bool running:1; usec_t start_timestamp; @@ -2364,12 +2493,19 @@ typedef struct UnitStatusInfo { usec_t condition_timestamp; bool condition_result; + bool failed_condition_trigger; + bool failed_condition_negate; + const char *failed_condition; + const char *failed_condition_param; /* Socket */ unsigned n_accepted; unsigned n_connections; bool accept; + /* Pairs of type, path */ + char **listen; + /* Device */ const char *sysfs_path; @@ -2382,13 +2518,21 @@ typedef struct UnitStatusInfo { LIST_HEAD(ExecStatusInfo, exec); } UnitStatusInfo; -static void print_status_info(UnitStatusInfo *i) { +static void print_status_info(UnitStatusInfo *i, + bool *ellipsized) { ExecStatusInfo *p; const char *on, *off, *ss; usec_t timestamp; - char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1; + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; char since2[FORMAT_TIMESTAMP_MAX], *s2; const char *path; + int flags = + arg_all * OUTPUT_SHOW_ALL | + (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + on_tty() * OUTPUT_COLOR | + !arg_quiet * OUTPUT_WARN_CUTOFF | + arg_full * OUTPUT_FULL_WIDTH; + char **t, **t2; assert(i); @@ -2403,47 +2547,74 @@ static void print_status_info(UnitStatusInfo *i) { printf("\n"); if (i->following) - printf("\t Follow: unit currently follows state of %s\n", i->following); + printf(" Follow: unit currently follows state of %s\n", i->following); if (streq_ptr(i->load_state, "error")) { - on = ansi_highlight_red(true); - off = ansi_highlight_red(false); + on = ansi_highlight_red(); + off = ansi_highlight_off(); } else on = off = ""; path = i->source_path ? i->source_path : i->fragment_path; if (i->load_error) - printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); + printf(" Loaded: %s%s%s (Reason: %s)\n", + on, strna(i->load_state), off, i->load_error); else if (path && i->unit_file_state) - printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, path, i->unit_file_state); + printf(" Loaded: %s%s%s (%s; %s)\n", + on, strna(i->load_state), off, path, i->unit_file_state); else if (path) - printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, path); + printf(" Loaded: %s%s%s (%s)\n", + on, strna(i->load_state), off, path); else - printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off); + printf(" Loaded: %s%s%s\n", + on, strna(i->load_state), off); + + if (!strv_isempty(i->dropin_paths)) { + char ** dropin; + char * dir = NULL; + bool last = false; + + STRV_FOREACH(dropin, i->dropin_paths) { + if (! dir || last) { + printf(dir ? " " : " Drop-In: "); + + free(dir); + + if (path_get_parent(*dropin, &dir) < 0) { + log_oom(); + return; + } + + printf("%s\n %s", dir, + draw_special_char(DRAW_TREE_RIGHT)); + } + + last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir)); + + printf("%s%s", path_get_file_name(*dropin), last ? "\n" : ", "); + } + + free(dir); + } ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state; if (streq_ptr(i->active_state, "failed")) { - on = ansi_highlight_red(true); - off = ansi_highlight_red(false); + on = ansi_highlight_red(); + off = ansi_highlight_off(); } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) { - on = ansi_highlight_green(true); - off = ansi_highlight_green(false); + on = ansi_highlight_green(); + off = ansi_highlight_off(); } else on = off = ""; if (ss) - printf("\t Active: %s%s (%s)%s", - on, - strna(i->active_state), - ss, - off); + printf(" Active: %s%s (%s)%s", + on, strna(i->active_state), ss, off); else - printf("\t Active: %s%s%s", - on, - strna(i->active_state), - off); + printf(" Active: %s%s%s", + on, strna(i->active_state), off); if (!isempty(i->result) && !streq(i->result, "success")) printf(" (Result: %s)", i->result); @@ -2455,7 +2626,7 @@ static void print_status_info(UnitStatusInfo *i) { streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp : i->active_exit_timestamp; - s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp); + s1 = format_timestamp_relative(since1, sizeof(since1), timestamp); s2 = format_timestamp(since2, sizeof(since2), timestamp); if (s1) @@ -2466,54 +2637,51 @@ static void print_status_info(UnitStatusInfo *i) { printf("\n"); if (!i->condition_result && i->condition_timestamp > 0) { - s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp); + s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp); s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp); - if (s1) - printf("\t start condition failed at %s; %s\n", s2, s1); - else if (s2) - printf("\t start condition failed at %s\n", s2); + printf(" start condition failed at %s%s%s\n", + s2, s1 ? "; " : "", s1 ? s1 : ""); + if (i->failed_condition_trigger) + printf(" none of the trigger conditions were met\n"); + else if (i->failed_condition) + printf(" %s=%s%s was not met\n", + i->failed_condition, + i->failed_condition_negate ? "!" : "", + i->failed_condition_param); } if (i->sysfs_path) - printf("\t Device: %s\n", i->sysfs_path); + printf(" Device: %s\n", i->sysfs_path); if (i->where) - printf("\t Where: %s\n", i->where); + printf(" Where: %s\n", i->where); if (i->what) - printf("\t What: %s\n", i->what); + printf(" What: %s\n", i->what); - if (!strv_isempty(i->documentation)) { - char **t; - bool first = true; + STRV_FOREACH(t, i->documentation) + printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t); - STRV_FOREACH(t, i->documentation) { - if (first) { - printf("\t Docs: %s\n", *t); - first = false; - } else - printf("\t %s\n", *t); - } - } + STRV_FOREACH_PAIR(t, t2, i->listen) + printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t); if (i->accept) - printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); + printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); LIST_FOREACH(exec, p, i->exec) { - char *t; + _cleanup_free_ char *argv = NULL; bool good; /* Only show exited processes here */ if (p->code == 0) continue; - t = strv_join(p->argv, " "); - printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t)); - free(t); + argv = strv_join(p->argv, " "); + printf(" Process: %u %s=%s ", p->pid, p->name, strna(argv)); - good = is_clean_exit_lsb(p->code, p->status); + good = is_clean_exit_lsb(p->code, p->status, NULL); if (!good) { - on = ansi_highlight_red(true); - off = ansi_highlight_red(false); + on = ansi_highlight_red(); + off = ansi_highlight_off(); } else on = off = ""; @@ -2544,18 +2712,14 @@ static void print_status_info(UnitStatusInfo *i) { } if (i->main_pid > 0 || i->control_pid > 0) { - printf("\t"); - if (i->main_pid > 0) { - printf("Main PID: %u", (unsigned) i->main_pid); + printf(" Main PID: %u", (unsigned) i->main_pid); if (i->running) { - char *t = NULL; - get_process_comm(i->main_pid, &t); - if (t) { - printf(" (%s)", t); - free(t); - } + _cleanup_free_ char *comm = NULL; + get_process_comm(i->main_pid, &comm); + if (comm) + printf(" (%s)", comm); } else if (i->exit_code > 0) { printf(" (code=%s, ", sigchld_code_to_string(i->exit_code)); @@ -2572,41 +2736,41 @@ static void print_status_info(UnitStatusInfo *i) { printf("signal=%s", signal_to_string(i->exit_status)); printf(")"); } - } - if (i->main_pid > 0 && i->control_pid > 0) - printf(";"); + if (i->control_pid > 0) + printf(";"); + } if (i->control_pid > 0) { - char *t = NULL; + _cleanup_free_ char *c = NULL; - printf(" Control: %u", (unsigned) i->control_pid); + printf(" %8s: %u", i->main_pid ? "" : " Control", (unsigned) i->control_pid); - get_process_comm(i->control_pid, &t); - if (t) { - printf(" (%s)", t); - free(t); - } + get_process_comm(i->control_pid, &c); + if (c) + printf(" (%s)", c); } printf("\n"); } if (i->status_text) - printf("\t Status: \"%s\"\n", i->status_text); + printf(" Status: \"%s\"\n", i->status_text); - if (i->default_control_group) { + if (i->control_group && + (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0)) { unsigned c; - printf("\t CGroup: %s\n", i->default_control_group); + printf(" CGroup: %s\n", i->control_group); if (arg_transport != TRANSPORT_SSH) { unsigned k = 0; pid_t extra[2]; + char prefix[] = " "; c = columns(); - if (c > 18) - c -= 18; + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; else c = 0; @@ -2616,20 +2780,30 @@ static void print_status_info(UnitStatusInfo *i) { if (i->control_pid > 0) extra[k++] = i->control_pid; - show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, extra, k); + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, + c, false, extra, k, flags); } } if (i->id && arg_transport != TRANSPORT_SSH) { printf("\n"); - show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow); + show_journal_by_unit(stdout, + i->id, + arg_output, + 0, + i->inactive_exit_timestamp_monotonic, + arg_lines, + getuid(), + flags, + arg_scope == UNIT_FILE_SYSTEM, + ellipsized); } if (i->need_daemon_reload) - printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n", - ansi_highlight_red(true), - ansi_highlight_red(false), - arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); + printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %sdaemon-reload' recommended.\n", + ansi_highlight_red(), + ansi_highlight_off(), + arg_scope == UNIT_FILE_SYSTEM ? "" : "--user "); } static void show_unit_help(UnitStatusInfo *i) { @@ -2647,7 +2821,7 @@ static void show_unit_help(UnitStatusInfo *i) { if (startswith(*p, "man:")) { size_t k; char *e = NULL; - char *page = NULL, *section = NULL; + _cleanup_free_ char *page = NULL, *section = NULL; const char *args[4] = { "man", NULL, NULL, NULL }; pid_t pid; @@ -2658,15 +2832,9 @@ static void show_unit_help(UnitStatusInfo *i) { if (e) { page = strndup((*p) + 4, e - *p - 4); - if (!page) { - log_error("Out of memory."); - return; - } - section = strndup(e + 1, *p + k - e - 2); - if (!section) { - free(page); - log_error("Out of memory"); + if (!page || !section) { + log_oom(); return; } @@ -2678,8 +2846,6 @@ static void show_unit_help(UnitStatusInfo *i) { pid = fork(); if (pid < 0) { log_error("Failed to fork: %m"); - free(page); - free(section); continue; } @@ -2690,12 +2856,9 @@ static void show_unit_help(UnitStatusInfo *i) { _exit(EXIT_FAILURE); } - free(page); - free(section); - wait_for_terminate(pid, NULL); } else - log_info("Can't show %s.", *p); + log_info("Can't show: %s", *p); } } @@ -2727,10 +2890,20 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn i->fragment_path = s; else if (streq(name, "SourcePath")) i->source_path = s; - else if (streq(name, "DefaultControlGroup")) - i->default_control_group = s; +#ifndef NOLEGACY + else if (streq(name, "DefaultControlGroup")) { + const char *e; + e = startswith(s, SYSTEMD_CGROUP_CONTROLLER ":"); + if (e) + i->control_group = e; + } +#endif + else if (streq(name, "ControlGroup")) + i->control_group = s; else if (streq(name, "StatusText")) i->status_text = s; + else if (streq(name, "PIDFile")) + i->pid_file = s; else if (streq(name, "SysFSPath")) i->sysfs_path = s; else if (streq(name, "Where")) @@ -2835,23 +3008,60 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn ExecStatusInfo *info; int r; - if (!(info = new0(ExecStatusInfo, 1))) + info = new0(ExecStatusInfo, 1); + if (!info) return -ENOMEM; - if (!(info->name = strdup(name))) { + info->name = strdup(name); + if (!info->name) { free(info); return -ENOMEM; } - if ((r = exec_status_info_deserialize(&sub, info)) < 0) { + r = exec_status_info_deserialize(&sub, info); + if (r < 0) { free(info); return r; } - LIST_PREPEND(ExecStatusInfo, exec, i->exec, info); + LIST_PREPEND(exec, i->exec, info); + + dbus_message_iter_next(&sub); + } + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && + streq(name, "Listen")) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *type, *path; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0) { + int r; + + r = strv_extend(&i->listen, type); + if (r < 0) + return r; + r = strv_extend(&i->listen, path); + if (r < 0) + return r; + } dbus_message_iter_next(&sub); } + + return 0; + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING && + streq(name, "DropInPaths")) { + int r = bus_parse_strv_iter(iter, &i->dropin_paths); + if (r < 0) + return r; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING && streq(name, "Documentation")) { @@ -2860,16 +3070,43 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn dbus_message_iter_recurse(iter, &sub); while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { const char *s; - char **l; + int r; dbus_message_iter_get_basic(&sub, &s); - l = strv_append(i->documentation, s); - if (!l) - return -ENOMEM; + r = strv_extend(&i->documentation, s); + if (r < 0) + return r; + + dbus_message_iter_next(&sub); + } + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && + streq(name, "Conditions")) { + DBusMessageIter sub, sub2; - strv_free(i->documentation); - i->documentation = l; + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *cond, *param; + dbus_bool_t trigger, negate; + dbus_int32_t state; + + dbus_message_iter_recurse(&sub, &sub2); + log_debug("here"); + + if(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &cond, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &trigger, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &negate, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, ¶m, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &state, false) >= 0) { + log_debug("%s %d %d %s %d", cond, trigger, negate, param, state); + if (state < 0 && (!trigger || !i->failed_condition)) { + i->failed_condition = cond; + i->failed_condition_trigger = trigger; + i->failed_condition_negate = negate; + i->failed_condition_param = param; + } + } dbus_message_iter_next(&sub); } @@ -2913,7 +3150,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { /* This is a low-level property printer, see * print_status_info() for the nicer output */ - if (arg_property && !strv_find(arg_property, name)) + if (arg_properties && !strv_find(arg_properties, name)) return 0; switch (dbus_message_iter_get_arg_type(iter)) { @@ -2982,6 +3219,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { DBusMessageIter sub, sub2; dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { const char *type, *path; @@ -2996,49 +3234,43 @@ static int print_property(const char *name, DBusMessageIter *iter) { return 0; - } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) { + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Listen")) { DBusMessageIter sub, sub2; dbus_message_iter_recurse(iter, &sub); while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { - const char *base; - uint64_t value, next_elapse; + const char *type, *path; dbus_message_iter_recurse(&sub, &sub2); - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 && - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 && - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) { - char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; - - printf("%s={ value=%s ; next_elapse=%s }\n", - base, - format_timespan(timespan1, sizeof(timespan1), value), - format_timespan(timespan2, sizeof(timespan2), next_elapse)); - } + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0) + printf("Listen%s=%s\n", type, path); dbus_message_iter_next(&sub); } return 0; - } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) { + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) { DBusMessageIter sub, sub2; dbus_message_iter_recurse(iter, &sub); while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { - const char *controller, *attr, *value; + const char *base; + uint64_t value, next_elapse; dbus_message_iter_recurse(&sub, &sub2); - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 && - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 && - bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) { + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) { + char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; - printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n", - controller, - attr, - value); + printf("%s={ value=%s ; next_elapse=%s }\n", + base, + format_timespan(timespan1, sizeof(timespan1), value, 0), + format_timespan(timespan2, sizeof(timespan2), next_elapse, 0)); } dbus_message_iter_next(&sub); @@ -3051,12 +3283,11 @@ static int print_property(const char *name, DBusMessageIter *iter) { dbus_message_iter_recurse(iter, &sub); while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { - ExecStatusInfo info; + ExecStatusInfo info = {}; - zero(info); if (exec_status_info_deserialize(&sub, &info) >= 0) { char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; - char *t; + _cleanup_free_ char *t; t = strv_join(info.argv, " "); @@ -3072,8 +3303,6 @@ static int print_property(const char *name, DBusMessageIter *iter) { info.status, info.code == CLD_EXITED ? "" : "/", strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status))); - - free(t); } free(info.path); @@ -3083,8 +3312,62 @@ static int print_property(const char *name, DBusMessageIter *iter) { } return 0; + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "DeviceAllow")) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *path, *rwm; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) >= 0) + printf("%s=%s %s\n", name, strna(path), strna(rwm)); + + dbus_message_iter_next(&sub); + } + return 0; + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "BlockIODeviceWeight")) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *path; + uint64_t weight; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &weight, false) >= 0) + printf("%s=%s %" PRIu64 "\n", name, strna(path), weight); + + dbus_message_iter_next(&sub); + } + return 0; + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *path; + uint64_t bandwidth; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &bandwidth, false) >= 0) + printf("%s=%s %" PRIu64 "\n", name, strna(path), bandwidth); + + dbus_message_iter_next(&sub); + } + return 0; } + break; } @@ -3097,52 +3380,40 @@ static int print_property(const char *name, DBusMessageIter *iter) { return 0; } -static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { - DBusMessage *m = NULL, *reply = NULL; +static int show_one(const char *verb, + DBusConnection *bus, + const char *path, + bool show_properties, + bool *new_line, + bool *ellipsized) { + _cleanup_free_ DBusMessage *reply = NULL; const char *interface = ""; int r; - DBusError error; DBusMessageIter iter, sub, sub2, sub3; - UnitStatusInfo info; + UnitStatusInfo info = {}; ExecStatusInfo *p; - assert(bus); assert(path); assert(new_line); - zero(info); - dbus_error_init(&error); - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&iter, &sub); @@ -3155,24 +3426,13 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { const char *name; - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } - + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY); dbus_message_iter_recurse(&sub, &sub2); - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } - - if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 || + dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&sub2, &sub3); @@ -3181,11 +3441,9 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo r = print_property(name, &sub3); else r = status_property(name, &sub3, &info); - if (r < 0) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_next(&sub); @@ -3197,405 +3455,559 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo if (streq(verb, "help")) show_unit_help(&info); else - print_status_info(&info); + print_status_info(&info, ellipsized); } strv_free(info.documentation); + strv_free(info.dropin_paths); + strv_free(info.listen); if (!streq_ptr(info.active_state, "active") && !streq_ptr(info.active_state, "reloading") && - streq(verb, "status")) + streq(verb, "status")) { /* According to LSB: "program not running" */ - r = 3; + /* 0: program is running or service is OK + * 1: program is dead and /var/run pid file exists + * 2: program is dead and /var/lock lock file exists + * 3: program is not running + * 4: program or service status is unknown + */ + if (info.pid_file && access(info.pid_file, F_OK) == 0) + r = 1; + else + r = 3; + } while ((p = info.exec)) { - LIST_REMOVE(ExecStatusInfo, exec, info.exec, p); + LIST_REMOVE(exec, info.exec, p); exec_status_info_free(p); } -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - return r; } -static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, bool *new_line) { - DBusMessage *m = NULL, *reply = NULL; +static int show_one_by_pid(const char *verb, + DBusConnection *bus, + uint32_t pid, + bool *new_line, + bool *ellipsized) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; const char *path = NULL; - DBusError error; + _cleanup_dbus_error_free_ DBusError error; int r; dbus_error_init(&error); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitByPID"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &pid, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitByPID", + &reply, + NULL, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + return -EIO; } - r = show_one(verb, bus, path, false, new_line); + r = show_one(verb, bus, path, false, new_line, ellipsized); + return r; +} + +static int show_all(const char* verb, + DBusConnection *bus, + bool show_properties, + bool *new_line, + bool *ellipsized) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ struct unit_info *unit_infos = NULL; + unsigned c = 0; + const struct unit_info *u; + int r; -finish: - if (m) - dbus_message_unref(m); + r = get_unit_list(bus, &reply, &unit_infos, &c); + if (r < 0) + return r; - if (reply) - dbus_message_unref(reply); + qsort_safe(unit_infos, c, sizeof(struct unit_info), compare_unit_info); - dbus_error_free(&error); + for (u = unit_infos; u < unit_infos + c; u++) { + _cleanup_free_ char *p = NULL; - return r; + if (!output_show_unit(u)) + continue; + + p = unit_dbus_path_from_name(u->id); + 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; + } + + return 0; } static int show(DBusConnection *bus, char **args) { int r, ret = 0; - bool show_properties, new_line = false; + bool show_properties, show_status, new_line = false; char **name; + bool ellipsized = false; assert(bus); assert(args); show_properties = streq(args[0], "show"); + show_status = streq(args[0], "status"); if (show_properties) pager_open_if_enabled(); - if (show_properties && strv_length(args) <= 1) { - /* If not argument is specified inspect the manager - * itself */ + /* If no argument is specified inspect the manager itself */ + + if (show_properties && strv_length(args) <= 1) + return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized); + + if (show_status && strv_length(args) <= 1) + ret = show_all(args[0], bus, false, &new_line, &ellipsized); + else + STRV_FOREACH(name, args+1) { + uint32_t id; + + if (safe_atou32(*name, &id) < 0) { + _cleanup_free_ char *p = NULL, *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) + 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) + 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) + ret = r; + } + } + + if (ellipsized && !arg_quiet) + printf("Hint: Some lines were ellipsized, use -l to show in full.\n"); + + return ret; +} + +static int append_assignment(DBusMessageIter *iter, const char *assignment) { + const char *eq; + char *field; + DBusMessageIter sub; + int r; + + assert(iter); + assert(assignment); - return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line); + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; } - STRV_FOREACH(name, args+1) { - uint32_t id; + field = strndupa(assignment, eq - assignment); + eq ++; + + if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &field)) + return log_oom(); + + if (streq(field, "CPUAccounting") || + streq(field, "MemoryAccounting") || + streq(field, "BlockIOAccounting")) { + dbus_bool_t b; - if (safe_atou32(*name, &id) < 0) { + r = parse_boolean(eq); + if (r < 0) { + log_error("Failed to parse boolean assignment %s.", assignment); + return -EINVAL; + } - /* Interpret as unit name */ + b = r; + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "b", &sub) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &b)) + return log_oom(); - char *e, *p; - e = bus_path_escape(*name); - if (!e) - return -ENOMEM; - p = strappend("/org/freedesktop/systemd1/unit/", e); - free(e); - if (!p) - return -ENOMEM; + } else if (streq(field, "MemoryLimit")) { + off_t bytes; + uint64_t u; - r = show_one(args[0], bus, p, show_properties, &new_line); - free(p); + r = parse_bytes(eq, &bytes); + if (r < 0) { + log_error("Failed to parse bytes specification %s", assignment); + return -EINVAL; + } - if (r != 0) - ret = r; + u = bytes; + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "t", &sub) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &u)) + return log_oom(); - } else if (show_properties) { + } else if (streq(field, "CPUShares") || streq(field, "BlockIOWeight")) { + uint64_t u; + + r = safe_atou64(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } - /* Interpret as job id */ + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "t", &sub) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &u)) + return log_oom(); - char *p; - if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) - return -ENOMEM; + } else if (streq(field, "DevicePolicy")) { - r = show_one(args[0], bus, p, show_properties, &new_line); - free(p); + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "s", &sub) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &eq)) + return log_oom(); - if (r != 0) - ret = r; + } else if (streq(field, "DeviceAllow")) { + DBusMessageIter sub2; - } else { + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(ss)", &sub) || + !dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "(ss)", &sub2)) + return log_oom(); + + if (!isempty(eq)) { + const char *path, *rwm; + DBusMessageIter sub3; + char *e; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + rwm = e+1; + } else { + path = eq; + rwm = ""; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + if (!dbus_message_iter_open_container(&sub2, DBUS_TYPE_STRUCT, NULL, &sub3) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &path) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &rwm) || + !dbus_message_iter_close_container(&sub2, &sub3)) + return log_oom(); + } + + if (!dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + + } else if (streq(field, "BlockIOReadBandwidth") || streq(field, "BlockIOWriteBandwidth")) { + DBusMessageIter sub2; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(st)", &sub) || + !dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "(st)", &sub2)) + return log_oom(); + + if (!isempty(eq)) { + const char *path, *bandwidth; + DBusMessageIter sub3; + uint64_t u; + off_t bytes; + char *e; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + bandwidth = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = parse_bytes(bandwidth, &bytes); + if (r < 0) { + log_error("Failed to parse byte value %s.", bandwidth); + return -EINVAL; + } + + u = (uint64_t) bytes; + + if (!dbus_message_iter_open_container(&sub2, DBUS_TYPE_STRUCT, NULL, &sub3) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &path) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &u) || + !dbus_message_iter_close_container(&sub2, &sub3)) + return log_oom(); + } + + if (!dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + + } else if (streq(field, "BlockIODeviceWeight")) { + DBusMessageIter sub2; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(st)", &sub) || + !dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "(st)", &sub2)) + return log_oom(); + + if (!isempty(eq)) { + const char *path, *weight; + DBusMessageIter sub3; + uint64_t u; + char *e; - /* Interpret as PID */ + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + weight = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } - r = show_one_by_pid(args[0], bus, id, &new_line); - if (r != 0) - ret = r; + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = safe_atou64(weight, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, weight); + return -EINVAL; + } + if (!dbus_message_iter_open_container(&sub2, DBUS_TYPE_STRUCT, NULL, &sub3) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &path) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &u) || + !dbus_message_iter_close_container(&sub2, &sub3)) + return log_oom(); } + + if (!dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + + } else { + log_error("Unknown assignment %s.", assignment); + return -EINVAL; } - return ret; + if (!dbus_message_iter_close_container(iter, &sub)) + return log_oom(); + + return 0; } -static int dump(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; +static int set_property(DBusConnection *bus, char **args) { + + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + _cleanup_free_ char *n = NULL; + DBusMessageIter iter, sub; + dbus_bool_t runtime; DBusError error; + char **i; int r; - const char *text; dbus_error_init(&error); - pager_open_if_enabled(); + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetUnitProperties"); + if (!m) + return log_oom(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Dump"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } + dbus_message_iter_init_append(m, &iter); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + runtime = arg_runtime; - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_STRING, &text, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); - fputs(text, stdout); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &runtime) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub)) + return log_oom(); - r = 0; + STRV_FOREACH(i, args + 2) { + DBusMessageIter sub2; -finish: - if (m) - dbus_message_unref(m); + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) + return log_oom(); + + r = append_assignment(&sub2, *i); + if (r < 0) + return r; - if (reply) - dbus_message_unref(reply); + if (!dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); - dbus_error_free(&error); + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + return log_oom(); + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } - return r; + return 0; } static int snapshot(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; int r; - const char *name = "", *path, *id; dbus_bool_t cleanup = FALSE; DBusMessageIter iter, sub; const char + *path, *id, *interface = "org.freedesktop.systemd1.Unit", *property = "Id"; + _cleanup_free_ char *n = NULL; dbus_error_init(&error); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CreateSnapshot"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - if (strv_length(args) > 1) - name = args[1]; - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &cleanup, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + n = unit_name_mangle_with_suffix(args[1], ".snapshot"); + else + n = strdup(""); + if (!n) + return log_oom(); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "CreateSnapshot", + &reply, + NULL, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_BOOLEAN, &cleanup, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; + dbus_error_free(&error); + return -EIO; } dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + reply = NULL; + + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&iter, &sub); if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_get_basic(&sub, &id); if (!arg_quiet) puts(id); - r = 0; - -finish: - if (m) - dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } static int delete_snapshot(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - int r; - DBusError error; char **name; - assert(bus); assert(args); - dbus_error_init(&error); - STRV_FOREACH(name, args+1) { - const char *path = NULL; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Snapshot", - "Remove"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + _cleanup_free_ char *n = NULL; + int r; - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + n = unit_name_mangle_with_suffix(*name, ".snapshot"); + if (!n) + return log_oom(); - dbus_message_unref(m); - dbus_message_unref(reply); - m = reply = NULL; + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "RemoveSnapshot", + NULL, + NULL, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_INVALID); + if (r < 0) + return r; } - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } static int daemon_reload(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; int r; const char *method; - - dbus_error_init(&error); + DBusError error; if (arg_action == ACTION_RELOAD) method = "Reload"; @@ -3617,147 +4029,90 @@ static int daemon_reload(DBusConnection *bus, char **args) { /* "daemon-reload" */ "Reload"; } - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - - if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) { - /* There's always a fallback possible for - * legacy actions. */ - r = -EADDRNOTAVAIL; - goto finish; - } - - if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) { - /* On reexecution, we expect a disconnect, not - * a reply */ - r = 0; - goto finish; - } - + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + NULL, + &error, + DBUS_TYPE_INVALID); + + if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL) + /* There's always a fallback possible for + * legacy actions. */ + r = -EADDRNOTAVAIL; + else if ((r == -ETIMEDOUT || r == -ECONNRESET) && streq(method, "Reexecute")) + /* On reexecution, we expect a disconnect, not a + * reply */ + r = 0; + else if (r < 0) log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); dbus_error_free(&error); - return r; } static int reset_failed(DBusConnection *bus, char **args) { - DBusMessage *m = NULL; - int r; - DBusError error; + int r = 0; char **name; - assert(bus); - dbus_error_init(&error); - if (strv_length(args) <= 1) return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { - DBusMessage *reply; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ResetFailedUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + _cleanup_free_ char *n; - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + n = unit_name_mangle(*name); + if (!n) + return log_oom(); - dbus_message_unref(m); - dbus_message_unref(reply); - m = reply = NULL; + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ResetFailedUnit", + NULL, + NULL, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_INVALID); + if (r < 0) + return r; } - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - dbus_error_free(&error); - - return r; + return 0; } static int show_enviroment(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; int r; const char *interface = "org.freedesktop.systemd1.Manager", *property = "Environment"; - dbus_error_init(&error); - pager_open_if_enabled(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.DBus.Properties", - "Get"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&iter, &sub); @@ -3765,8 +4120,7 @@ static int show_enviroment(DBusConnection *bus, char **args) { if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_recurse(&sub, &sub2); @@ -3776,38 +4130,22 @@ static int show_enviroment(DBusConnection *bus, char **args) { if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_iter_get_basic(&sub2, &text); - printf("%s\n", text); + puts(text); dbus_message_iter_next(&sub2); } - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } static int switch_root(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; unsigned l; - const char *root, *init; - DBusError error; - int r; - - dbus_error_init(&error); + const char *root; + _cleanup_free_ char *init = NULL; l = strv_length(args); if (l < 2 || l > 3) { @@ -3816,56 +4154,44 @@ static int switch_root(DBusConnection *bus, char **args) { } root = args[1]; - init = l >= 3 ? args[2] : ""; - - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SwitchRoot"); - if (!m) { - log_error("Could not allocate message."); - return -ENOMEM; - } - if (!dbus_message_append_args( - m, - DBUS_TYPE_STRING, &root, - DBUS_TYPE_STRING, &init, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + if (l >= 3) + init = strdup(args[2]); + else { + parse_env_file("/proc/cmdline", WHITESPACE, + "init", &init, + NULL); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + if (!init) + init = strdup(""); } + if (!init) + return log_oom(); - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); + log_debug("switching root - root: %s; init: %s", root, init); - return r; + return bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SwitchRoot", + NULL, + NULL, + DBUS_TYPE_STRING, &root, + DBUS_TYPE_STRING, &init, + DBUS_TYPE_INVALID); } static int set_environment(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; DBusError error; - int r; const char *method; - DBusMessageIter iter, sub; - char **name; + DBusMessageIter iter; + int r; + + assert(bus); + assert(args); dbus_error_init(&error); @@ -3873,64 +4199,36 @@ static int set_environment(DBusConnection *bus, char **args) { ? "SetEnvironment" : "UnsetEnvironment"; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { - - log_error("Could not allocate message."); - return -ENOMEM; - } - - dbus_message_iter_init_append(m, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - STRV_FOREACH(name, args+1) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_iter_close_container(&iter, &sub)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 0; + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (!m) + return log_oom(); -finish: - if (m) - dbus_message_unref(m); + dbus_message_iter_init_append(m, &iter); - if (reply) - dbus_message_unref(reply); + r = bus_append_strv_iter(&iter, args + 1); + if (r < 0) + return log_oom(); - dbus_error_free(&error); + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } - return r; + return 0; } -static int enable_sysv_units(char **args) { +static int enable_sysv_units(const char *verb, char **args) { int r = 0; -#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA)) - const char *verb = args[0]; +#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG) unsigned f = 1, t = 1; - LookupPaths paths; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; if (arg_scope != UNIT_FILE_SYSTEM) return 0; @@ -3943,19 +4241,18 @@ static int enable_sysv_units(char **args) { /* Processes all SysV units, and reshuffles the array so that * afterwards only the native units remain */ - zero(paths); - r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, NULL, NULL, NULL); + r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL); if (r < 0) return r; r = 0; - for (f = 1; args[f]; f++) { + for (f = 0; args[f]; f++) { const char *name; - char *p; + _cleanup_free_ char *p = NULL, *q = NULL; bool found_native = false, found_sysv; unsigned c = 1; const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL }; - char **k, *l, *q = NULL; + char **k, *l; int j; pid_t pid; siginfo_t status; @@ -3969,21 +4266,19 @@ static int enable_sysv_units(char **args) { continue; STRV_FOREACH(k, paths.unit_path) { - p = NULL; - if (!isempty(arg_root)) asprintf(&p, "%s/%s/%s", arg_root, *k, name); else asprintf(&p, "%s/%s", *k, name); if (!p) { - log_error("No memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } found_native = access(p, F_OK) >= 0; free(p); + p = NULL; if (found_native) break; @@ -3992,24 +4287,20 @@ static int enable_sysv_units(char **args) { if (found_native) continue; - p = NULL; if (!isempty(arg_root)) asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name); else asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name); if (!p) { - log_error("No memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } p[strlen(p) - sizeof(".service") + 1] = 0; found_sysv = access(p, F_OK) >= 0; - if (!found_sysv) { - free(p); + if (!found_sysv) continue; - } /* Mark this entry, so that we don't try enabling it as native unit */ args[f] = (char*) ""; @@ -4027,10 +4318,7 @@ static int enable_sysv_units(char **args) { l = strv_join((char**)argv, " "); if (!l) { - log_error("No memory."); - free(q); - free(p); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -4040,8 +4328,6 @@ static int enable_sysv_units(char **args) { pid = fork(); if (pid < 0) { log_error("Failed to fork: %m"); - free(p); - free(q); r = -errno; goto finish; } else if (pid == 0) { @@ -4051,9 +4337,6 @@ static int enable_sysv_units(char **args) { _exit(EXIT_FAILURE); } - free(p); - free(q); - j = wait_for_terminate(pid, &status); if (j < 0) { log_error("Failed to wait for child: %s", strerror(-r)); @@ -4083,10 +4366,8 @@ static int enable_sysv_units(char **args) { } finish: - lookup_paths_free(&paths); - /* Drop all SysV units */ - for (f = 1, t = 1; args[f]; f++) { + for (f = 0, t = 0; args[f]; f++) { if (isempty(args[f])) continue; @@ -4100,42 +4381,81 @@ finish: return r; } +static int mangle_names(char **original_names, char ***mangled_names) { + char **i, **l, **name; + + l = new(char*, strv_length(original_names) + 1); + if (!l) + return log_oom(); + + i = l; + STRV_FOREACH(name, original_names) { + + /* When enabling units qualified path names are OK, + * too, hence allow them explicitly. */ + + if (is_path(*name)) + *i = strdup(*name); + else + *i = unit_name_mangle(*name); + + if (!*i) { + strv_free(l); + return log_oom(); + } + + i++; + } + + *i = NULL; + *mangled_names = l; + + return 0; +} + static int enable_unit(DBusConnection *bus, char **args) { const char *verb = args[0]; UnitFileChange *changes = NULL; unsigned n_changes = 0, i; int carries_install_info = -1; - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; int r; - DBusError error; + _cleanup_dbus_error_free_ DBusError error; + _cleanup_strv_free_ char **mangled_names = NULL; - r = enable_sysv_units(args); - if (r < 0) - return r; + dbus_error_init(&error); if (!args[1]) return 0; - dbus_error_init(&error); + r = mangle_names(args+1, &mangled_names); + if (r < 0) + return r; + + r = enable_sysv_units(verb, mangled_names); + if (r < 0) + return r; if (!bus || avoid_bus()) { if (streq(verb, "enable")) { - r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_enable(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "disable")) - r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes); + r = unit_file_disable(arg_scope, arg_runtime, arg_root, mangled_names, &changes, &n_changes); else if (streq(verb, "reenable")) { - r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_reenable(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "link")) - r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_link(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); else if (streq(verb, "preset")) { - r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_preset(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "mask")) - r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + 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, args+1, &changes, &n_changes); + 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"); @@ -4153,6 +4473,7 @@ static int enable_unit(DBusConnection *bus, char **args) { } } + r = 0; } else { const char *method; bool send_force = true, expect_carries_install_info = false; @@ -4178,6 +4499,8 @@ static int enable_unit(DBusConnection *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"); @@ -4187,14 +4510,13 @@ static int enable_unit(DBusConnection *bus, char **args) { "org.freedesktop.systemd1.Manager", method); if (!m) { - log_error("Out of memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } dbus_message_iter_init_append(m, &iter); - r = bus_append_strv_iter(&iter, args+1); + r = bus_append_strv_iter(&iter, mangled_names); if (r < 0) { log_error("Failed to append unit files."); goto finish; @@ -4282,31 +4604,37 @@ static int enable_unit(DBusConnection *bus, char **args) { } if (carries_install_info == 0) - log_warning("Warning: unit files do not carry install information. No operation executed."); + log_warning("The unit files have no [Install] section. They are not meant to be enabled\n" + "using systemctl.\n" + "Possible reasons for having this kind of units are:\n" + "1) A unit may be statically enabled by being symlinked from another unit's\n" + " .wants/ or .requires/ directory.\n" + "2) A unit's purpose may be to act as a helper for some other unit which has\n" + " a requirement dependency on it.\n" + "3) A unit may be started when needed via activation (socket, path, timer,\n" + " D-Bus, udev, scripted systemctl call, ...).\n"); finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - unit_file_changes_free(changes, n_changes); - dbus_error_free(&error); return r; } static int unit_is_enabled(DBusConnection *bus, char **args) { - DBusError error; + _cleanup_dbus_error_free_ DBusError error; int r; - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; bool enabled; char **name; + _cleanup_strv_free_ char **mangled_names = NULL; dbus_error_init(&error); - r = enable_sysv_units(args); + r = mangle_names(args+1, &mangled_names); + if (r < 0) + return r; + + r = enable_sysv_units(args[0], mangled_names); if (r < 0) return r; @@ -4314,14 +4642,13 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { if (!bus || avoid_bus()) { - STRV_FOREACH(name, args+1) { + STRV_FOREACH(name, mangled_names) { UnitFileState state; state = unit_file_get_state(arg_scope, arg_root, *name); - if (state < 0) { - r = state; - goto finish; - } + + if (state < 0) + return state; if (state == UNIT_FILE_ENABLED || state == UNIT_FILE_ENABLED_RUNTIME || @@ -4333,46 +4660,32 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { } } else { - STRV_FOREACH(name, args+1) { + STRV_FOREACH(name, mangled_names) { const char *s; - m = dbus_message_new_method_call( + r = bus_method_call_with_reply ( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "GetUnitFileState"); - if (!m) { - log_error("Out of memory"); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + "GetUnitFileState", + &reply, + NULL, + DBUS_TYPE_STRING, name, + DBUS_TYPE_INVALID); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } + if (r) + return r; if (!dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) { log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; + return -EIO; } - dbus_message_unref(m); dbus_message_unref(reply); - m = reply = NULL; + reply = NULL; if (streq(s, "enabled") || streq(s, "enabled-runtime") || @@ -4384,17 +4697,7 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { } } - r = enabled ? 0 : 1; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - return r; + return enabled ? 0 : 1; } static int systemctl_help(void) { @@ -4406,14 +4709,22 @@ static int systemctl_help(void) { " -h --help Show this help\n" " --version Show package version\n" " -t --type=TYPE List only units of a particular type\n" + " --state=STATE List only units with particular LOAD or SUB or ACTIVE state\n" " -p --property=NAME Show only properties by this name\n" - " -a --all Show all units/properties, including dead/empty ones\n" - " --failed Show only failed units\n" - " --full Don't ellipsize unit names on output\n" + " -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" + " --show-types When showing sockets, explicitly show their type\n" + " -i --ignore-inhibitors\n" + " When shutting down or sleeping, ignore inhibitors\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" " -H --host=[USER@]HOST\n" @@ -4428,40 +4739,43 @@ static int systemctl_help(void) { " --no-pager Do not pipe output into a pager\n" " --no-ask-password\n" " Do not ask for system passwords\n" - " --order When generating graph for dot, show only order\n" - " --require When generating graph for dot, show only requirement\n" " --system Connect to system manager\n" " --user Connect to user service manager\n" " --global Enable/disable unit files globally\n" + " --runtime Enable unit files only temporarily until next reboot\n" " -f --force When enabling unit files, override existing symlinks\n" " When shutting down, execute action immediately\n" " --root=PATH Enable unit files in the specified root directory\n" - " --runtime Enable unit files only temporarily until next reboot\n" - " -n --lines=INTEGER Journal entries to show\n" - " --follow Follow journal\n" + " -n --lines=INTEGER Number of journal entries to show\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, cat)\n\n" + " verbose, export, json, json-pretty, json-sse, cat)\n\n" "Unit Commands:\n" " list-units List loaded units\n" + " list-sockets List loaded sockets ordered by address\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" " restart [NAME...] Start or restart one or more units\n" " try-restart [NAME...] Restart one or more units if active\n" - " reload-or-restart [NAME...] Reload one or more units is possible,\n" + " reload-or-restart [NAME...] Reload one or more units if possible,\n" " otherwise start or restart\n" - " reload-or-try-restart [NAME...] Reload one or more units is possible,\n" + " reload-or-try-restart [NAME...] Reload one or more units if possible,\n" " otherwise restart if active\n" " isolate [NAME] Start one unit and stop all others\n" " kill [NAME...] Send signal to processes of a unit\n" " is-active [NAME...] Check whether units are active\n" + " is-failed [NAME...] Check whether units are failed\n" " 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" - " help [NAME...|PID...] Show manual for 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" " reset-failed [NAME...] Reset failed state for all, one, or more\n" " units\n" - " load [NAME...] Load one or more units\n\n" + " list-dependencies [NAME] Recursively show units which are required\n" + " or wanted by this unit or by which this\n" + " unit is required or wanted\n\n" "Unit File Commands:\n" " list-unit-files List installed unit files\n" " enable [NAME...] Enable one or more unit files\n" @@ -4469,17 +4783,16 @@ static int systemctl_help(void) { " reenable [NAME...] Reenable one or more unit files\n" " preset [NAME...] Enable/disable one or more unit files\n" " based on preset configuration\n" + " is-enabled [NAME...] Check whether unit files are enabled\n\n" " mask [NAME...] Mask one or more units\n" " unmask [NAME...] Unmask one or more units\n" " link [PATH...] Link one or more units files into\n" " the search path\n" - " is-enabled [NAME...] Check whether unit files are enabled\n\n" + " get-default Get the name of the default target\n" + " set-default NAME Set the default target\n\n" "Job Commands:\n" " list-jobs List jobs\n" " cancel [JOB...] Cancel all, one, or more jobs\n\n" - "Status Commands:\n" - " dump Dump server status\n" - " dot Dump dependency graph for dot(1)\n\n" "Snapshot Commands:\n" " snapshot [NAME] Create a snapshot\n" " delete [NAME...] Remove one or more snapshots\n\n" @@ -4496,12 +4809,13 @@ static int systemctl_help(void) { " emergency Enter system emergency mode\n" " halt Shut down and halt the system\n" " poweroff Shut down and power-off the system\n" - " reboot Shut down and reboot the system\n" + " reboot [ARG] Shut down and reboot the system\n" " kexec Shut down and reboot the system with kexec\n" " exit Request user instance exit\n" " switch-root [ROOT] [INIT] Change to a different root file system\n" " suspend Suspend the system\n" - " hibernate Hibernate the system\n", + " hibernate Hibernate the system\n" + " hybrid-sleep Hibernate and suspend the system\n", program_invocation_short_name); return 0; @@ -4509,7 +4823,7 @@ static int systemctl_help(void) { static int halt_help(void) { - printf("%s [OPTIONS...]\n\n" + printf("%s [OPTIONS...]%s\n\n" "%s the system.\n\n" " --help Show this help\n" " --halt Halt the machine\n" @@ -4518,9 +4832,9 @@ static int halt_help(void) { " -f --force Force immediate halt/power-off/reboot\n" " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n" " -d --no-wtmp Don't write wtmp record\n" - " -n --no-sync Don't sync before halt/power-off/reboot\n" " --no-wall Don't send wall message before halt/power-off/reboot\n", program_invocation_short_name, + arg_action == ACTION_REBOOT ? " [ARG]" : "", arg_action == ACTION_REBOOT ? "Reboot" : arg_action == ACTION_POWEROFF ? "Power off" : "Halt"); @@ -4573,10 +4887,29 @@ static int runlevel_help(void) { return 0; } +static int help_types(void) { + int i; + const char *t; + + puts("Available unit types:"); + for(i = 0; i < _UNIT_TYPE_MAX; i++) { + t = unit_type_to_string(i); + if (t) + puts(t); + } + + return 0; +} + static int systemctl_parse_argv(int argc, char *argv[]) { enum { ARG_FAIL = 0x100, + ARG_REVERSE, + ARG_AFTER, + ARG_BEFORE, + ARG_SHOW_TYPES, + ARG_IRREVERSIBLE, ARG_IGNORE_DEPENDENCIES, ARG_VERSION, ARG_USER, @@ -4586,54 +4919,55 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_NO_LEGEND, ARG_NO_PAGER, ARG_NO_WALL, - ARG_ORDER, - ARG_REQUIRE, ARG_ROOT, - ARG_FULL, ARG_NO_RELOAD, - ARG_KILL_MODE, ARG_KILL_WHO, ARG_NO_ASK_PASSWORD, ARG_FAILED, ARG_RUNTIME, - ARG_FOLLOW, - ARG_FORCE + ARG_FORCE, + ARG_PLAIN, + ARG_STATE }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "property", required_argument, NULL, 'p' }, - { "all", no_argument, NULL, 'a' }, - { "failed", no_argument, NULL, ARG_FAILED }, - { "full", no_argument, NULL, ARG_FULL }, - { "fail", no_argument, NULL, ARG_FAIL }, - { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "global", no_argument, NULL, ARG_GLOBAL }, - { "no-block", no_argument, NULL, ARG_NO_BLOCK }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { "quiet", no_argument, NULL, 'q' }, - { "order", no_argument, NULL, ARG_ORDER }, - { "require", no_argument, NULL, ARG_REQUIRE }, - { "root", required_argument, NULL, ARG_ROOT }, - { "force", no_argument, NULL, ARG_FORCE }, - { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, - { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */ - { "kill-who", required_argument, NULL, ARG_KILL_WHO }, - { "signal", required_argument, NULL, 's' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "host", required_argument, NULL, 'H' }, - { "privileged",no_argument, NULL, 'P' }, - { "runtime", no_argument, NULL, ARG_RUNTIME }, - { "lines", required_argument, NULL, 'n' }, - { "follow", no_argument, NULL, ARG_FOLLOW }, - { "output", required_argument, NULL, 'o' }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "type", required_argument, NULL, 't' }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "reverse", no_argument, NULL, ARG_REVERSE }, + { "after", no_argument, NULL, ARG_AFTER }, + { "before", no_argument, NULL, ARG_BEFORE }, + { "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 }, + { "ignore-inhibitors", no_argument, NULL, 'i' }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "global", no_argument, NULL, ARG_GLOBAL }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, + { "quiet", no_argument, NULL, 'q' }, + { "root", required_argument, NULL, ARG_ROOT }, + { "force", no_argument, NULL, ARG_FORCE }, + { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "runtime", no_argument, NULL, ARG_RUNTIME }, + { "lines", required_argument, NULL, 'n' }, + { "output", required_argument, NULL, 'o' }, + { "plain", no_argument, NULL, ARG_PLAIN }, + { "state", required_argument, NULL, ARG_STATE }, + {} }; int c; @@ -4641,37 +4975,90 @@ static int systemctl_parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:Pn:o:i", options, NULL)) >= 0) { switch (c) { case 'h': - systemctl_help(); - return 0; + return systemctl_help(); case ARG_VERSION: puts(PACKAGE_STRING); - puts(DISTRIBUTION); puts(SYSTEMD_FEATURES); return 0; - case 't': - arg_type = optarg; + case 't': { + char *word, *state; + size_t size; + + FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { + _cleanup_free_ char *type; + + type = strndup(word, size); + if (!type) + return -ENOMEM; + + if (streq(type, "help")) { + help_types(); + return 0; + } + + if (unit_type_from_string(type) >= 0) { + if (strv_push(&arg_types, type)) + return log_oom(); + type = NULL; + continue; + } + + /* It's much nicer to use --state= for + * load states, but let's support this + * in --types= too for compatibility + * with old versions */ + if (unit_load_state_from_string(optarg) >= 0) { + if (strv_push(&arg_states, type) < 0) + return log_oom(); + type = NULL; + continue; + } + + log_error("Unknown unit type or load state '%s'.", type); + log_info("Use -t help to see a list of allowed values."); + return -EINVAL; + } + break; + } case 'p': { - char **l; + /* 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); + if (!arg_properties) + return log_oom(); + } else { + char *word, *state; + size_t size; - if (!(l = strv_append(arg_property, optarg))) - return -ENOMEM; + FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { + char *prop; - strv_free(arg_property); - arg_property = l; + prop = strndup(word, size); + if (!prop) + return log_oom(); + + if (strv_push(&arg_properties, prop) < 0) { + free(prop); + return log_oom(); + } + } + } /* If the user asked for a particular * property, show it to him, even if it is * empty. */ arg_all = true; + break; } @@ -4679,10 +5066,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_all = true; break; + case ARG_REVERSE: + arg_dependency = DEPENDENCY_REVERSE; + break; + + case ARG_AFTER: + arg_dependency = DEPENDENCY_AFTER; + break; + + case ARG_BEFORE: + arg_dependency = DEPENDENCY_BEFORE; + break; + + case ARG_SHOW_TYPES: + arg_show_types = true; + break; + case ARG_FAIL: arg_job_mode = "fail"; break; + case ARG_IRREVERSIBLE: + arg_job_mode = "replace-irreversibly"; + break; + case ARG_IGNORE_DEPENDENCIES: arg_job_mode = "ignore-dependencies"; break; @@ -4715,24 +5122,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_no_wall = true; break; - case ARG_ORDER: - arg_dot = DOT_ORDER; - break; - - case ARG_REQUIRE: - arg_dot = DOT_REQUIRE; - break; - case ARG_ROOT: arg_root = optarg; break; - case ARG_FULL: + case 'l': arg_full = true; break; case ARG_FAILED: - arg_failed = true; + if (strv_extend(&arg_states, "failed") < 0) + return log_oom(); + break; case 'q': @@ -4743,14 +5144,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_force ++; break; - case ARG_FOLLOW: - arg_follow = true; - break; - case 'f': - /* -f is short for both --follow and --force! */ arg_force ++; - arg_follow = true; break; case ARG_NO_RELOAD: @@ -4761,10 +5156,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_kill_who = optarg; break; - case ARG_KILL_MODE: - arg_kill_mode = optarg; - break; - case 's': if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) { log_error("Failed to parse signal string %s.", optarg); @@ -4782,7 +5173,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { case 'H': arg_transport = TRANSPORT_SSH; - arg_host = optarg; + parse_user_at_host(optarg, &arg_user, &arg_host); break; case ARG_RUNTIME: @@ -4804,12 +5195,38 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } break; + case 'i': + arg_ignore_inhibitors = true; + break; + + case ARG_PLAIN: + arg_plain = true; + break; + + case ARG_STATE: { + char *word, *state; + size_t size; + + FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { + char *s; + + s = strndup(word, size); + if (!s) + return log_oom(); + + if (strv_push(&arg_states, s) < 0) { + free(s); + return log_oom(); + } + } + break; + } + case '?': return -EINVAL; default: - log_error("Unknown option code %c", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -4838,12 +5255,11 @@ static int halt_parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, 'f' }, { "wtmp-only", no_argument, NULL, 'w' }, { "no-wtmp", no_argument, NULL, 'd' }, - { "no-sync", no_argument, NULL, 'n' }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { NULL, 0, NULL, 0 } + {} }; - int c, runlevel; + int c, r, runlevel; assert(argc >= 0); assert(argv); @@ -4856,8 +5272,7 @@ static int halt_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - halt_help(); - return 0; + return halt_help(); case ARG_HALT: arg_action = ACTION_HALT; @@ -4884,16 +5299,13 @@ static int halt_parse_argv(int argc, char *argv[]) { arg_no_wtmp = true; break; - case 'n': - arg_no_sync = true; - break; - case ARG_NO_WALL: arg_no_wall = true; break; case 'i': case 'h': + case 'n': /* Compatibility nops */ break; @@ -4901,12 +5313,18 @@ static int halt_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } - if (optind < argc) { + if (arg_action == ACTION_REBOOT && argc == optind + 1) { + r = write_string_file(REBOOT_PARAM_FILE, argv[optind]); + if (r < 0) { + log_error("Failed to write reboot param to " + REBOOT_PARAM_FILE": %s", strerror(-r)); + return r; + } + } else if (optind < argc) { log_error("Too many arguments."); return -EINVAL; } @@ -4930,23 +5348,22 @@ static int parse_time_spec(const char *t, usec_t *_u) { } else { char *e = NULL; long hour, minute; - struct tm tm; + struct tm tm = {}; time_t s; usec_t n; errno = 0; hour = strtol(t, &e, 10); - if (errno != 0 || *e != ':' || hour < 0 || hour > 23) + if (errno > 0 || *e != ':' || hour < 0 || hour > 23) return -EINVAL; minute = strtol(e+1, &e, 10); - if (errno != 0 || *e != 0 || minute < 0 || minute > 59) + if (errno > 0 || *e != 0 || minute < 0 || minute > 59) return -EINVAL; n = now(CLOCK_REALTIME); s = (time_t) (n / USEC_PER_SEC); - zero(tm); assert_se(localtime_r(&s, &tm)); tm.tm_hour = (int) hour; @@ -4978,7 +5395,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { { "reboot", no_argument, NULL, 'r' }, { "kexec", no_argument, NULL, 'K' }, /* not documented extension */ { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { NULL, 0, NULL, 0 } + {} }; int c, r; @@ -4990,8 +5407,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - shutdown_help(); - return 0; + return shutdown_help(); case 'H': arg_action = ACTION_HALT; @@ -5038,12 +5454,11 @@ static int shutdown_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } - if (argc > optind) { + if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) { r = parse_time_spec(argv[optind], &arg_when); if (r < 0) { log_error("Failed to parse time specification: %s", argv[optind]); @@ -5052,8 +5467,11 @@ static int shutdown_parse_argv(int argc, char *argv[]) { } else arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE; - /* We skip the time argument */ - if (argc > optind + 1) + if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN) + /* No time argument for shutdown cancel */ + arg_wall = argv + optind; + else if (argc > optind + 1) + /* We skip the time argument */ arg_wall = argv + optind + 1; optind = argc; @@ -5071,7 +5489,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, ARG_HELP }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { NULL, 0, NULL, 0 } + {} }; static const struct { @@ -5103,8 +5521,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - telinit_help(); - return 0; + return telinit_help(); case ARG_NO_WALL: arg_no_wall = true; @@ -5114,8 +5531,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -5139,7 +5555,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { break; if (i >= ELEMENTSOF(table)) { - log_error("Unknown command %s.", argv[optind]); + log_error("Unknown command '%s'.", argv[optind]); return -EINVAL; } @@ -5158,7 +5574,7 @@ static int runlevel_parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, ARG_HELP }, - { NULL, 0, NULL, 0 } + {} }; int c; @@ -5170,15 +5586,14 @@ static int runlevel_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - runlevel_help(); + return runlevel_help(); return 0; case '?': return -EINVAL; default: - log_error("Unknown option code %c", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -5222,7 +5637,7 @@ static int parse_argv(int argc, char *argv[]) { * request to it. For now we simply * guess that it is Upstart. */ - execv("/lib/upstart/telinit", argv); + execv(TELINIT, argv); log_error("Couldn't find an alternative telinit implementation to spawn."); return -EIO; @@ -5238,7 +5653,7 @@ static int parse_argv(int argc, char *argv[]) { return systemctl_parse_argv(argc, argv); } -static int action_to_runlevel(void) { +_pure_ static int action_to_runlevel(void) { static const char table[_ACTION_MAX] = { [ACTION_HALT] = '0', @@ -5256,118 +5671,23 @@ static int action_to_runlevel(void) { return table[arg_action]; } -static int talk_upstart(void) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int previous, rl, r; - char - env1_buf[] = "RUNLEVEL=X", - env2_buf[] = "PREVLEVEL=X"; - char *env1 = env1_buf, *env2 = env2_buf; - const char *emit = "runlevel"; - dbus_bool_t b_false = FALSE; - DBusMessageIter iter, sub; - DBusConnection *bus; - - dbus_error_init(&error); - - if (!(rl = action_to_runlevel())) - return 0; - - if (utmp_get_runlevel(&previous, NULL) < 0) - previous = 'N'; - - if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) { - if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) { - r = 0; - goto finish; - } - - log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - if ((r = bus_check_peercred(bus)) < 0) { - log_error("Failed to verify owner of bus."); - goto finish; - } - - if (!(m = dbus_message_new_method_call( - "com.ubuntu.Upstart", - "/com/ubuntu/Upstart", - "com.ubuntu.Upstart0_6", - "EmitEvent"))) { - - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_iter_init_append(m, &iter); - - env1_buf[sizeof(env1_buf)-2] = rl; - env2_buf[sizeof(env2_buf)-2] = previous; - - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) || - !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) || - !dbus_message_iter_close_container(&iter, &sub) || - !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - - if (error_is_no_service(&error)) { - r = -EADDRNOTAVAIL; - goto finish; - } - - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 1; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - if (bus) { - dbus_connection_flush(bus); - dbus_connection_close(bus); - dbus_connection_unref(bus); - } - - dbus_error_free(&error); - - return r; -} - static int talk_initctl(void) { - struct init_request request; - int r, fd; + struct init_request request = {}; + int r; + _cleanup_close_ int fd = -1; char rl; - if (!(rl = action_to_runlevel())) + rl = action_to_runlevel(); + if (!rl) return 0; - zero(request); request.magic = INIT_MAGIC; request.sleeptime = 0; request.cmd = INIT_CMD_RUNLVL; request.runlevel = rl; - if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) { - + fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { if (errno == ENOENT) return 0; @@ -5377,11 +5697,9 @@ static int talk_initctl(void) { errno = 0; r = loop_write(fd, &request, sizeof(request), false) != sizeof(request); - close_nointr_nofail(fd); - - if (r < 0) { + if (r) { log_error("Failed to write to "INIT_FIFO": %m"); - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; } return 1; @@ -5401,9 +5719,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError } verbs[] = { { "list-units", LESS, 1, list_units }, { "list-unit-files", EQUAL, 1, list_unit_files }, + { "list-sockets", LESS, 1, list_sockets }, { "list-jobs", EQUAL, 1, list_jobs }, { "clear-jobs", EQUAL, 1, daemon_reload }, - { "load", MORE, 2, load_unit }, { "cancel", MORE, 2, cancel_job }, { "start", MORE, 2, start_unit }, { "stop", MORE, 2, start_unit }, @@ -5418,13 +5736,12 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */ { "isolate", EQUAL, 2, start_unit }, { "kill", MORE, 2, kill_unit }, - { "is-active", MORE, 2, check_unit }, - { "check", MORE, 2, check_unit }, + { "is-active", MORE, 2, check_unit_active }, + { "check", MORE, 2, check_unit_active }, + { "is-failed", MORE, 2, check_unit_failed }, { "show", MORE, 1, show }, - { "status", MORE, 2, show }, + { "status", MORE, 1, show }, { "help", MORE, 2, show }, - { "dump", EQUAL, 1, dump }, - { "dot", EQUAL, 1, dot }, { "snapshot", LESS, 2, snapshot }, { "delete", MORE, 2, delete_snapshot }, { "daemon-reload", EQUAL, 1, daemon_reload }, @@ -5438,6 +5755,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "kexec", EQUAL, 1, start_special }, { "suspend", EQUAL, 1, start_special }, { "hibernate", EQUAL, 1, start_special }, + { "hybrid-sleep", EQUAL, 1, start_special }, { "default", EQUAL, 1, start_special }, { "rescue", EQUAL, 1, start_special }, { "emergency", EQUAL, 1, start_special }, @@ -5452,6 +5770,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "unmask", MORE, 2, enable_unit }, { "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-property", MORE, 3, set_property }, }; int left; @@ -5478,7 +5800,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError break; if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation %s", argv[optind]); + log_error("Unknown operation '%s'.", argv[optind]); return -EINVAL; } } @@ -5523,7 +5845,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError !streq(verbs[i].verb, "preset") && !streq(verbs[i].verb, "mask") && !streq(verbs[i].verb, "unmask") && - !streq(verbs[i].verb, "link")) { + !streq(verbs[i].verb, "link") && + !streq(verbs[i].verb, "set-default") && + !streq(verbs[i].verb, "get-default")) { if (running_in_chroot() > 0) { log_info("Running in chroot, ignoring request."); @@ -5551,49 +5875,43 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError } static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) { - int fd; - struct msghdr msghdr; - struct iovec iovec[2]; - union sockaddr_union sockaddr; - struct sd_shutdown_command c; + _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, + .iov_len = offsetof(struct sd_shutdown_command, wall_message), + } + }; + struct msghdr msghdr = { + .msg_name = &sockaddr, + .msg_namelen = offsetof(struct sockaddr_un, sun_path) + + sizeof("/run/systemd/shutdownd") - 1, + .msg_iov = iovec, + .msg_iovlen = 1, + }; fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; - zero(c); - c.usec = t; - c.mode = mode; - c.dry_run = dry_run; - c.warn_wall = warn; - - zero(sockaddr); - sockaddr.sa.sa_family = AF_UNIX; - strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path)); - - zero(msghdr); - msghdr.msg_name = &sockaddr; - msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1; - - zero(iovec); - iovec[0].iov_base = (char*) &c; - iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message); - - if (isempty(message)) - msghdr.msg_iovlen = 1; - else { + if (!isempty(message)) { iovec[1].iov_base = (char*) message; iovec[1].iov_len = strlen(message); - msghdr.msg_iovlen = 2; + msghdr.msg_iovlen++; } - msghdr.msg_iov = iovec; - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { - close_nointr_nofail(fd); + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) return -errno; - } - close_nointr_nofail(fd); return 0; } @@ -5624,11 +5942,6 @@ static int start_with_fallback(DBusConnection *bus) { goto done; } - /* Hmm, talking to systemd via D-Bus didn't work. Then - * let's try to talk to Upstart via D-Bus. */ - if (talk_upstart() > 0) - goto done; - /* Nothing else worked, so let's try * /dev/initctl */ if (talk_initctl() > 0) @@ -5642,7 +5955,9 @@ done: return 0; } -static void halt_now(enum action a) { +static _noreturn_ void halt_now(enum action a) { + + _cleanup_free_ char *param = NULL; /* Make sure C-A-D is handled by the kernel from this * point on... */ @@ -5661,8 +5976,14 @@ static void halt_now(enum action a) { break; case ACTION_REBOOT: - log_info("Rebooting."); - reboot(RB_AUTOBOOT); + if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) == 0) { + log_info("Rebooting with arg '%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: @@ -5675,6 +5996,10 @@ static void halt_now(enum action a) { static int halt_main(DBusConnection *bus) { int r; + r = check_inhibitors(bus, arg_action); + if (r < 0) + return r; + if (geteuid() != 0) { /* Try logind if we are a normal user and no special * mode applies. Maybe PolicyKit allows us to shutdown @@ -5682,7 +6007,7 @@ static int halt_main(DBusConnection *bus) { if (arg_when <= 0 && !arg_dry && - !arg_force && + arg_force <= 0 && (arg_action == ACTION_POWEROFF || arg_action == ACTION_REBOOT)) { r = reboot_with_logind(bus, arg_action); @@ -5695,7 +6020,7 @@ static int halt_main(DBusConnection *bus) { } if (arg_when > 0) { - char *m; + _cleanup_free_ char *m; m = strv_join(arg_wall, " "); r = send_shutdownd(arg_when, @@ -5706,7 +6031,6 @@ static int halt_main(DBusConnection *bus) { arg_dry, !arg_no_wall, m); - free(m); if (r < 0) log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r)); @@ -5732,9 +6056,6 @@ static int halt_main(DBusConnection *bus) { } } - if (!arg_no_sync) - sync(); - if (arg_dry) return 0; @@ -5762,13 +6083,19 @@ static int runlevel_main(void) { int main(int argc, char*argv[]) { int r, retval = EXIT_FAILURE; DBusConnection *bus = NULL; - DBusError error; + _cleanup_dbus_error_free_ DBusError error; dbus_error_init(&error); + setlocale(LC_ALL, ""); log_parse_environment(); log_open(); + /* Explicitly not on_tty() to avoid setting cached value. + * This becomes relevant for piping output which might be + * ellipsized. */ + original_stdout_is_tty = isatty(STDOUT_FILENO); + r = parse_argv(argc, argv); if (r < 0) goto finish; @@ -5798,7 +6125,7 @@ int main(int argc, char*argv[]) { bus_connect_system_polkit(&bus, &error); private_bus = false; } else if (arg_transport == TRANSPORT_SSH) { - bus_connect_system_ssh(NULL, arg_host, &bus, &error); + bus_connect_system_ssh(arg_user, arg_host, &bus, &error); private_bus = false; } else assert_not_reached("Uh, invalid transport..."); @@ -5832,9 +6159,22 @@ int main(int argc, char*argv[]) { r = reload_with_fallback(bus); break; - case ACTION_CANCEL_SHUTDOWN: - r = send_shutdownd(0, 0, false, false, NULL); + case ACTION_CANCEL_SHUTDOWN: { + char *m = NULL; + + if (arg_wall) { + m = strv_join(arg_wall, " "); + if (!m) { + retval = EXIT_FAILURE; + goto finish; + } + } + r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m); + if (r < 0) + log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r)); + free(m); break; + } case ACTION_INVALID: case ACTION_RUNLEVEL: @@ -5851,11 +6191,11 @@ finish: dbus_connection_unref(bus); } - dbus_error_free(&error); - dbus_shutdown(); - strv_free(arg_property); + strv_free(arg_types); + strv_free(arg_states); + strv_free(arg_properties); pager_close(); ask_password_agent_close();