X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=a7c2eef5d4adabe74cff97cead0241ca3404c11c;hp=367dd8060aabef0f8acc8808c689f5fab851bcf4;hb=e8853816bf197afc71819e28f1316a5d5ee4b4c3;hpb=d3b52baff90876a648e321f3658a74bc58a8647c diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 367dd8060..a7c2eef5d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include +#include #include "log.h" #include "util.h" @@ -64,6 +66,7 @@ #include "logs-show.h" #include "path-util.h" #include "socket-util.h" +#include "fileio.h" static const char *arg_type = NULL; static const char *arg_load_state = NULL; @@ -77,6 +80,7 @@ static bool arg_no_pager = false; static bool arg_no_wtmp = false; static bool arg_no_wall = false; static bool arg_no_reload = false; +static bool arg_ignore_inhibitors = false; static bool arg_dry = false; static bool arg_quiet = false; static bool arg_full = false; @@ -99,6 +103,7 @@ static enum action { ACTION_EXIT, ACTION_SUSPEND, ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, ACTION_RUNLEVEL2, ACTION_RUNLEVEL3, ACTION_RUNLEVEL4, @@ -112,18 +117,12 @@ 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 unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; @@ -132,30 +131,12 @@ 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,6 +167,14 @@ static void polkit_agent_open_if_enabled(void) { } #endif +static const char *ansi_highlight(bool b) { + + if (!on_tty()) + return ""; + + return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF; +} + static const char *ansi_highlight_red(bool b) { if (!on_tty()) @@ -245,21 +234,18 @@ static void warn_wall(enum action a) { 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]) @@ -285,19 +271,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; @@ -308,7 +281,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; } @@ -331,6 +305,7 @@ static bool output_show_unit(const struct unit_info *u) { 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; const struct unit_info *u; + int job_count = 0; max_id_len = sizeof("UNIT")-1; active_len = sizeof("ACTIVE")-1; @@ -345,20 +320,24 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { max_id_len = MAX(max_id_len, strlen(u->id)); 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) { 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, @@ -372,23 +351,25 @@ 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; + char _cleanup_free_ *e = NULL; const char *on_loaded, *off_loaded; const char *on_active, *off_active; if (!output_show_unit(u)) continue; + if (!n_shown && !arg_no_legend) { + printf("%-*s %-6s %-*s %-*s ", id_len, "UNIT", "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")) { @@ -405,62 +386,73 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { e = arg_full ? NULL : ellipsize(u->id, id_len, 33); - printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ", + printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s", id_len, e ? e : u->id, on_loaded, 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 : ""); + job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); if (!arg_full && arg_no_pager) 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(true); + off = ansi_highlight(false); + } else { + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); + } if (arg_all) - printf("\n%u loaded units listed.\n" - "To show all installed unit files use 'systemctl list-unit-files'.\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 loaded units listed. Pass --all to see loaded but inactive units, too.\n" - "To show all installed unit files use 'systemctl list-unit-files'.\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 *reply = NULL; +static int get_unit_list(DBusConnection *bus, DBusMessage **reply, + struct unit_info **unit_infos, unsigned *c) { + DBusMessageIter iter, sub; + unsigned n_units = 0; int r; - DBusMessageIter iter, sub, sub2; - unsigned c = 0, n_units = 0; - struct unit_info *unit_infos = NULL; - pager_open_if_enabled(); + assert(bus); + assert(unit_infos); + assert(c); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "ListUnits", - &reply, + reply, NULL, DBUS_TYPE_INVALID); - if (r) - goto finish; + if (r < 0) + return r; - if (!dbus_message_iter_init(reply, &iter) || + 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); @@ -468,62 +460,47 @@ static int list_units(DBusConnection *bus, char **args) { while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { struct unit_info *u; - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } - - if (c >= n_units) { + if (*c >= n_units) { struct unit_info *w; - n_units = MAX(2*c, 16); - w = realloc(unit_infos, sizeof(struct unit_info) * n_units); - - if (!w) { - log_error("Failed to allocate unit array."); - r = -ENOMEM; - goto finish; - } + n_units = MAX(2 * *c, 16u); + w = realloc(*unit_infos, sizeof(struct unit_info) * n_units); + if (!w) + return log_oom(); - unit_infos = w; + *unit_infos = w; } - u = unit_infos+c; - - dbus_message_iter_recurse(&sub, &sub2); + u = *unit_infos + *c; - 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; - } + bus_parse_unit_info(&sub, u); dbus_message_iter_next(&sub); - c++; + (*c)++; } - if (c > 0) { - qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); - output_units_list(unit_infos, c); - } + if (*c > 0) + qsort(*unit_infos, *c, sizeof(struct unit_info), compare_unit_info); -finish: - if (reply) - dbus_message_unref(reply); + 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; - free(unit_infos); + pager_open_if_enabled(); - return r; + r = get_unit_list(bus, &reply, &unit_infos, &c); + if (r < 0) + return r; + + if (c > 0) + output_units_list(unit_infos, c); + + return 0; } static int compare_unit_file_list(const void *a, const void *b) { @@ -566,7 +543,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); @@ -577,7 +554,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; + char _cleanup_free_ *e = NULL; const char *on, *off; const char *id; @@ -605,8 +582,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) @@ -614,11 +589,11 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } static int list_unit_files(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; - 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; + int r; pager_open_if_enabled(); @@ -652,7 +627,7 @@ static int list_unit_files(DBusConnection *bus, char **args) { hashmap_free(h); } else { - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -661,15 +636,14 @@ static int list_unit_files(DBusConnection *bus, char **args) { &reply, NULL, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); @@ -678,36 +652,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); @@ -722,99 +687,86 @@ static int list_unit_files(DBusConnection *bus, char **args) { output_unit_file_list(units, c); } - r = 0; - -finish: - if (reply) - dbus_message_unref(reply); - - free(units); - - return r; + 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=\"grey66\"]", - "Conflicts", "[color=\"red\"]", - "ConflictedBy", "[color=\"red\"]", - "After", "[color=\"green\"]" - }; - - const char *c = NULL; - unsigned i; - - assert(name); - assert(prop); - assert(iter); +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); - for (i = 0; i < ELEMENTSOF(colors); i += 2) - if (streq(colors[i], prop)) { - c = colors[i+1]; - break; + for (i = level - 1; i >= 0; i--) { + len += 2; + if(len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; } - - if (!c) + printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERT : DRAW_TREE_SPACE)); + } + len += 2; + if(len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); return 0; + } + printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); - if (arg_dot != DOT_ALL) - if ((arg_dot == DOT_ORDER) != streq(prop, "After")) - return 0; + if(arg_full){ + printf("%s\n", name); + return 0; + } - switch (dbus_message_iter_get_arg_type(iter)) { + n = ellipsize(name, max_len-len, 100); + if(!n) + return log_oom(); - case DBUS_TYPE_ARRAY: + printf("%s\n", n); + return 0; +} - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; +static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) { + static const char dependencies[] = + "Requires\0" + "RequiresOverridable\0" + "Requisite\0" + "RequisiteOverridable\0" + "Wants\0"; - dbus_message_iter_recurse(iter, &sub); + _cleanup_free_ char *path; + const char *interface = "org.freedesktop.systemd1.Unit"; - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub, sub2, sub3; - 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); + int r = 0; + char **ret = NULL; - dbus_message_iter_next(&sub); - } + assert(bus); + assert(name); + assert(deps); - return 0; - } + path = unit_dbus_path_from_name(name); + if (path == NULL) { + r = -EINVAL; + goto finish; } - return 0; -} - -static int dot_one(DBusConnection *bus, const char *name, const char *path) { - DBusMessage *reply = NULL; - const char *interface = "org.freedesktop.systemd1.Unit"; - int r; - DBusMessageIter iter, sub, sub2, sub3; - - assert(path); - - 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) + 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; @@ -825,12 +777,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) { @@ -839,118 +786,124 @@ 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); + dbus_message_iter_next(&sub); - if (dot_one_property(name, prop, &sub3)) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } + if (!nulstr_contains(dependencies, prop)) + continue; - dbus_message_iter_next(&sub); - } + 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); -finish: - if (reply) - dbus_message_unref(reply); + while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) { + const char *s; + + 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 *reply = NULL; - 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); +} - 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) - goto finish; +static int list_dependencies_one(DBusConnection *bus, const char *name, int level, char **units, unsigned int branches) { + char _cleanup_strv_free_ **deps = NULL, **u; + char **c; + int r = 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."); - r = -EIO; - goto finish; - } + u = strv_append(units, name); + if (!u) + return log_oom(); - printf("digraph systemd {\n"); + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + return r; - 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; + qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; + STRV_FOREACH(c, deps) { + if (strv_contains(u, *c)) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); + if (r < 0) + return r; + continue; } - dbus_message_iter_recurse(&sub, &sub2); + r = list_dependencies_print(*c, level, branches, c[1] == NULL); + if (r < 0) + return r; - 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; - goto finish; + 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 ((r = dot_one(bus, id, unit_path)) < 0) - goto finish; - - /* printf("\t\"%s\";\n", id); */ - dbus_message_iter_next(&sub); } - printf("}\n"); + return 0; +} - log_info(" Color legend: black = Requires\n" - " dark blue = Requisite\n" - " dark grey = Wants\n" - " red = Conflicts\n" - " green = After\n"); +static int list_dependencies(DBusConnection *bus, char **args) { + _cleanup_free_ char *unit = NULL; + const char *u; - 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"); + assert(bus); - r = 0; + if (args[1]) { + unit = unit_name_mangle(args[1]); + if (!unit) + return log_oom(); + u = unit; + } else + u = SPECIAL_DEFAULT_TARGET; -finish: - if (reply) - dbus_message_unref(reply); + pager_open_if_enabled(); - return r; + puts(u); + + return list_dependencies_one(bus, u, 0, NULL, 0); } static int list_jobs(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; - int r; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; unsigned k = 0; + int r; pager_open_if_enabled(); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -959,15 +912,14 @@ static int list_jobs(DBusConnection *bus, char **args) { &reply, NULL, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); @@ -978,12 +930,11 @@ static int list_jobs(DBusConnection *bus, char **args) { 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; + char _cleanup_free_ *e = NULL; 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); @@ -995,13 +946,11 @@ static int list_jobs(DBusConnection *bus, char **args) { bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 || bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } e = arg_full ? NULL : ellipsize(name, 25, 33); printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state); - free(e); k++; @@ -1011,24 +960,23 @@ static int list_jobs(DBusConnection *bus, char **args) { if (on_tty()) printf("\n%u jobs listed.\n", k); - r = 0; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; + return 0; } static int load_unit(DBusConnection *bus, char **args) { - int r = 0; - char **name, *n; + char **name; assert(args); STRV_FOREACH(name, args+1) { + _cleanup_free_ char *n = NULL; + int r; + n = unit_name_mangle(*name); - r = bus_method_call_with_reply ( + if (!n) + return log_oom(); + + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -1036,20 +984,16 @@ static int load_unit(DBusConnection *bus, char **args) { "LoadUnit", NULL, NULL, - DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); - free(n); - if (r) - goto finish; + if (r < 0) + return r; } -finish: - return r; + return 0; } static int cancel_job(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; - int r = 0; char **name; assert(args); @@ -1058,71 +1002,50 @@ static int cancel_job(DBusConnection *bus, char **args) { return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { - unsigned id; - const char *path; + uint32_t id; + int r; - r = safe_atou(*name, &id); + r = safe_atou32(*name, &id); if (r < 0) { log_error("Failed to parse job id: %s", strerror(-r)); - goto finish; + return r; } - assert_cc(sizeof(uint32_t) == sizeof(id)); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "GetJob", - &reply, - NULL, - DBUS_TYPE_UINT32, &id, - DBUS_TYPE_INVALID); - if (r) - goto finish; - - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply"); - dbus_message_unref(reply); - r = -EIO; - goto finish; - } - dbus_message_unref(reply); - - r = bus_method_call_with_reply ( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Job", - "Cancel", + "CancelJob", NULL, NULL, + DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID); - if (r) - goto finish; + if (r < 0) + return r; } -finish: - return r; + return 0; } static bool need_daemon_reload(DBusConnection *bus, const char *unit) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; dbus_bool_t b = FALSE; DBusMessageIter iter, sub; const char *interface = "org.freedesktop.systemd1.Unit", *property = "NeedDaemonReload", *path; - char *n; + _cleanup_free_ char *n = NULL; int r; /* We ignore all errors here, since this is used to show a warning only */ n = unit_name_mangle(unit); - r = bus_method_call_with_reply ( + if (!n) + return log_oom(); + + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -1130,19 +1053,20 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { "GetUnit", &reply, NULL, - DBUS_TYPE_STRING, n ? (const char**) &n : &unit, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); - free(n); - if (r) - goto finish; + if (r < 0) + return r; if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) - goto finish; + return -EIO; dbus_message_unref(reply); - r = bus_method_call_with_reply ( + reply = NULL; + + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", path, @@ -1153,24 +1077,18 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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 (reply) - dbus_message_unref(reply); - return b; } @@ -1182,15 +1100,15 @@ typedef struct WaitData { } WaitData; static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) { - DBusError error; + DBusError _cleanup_dbus_error_free_ 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), @@ -1203,7 +1121,6 @@ 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; if (dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &id, @@ -1211,10 +1128,8 @@ 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); + free(set_remove(d->set, (char*) path)); if (!isempty(result)) d->result = strdup(result); @@ -1222,7 +1137,7 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me if (!isempty(unit)) d->name = strdup(unit); - goto finish; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } #ifndef LEGACY dbus_error_free(&error); @@ -1231,47 +1146,21 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me 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); + free(set_remove(d->set, (char*) path)); 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; } @@ -1322,15 +1211,18 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { return -ECONNREFUSED; } - if (!arg_quiet && d.result) { + if (!d.result) + goto free_name; + + 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' for details.", strna(d.name)); + 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' for details.", strna(d.name), strna(d.name)); + log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d.name), strna(d.name)); } if (streq_ptr(d.result, "timeout")) @@ -1343,28 +1235,34 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { free(d.result); d.result = NULL; + free_name: free(d.name); d.name = NULL; } - /* This is slightly dirty, since we don't undo the filter registration. */ + dbus_connection_remove_filter(bus, wait_filter, &d); return r; } -static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { - DBusMessage *reply = NULL; +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; + const char *state, *path; + DBusError error; int r; - char *n; assert(name); + dbus_error_init(&error); + n = unit_name_mangle(name); + if (!n) + return log_oom(); + r = bus_method_call_with_reply ( bus, "org.freedesktop.systemd1", @@ -1372,26 +1270,28 @@ static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { "org.freedesktop.systemd1.Manager", "GetUnit", &reply, - NULL, - DBUS_TYPE_STRING, n ? &n : &name, + &error, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); - free(n); - if (r) { - if ((r != -ENOMEM) && (!quiet)) + if (r < 0) { + dbus_error_free(&error); + + if (!quiet) puts("unknown"); - goto finish; + return 0; } if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { log_error("Failed to parse reply."); - r = -EIO; - goto finish; + return -EIO; } dbus_message_unref(reply); - r = bus_method_call_with_reply ( + reply = NULL; + + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", path, @@ -1402,22 +1302,23 @@ static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); @@ -1425,41 +1326,71 @@ static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { if (!quiet) puts(state); - if (streq(state, "active") || streq(state, "reloading")) - r = 0; - else - r = 3; /* According to LSB: "program is not running" */ - -finish: - if (reply) - dbus_message_unref(reply); - - return r; + return strv_find(check_states, state) ? 1 : 0; } static void check_triggering_units( DBusConnection *bus, const char *unit_name) { - DBusMessage _cleanup_dbus_msg_unref_ *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"; - + *load_state_property = "LoadState", + *triggered_by_property = "TriggeredBy", + *state; char _cleanup_free_ *unit_path = NULL, *n = NULL; bool print_warning_label = true; int r; n = unit_name_mangle(unit_name); - unit_path = unit_dbus_path_from_name(n ? n : unit_name); + if (!n) { + log_oom(); + return; + } + unit_path = unit_dbus_path_from_name(n); if (!unit_path) { - log_error("Could not allocate dbus path."); + log_oom(); return; } - r = bus_method_call_with_reply ( + 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; + } + + 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, @@ -1470,7 +1401,7 @@ static void check_triggering_units( DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &triggered_by_property, DBUS_TYPE_INVALID); - if (r) + if (r < 0) return; if (!dbus_message_iter_init(reply, &iter) || @@ -1484,6 +1415,12 @@ static void check_triggering_units( sub = iter; while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + 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."); @@ -1492,14 +1429,15 @@ static void check_triggering_units( dbus_message_iter_get_basic(&sub, &service_trigger); - r = check_one_unit(bus, service_trigger, true); + r = check_one_unit(bus, service_trigger, (char**) check_states, true); if (r < 0) return; - if (r == 0) { + if (r > 0) { if (print_warning_label) { log_warning("Warning: Stopping %s, but it can still be activated by:", unit_name); print_warning_label = false; } + log_warning(" %s", service_trigger); } @@ -1515,10 +1453,10 @@ static int start_unit_one( DBusError *error, Set *s) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *n; const char *path; int r; - _cleanup_free_ char *n, *p = NULL; assert(method); assert(name); @@ -1548,15 +1486,14 @@ static int start_unit_one( else log_error("Failed to issue method call: %s", bus_error_message(error)); - 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, n)) @@ -1564,83 +1501,60 @@ static int start_unit_one( n, arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); if (s) { + char *p; + p = strdup(path); - if (!p) { - r = log_oom(); - goto finish; - } + if (!p) + return log_oom(); r = set_put(s, p); if (r < 0) { + free(p); log_error("Failed to add path to set."); - goto finish; + return r; } - - p = NULL; } - /* When stopping a unit warn if it can still be triggered by - * another active unit (socket, path, timer) */ - if (!arg_quiet && streq(method, "StopUnit")) - check_triggering_units(bus, name); - - r = 0; - -finish: - if (reply) - dbus_message_unref(reply); - - 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; + Set _cleanup_set_free_free_ *s = NULL; + DBusError _cleanup_dbus_error_free_ error; char **name; dbus_error_init(&error); @@ -1650,6 +1564,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" : @@ -1666,42 +1581,33 @@ 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) { ret = enable_wait_for_jobs(bus); if (ret < 0) { log_error("Could not watch jobs: %s", strerror(-ret)); - goto finish; + return ret; } s = set_new(string_hash_func, string_compare_func); - if (!s) { - ret = log_oom(); - goto finish; - } + if (!s) + return log_oom(); } if (one_name) { @@ -1720,16 +1626,20 @@ static int start_unit(DBusConnection *bus, char **args) { if (!arg_no_block) { r = wait_for_jobs(bus, s); - if (r < 0) { - ret = r; - goto finish; + if (r < 0) + return r; + + /* 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); } } -finish: - set_free_free(s); - dbus_error_free(&error); - return ret; } @@ -1740,6 +1650,9 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) { const char *method; dbus_bool_t interactive = true; + if (!bus) + return -EIO; + polkit_agent_open_if_enabled(); switch (a) { @@ -1760,22 +1673,156 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) { method = "Hibernate"; break; - default: - return -EINVAL; + case ACTION_HYBRID_SLEEP: + method = "HybridSleep"; + break; + + default: + return -EINVAL; + } + + 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 +} + +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; + } + + 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; + } + + 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; + } + + 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); + } + + dbus_message_iter_recurse(&iter, &sub); + + /* 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 (sd_session_get_uid(*s, &uid) < 0 || uid == getuid()) + continue; + + if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) + continue; + + 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++; } - 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); + 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 } @@ -1787,6 +1834,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; @@ -1811,20 +1862,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" */ @@ -1832,10 +1890,37 @@ 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) + r = 0; + } + + return r; +} + +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); + + STRV_FOREACH(name, args+1) { + 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; } @@ -1843,17 +1928,23 @@ static int check_unit(DBusConnection *bus, char **args) { } static int kill_unit(DBusConnection *bus, char **args) { + char **name; int r = 0; - char **name, *n; + assert(bus); assert(args); if (!arg_kill_who) arg_kill_who = "all"; STRV_FOREACH(name, args+1) { + _cleanup_free_ char *n = NULL; + n = unit_name_mangle(*name); - r = bus_method_call_with_reply ( + if (!n) + return log_oom(); + + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -1861,14 +1952,157 @@ static int kill_unit(DBusConnection *bus, char **args) { "KillUnit", NULL, NULL, - DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_STRING, &n, DBUS_TYPE_STRING, &arg_kill_who, DBUS_TYPE_INT32, &arg_signal, DBUS_TYPE_INVALID); - free(n); - if (r) + if (r < 0) + return r; + } + return 0; +} + +static int set_cgroup(DBusConnection *bus, char **args) { + _cleanup_free_ char *n = NULL; + const char *method, *runtime; + char **argument; + int r; + + assert(bus); + assert(args); + + method = + streq(args[0], "set-cgroup") ? "SetUnitControlGroup" : + streq(args[0], "unset-cgroup") ? "UnsetUnitControlGroup" + : "UnsetUnitControlGroupAttribute"; + + runtime = arg_runtime ? "runtime" : "persistent"; + + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); + + STRV_FOREACH(argument, args + 2) { + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + NULL, + NULL, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_STRING, argument, + DBUS_TYPE_STRING, &runtime, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + } + + return 0; +} + +static int set_cgroup_attr(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + DBusError error; + DBusMessageIter iter; + _cleanup_free_ char *n = NULL; + const char *runtime; + int r; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + runtime = arg_runtime ? "runtime" : "persistent"; + + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetUnitControlGroupAttribute"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[2])) + return log_oom(); + + r = bus_append_strv_iter(&iter, args + 3); + if (r < 0) + return log_oom(); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime)) + 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 0; +} + +static int get_cgroup_attr(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *n = NULL; + char **argument; + int r; + + assert(bus); + assert(args); + + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); + + STRV_FOREACH(argument, args + 2) { + _cleanup_strv_free_ char **list = NULL; + DBusMessageIter iter; + char **a; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitControlGroupAttribute", + &reply, + NULL, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_STRING, argument, + DBUS_TYPE_INVALID); + if (r < 0) return r; + + if (!dbus_message_iter_init(reply, &iter)) { + log_error("Failed to initialize iterator."); + return -EIO; + } + + r = bus_parse_strv_iter(&iter, &list); + if (r < 0) { + log_error("Failed to parse value list."); + return r; + } + + STRV_FOREACH(a, list) { + if (endswith(*a, "\n")) + fputs(*a, stdout); + else + puts(*a); + } } + return 0; } @@ -1918,7 +2152,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 || @@ -1933,8 +2168,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; @@ -1946,8 +2181,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) || @@ -2017,6 +2255,9 @@ typedef struct UnitStatusInfo { unsigned n_connections; bool accept; + /* Pairs of type, path */ + char **listen; + /* Device */ const char *sysfs_path; @@ -2033,12 +2274,29 @@ static void print_status_info(UnitStatusInfo *i) { 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; + int maxlen = 8; /* a value that'll suffice most of the time */ + char **t, **t2; assert(i); + STRV_FOREACH_PAIR(t, t2, i->listen) + maxlen = MAX(maxlen, (int)(sizeof("Listen") - 1 + strlen(*t))); + if (i->accept) + maxlen = MAX(maxlen, (int)sizeof("Accept") - 1); + if (i->main_pid > 0) + maxlen = MAX(maxlen, (int)sizeof("Main PID") - 1); + else if (i->control_pid > 0) + maxlen = MAX(maxlen, (int)sizeof("Control") - 1); + /* This shows pretty information about a unit. See * print_property() for a low-level property printer */ @@ -2050,7 +2308,7 @@ 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(" %*s: unit currently follows state of %s\n", maxlen, "Follow", i->following); if (streq_ptr(i->load_state, "error")) { on = ansi_highlight_red(true); @@ -2061,13 +2319,17 @@ static void print_status_info(UnitStatusInfo *i) { 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(" %*s: %s%s%s (Reason: %s)\n", + maxlen, "Loaded", 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(" %*s: %s%s%s (%s; %s)\n", + maxlen, "Loaded", 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(" %*s: %s%s%s (%s)\n", + maxlen, "Loaded", on, strna(i->load_state), off, path); else - printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off); + printf(" %*s: %s%s%s\n", + maxlen, "Loaded", on, strna(i->load_state), off); ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state; @@ -2081,16 +2343,11 @@ static void print_status_info(UnitStatusInfo *i) { on = off = ""; if (ss) - printf("\t Active: %s%s (%s)%s", - on, - strna(i->active_state), - ss, - off); + printf(" %*s: %s%s (%s)%s", + maxlen, "Active", on, strna(i->active_state), ss, off); else - printf("\t Active: %s%s%s", - on, - strna(i->active_state), - off); + printf(" %*s: %s%s%s", + maxlen, "Active", on, strna(i->active_state), off); if (!isempty(i->result) && !streq(i->result, "success")) printf(" (Result: %s)", i->result); @@ -2102,7 +2359,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) @@ -2113,49 +2370,41 @@ 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); + printf(" %*s start condition failed at %s; %s\n", maxlen, "", s2, s1); else if (s2) - printf("\t start condition failed at %s\n", s2); + printf(" %*s start condition failed at %s\n", maxlen, "", s2); } if (i->sysfs_path) - printf("\t Device: %s\n", i->sysfs_path); + printf(" %*s: %s\n", maxlen, "Device", i->sysfs_path); if (i->where) - printf("\t Where: %s\n", i->where); + printf(" %*s: %s\n", maxlen, "Where", i->where); if (i->what) - printf("\t What: %s\n", i->what); + printf(" %*s: %s\n", maxlen, "What", i->what); - if (!strv_isempty(i->documentation)) { - char **t; - bool first = true; + STRV_FOREACH(t, i->documentation) + printf(" %*s %s\n", maxlen+1, 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", maxlen - (int)strlen(*t), "Listen", *t, *t2); if (i->accept) - printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); + printf(" %*s: %u; Connected: %u\n", maxlen, "Accepted", 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(" %*s: %u %s=%s ", maxlen, "Process", p->pid, p->name, strna(argv)); good = is_clean_exit_lsb(p->code, p->status, NULL); if (!good) { @@ -2191,18 +2440,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(" %*s: %u", maxlen, "Main PID", (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)); @@ -2219,41 +2464,43 @@ 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(" %*s: %u", i->main_pid ? 0 : maxlen, "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(" %*s: \"%s\"\n", maxlen, "Status", i->status_text); - if (i->default_control_group) { + if (i->default_control_group && + (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_by_spec(i->default_control_group, false) == 0)) { unsigned c; - printf("\t CGroup: %s\n", i->default_control_group); + printf(" %*s: %s\n", maxlen, "CGroup", i->default_control_group); if (arg_transport != TRANSPORT_SSH) { unsigned k = 0; pid_t extra[2]; + char prefix[maxlen + 4]; + memset(prefix, ' ', sizeof(prefix) - 1); + prefix[sizeof(prefix) - 1] = '\0'; c = columns(); - if (c > 18) - c -= 18; + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; else c = 0; @@ -2263,21 +2510,22 @@ 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_by_spec(i->default_control_group, prefix, + c, false, extra, k, flags); } } if (i->id && arg_transport != TRANSPORT_SSH) { - int flags = - arg_all * OUTPUT_SHOW_ALL | - arg_follow * OUTPUT_FOLLOW | - !arg_quiet * OUTPUT_WARN_CUTOFF | - on_tty() * OUTPUT_COLOR; - printf("\n"); - show_journal_by_unit(i->id, arg_output, 0, + show_journal_by_unit(stdout, + i->id, + arg_output, + 0, i->inactive_exit_timestamp_monotonic, - arg_lines, flags); + arg_lines, + getuid(), + flags, + arg_scope == UNIT_FILE_SYSTEM); } if (i->need_daemon_reload) @@ -2302,7 +2550,7 @@ static void show_unit_help(UnitStatusInfo *i) { if (startswith(*p, "man:")) { size_t k; char *e = NULL; - char *page = NULL, *section = NULL; + char _cleanup_free_ *page = NULL, *section = NULL; const char *args[4] = { "man", NULL, NULL, NULL }; pid_t pid; @@ -2313,14 +2561,8 @@ static void show_unit_help(UnitStatusInfo *i) { if (e) { page = strndup((*p) + 4, e - *p - 4); - if (!page) { - log_oom(); - return; - } - section = strndup(e + 1, *p + k - e - 2); - if (!section) { - free(page); + if (!page || !section) { log_oom(); return; } @@ -2333,8 +2575,6 @@ static void show_unit_help(UnitStatusInfo *i) { pid = fork(); if (pid < 0) { log_error("Failed to fork: %m"); - free(page); - free(section); continue; } @@ -2345,9 +2585,6 @@ 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); @@ -2507,6 +2744,33 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn 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, "Documentation")) { @@ -2515,16 +2779,13 @@ 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; - - strv_free(i->documentation); - i->documentation = l; + r = strv_extend(&i->documentation, s); + if (r < 0) + return r; dbus_message_iter_next(&sub); } @@ -2637,6 +2898,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; @@ -2651,6 +2913,24 @@ 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, "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) + 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, "Timers")) { DBusMessageIter sub, sub2; @@ -2668,8 +2948,8 @@ static int print_property(const char *name, DBusMessageIter *iter) { printf("%s={ value=%s ; next_elapse=%s }\n", base, - format_timespan(timespan1, sizeof(timespan1), value), - format_timespan(timespan2, sizeof(timespan2), next_elapse)); + format_timespan(timespan1, sizeof(timespan1), value, 0), + format_timespan(timespan2, sizeof(timespan2), next_elapse, 0)); } dbus_message_iter_next(&sub); @@ -2690,7 +2970,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { 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) { - printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n", + printf("ControlGroupAttributes={ controller=%s ; attribute=%s ; value=\"%s\" }\n", controller, attr, value); @@ -2711,7 +2991,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { zero(info); if (exec_status_info_deserialize(&sub, &info) >= 0) { char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; - char *t; + char _cleanup_free_ *t; t = strv_join(info.argv, " "); @@ -2727,8 +3007,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); @@ -2753,7 +3031,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { } static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { - DBusMessage *reply = NULL; + DBusMessage _cleanup_free_ *reply = NULL; const char *interface = ""; int r; DBusMessageIter iter, sub, sub2, sub3; @@ -2765,7 +3043,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo zero(info); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", path, @@ -2775,15 +3053,14 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); @@ -2796,24 +3073,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); @@ -2822,11 +3088,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); @@ -2842,6 +3106,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo } strv_free(info.documentation); + strv_free(info.listen); if (!streq_ptr(info.active_state, "active") && !streq_ptr(info.active_state, "reloading") && @@ -2854,22 +3119,18 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo exec_status_info_free(p); } -finish: - if (reply) - dbus_message_unref(reply); - return r; } static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, bool *new_line) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; const char *path = NULL; - DBusError error; + DBusError _cleanup_dbus_error_free_ error; int r; dbus_error_init(&error); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -2879,85 +3140,105 @@ static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, NULL, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); + return r; +} -finish: - if (reply) - dbus_message_unref(reply); +static int show_all(const char* verb, DBusConnection *bus, bool show_properties, bool *new_line) { + _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; - dbus_error_free(&error); + r = get_unit_list(bus, &reply, &unit_infos, &c); + if (r < 0) + return r; + + for (u = unit_infos; u < unit_infos + c; u++) { + char _cleanup_free_ *p = NULL; + + 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); + if (r != 0) + return r; + } - 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; 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); - } + + if (show_status && strv_length(args) <= 1) + return show_all(args[0], bus, false, &new_line); STRV_FOREACH(name, args+1) { uint32_t id; if (safe_atou32(*name, &id) < 0) { - char *p, *n; + _cleanup_free_ char *p = NULL, *n = NULL; /* Interpret as unit name */ n = unit_name_mangle(*name); - p = unit_dbus_path_from_name(n ? n : *name); - free(n); + 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); - free(p); - if (r != 0) ret = r; } else if (show_properties) { + _cleanup_free_ char *p = NULL; /* Interpret as job id */ - - char *p; if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) return log_oom(); r = show_one(args[0], bus, p, show_properties, &new_line); - free(p); - if (r != 0) ret = r; } else { - /* Interpret as PID */ - r = show_one_by_pid(args[0], bus, id, &new_line); if (r != 0) ret = r; @@ -2968,7 +3249,7 @@ static int show(DBusConnection *bus, char **args) { } static int dump(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; + _cleanup_free_ DBusMessage *reply = NULL; DBusError error; int r; const char *text; @@ -2977,7 +3258,7 @@ static int dump(DBusConnection *bus, char **args) { pager_open_if_enabled(); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -2986,46 +3267,42 @@ static int dump(DBusConnection *bus, char **args) { &reply, NULL, DBUS_TYPE_INVALID); - if (r) - goto finish; + if (r < 0) + return r; 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; + dbus_error_free(&error); + return -EIO; } fputs(text, stdout); - -finish: - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } static int snapshot(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusError error; int r; dbus_bool_t cleanup = FALSE; DBusMessageIter iter, sub; const char - *name = "", *path, *id, + *path, *id, *interface = "org.freedesktop.systemd1.Unit", *property = "Id"; - char *n; + _cleanup_free_ char *n = NULL; dbus_error_init(&error); if (strv_length(args) > 1) - name = args[1]; + n = snapshot_name_mangle(args[1]); + else + n = strdup(""); + if (!n) + return log_oom(); - n = unit_name_mangle(name); r = bus_method_call_with_reply ( bus, "org.freedesktop.systemd1", @@ -3034,22 +3311,23 @@ static int snapshot(DBusConnection *bus, char **args) { "CreateSnapshot", &reply, NULL, - DBUS_TYPE_STRING, n ? (const char**) &n : &name, + DBUS_TYPE_STRING, &n, DBUS_TYPE_BOOLEAN, &cleanup, DBUS_TYPE_INVALID); - free(n); - if (r) - goto finish; + 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_error_free(&error); + return -EIO; } dbus_message_unref(reply); + reply = NULL; + r = bus_method_call_with_reply ( bus, "org.freedesktop.systemd1", @@ -3061,22 +3339,20 @@ static int snapshot(DBusConnection *bus, char **args) { DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); @@ -3084,70 +3360,37 @@ static int snapshot(DBusConnection *bus, char **args) { if (!arg_quiet) puts(id); -finish: - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } static int delete_snapshot(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; - int r = 0; - DBusError error; char **name; assert(args); - dbus_error_init(&error); - STRV_FOREACH(name, args+1) { - const char *path = NULL; - char *n; + _cleanup_free_ char *n = NULL; + int r; - n = unit_name_mangle(*name); - r = bus_method_call_with_reply ( + n = snapshot_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", - "GetUnit", - &reply, - NULL, - DBUS_TYPE_STRING, n ? &n : name, - DBUS_TYPE_INVALID); - free(n); - if (r) - 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; - dbus_message_unref(reply); - dbus_error_free(&error); - goto finish; - } - dbus_message_unref(reply); - - r = bus_method_call_with_reply ( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Snapshot", - "Remove", + "RemoveSnapshot", NULL, NULL, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); - if (r) - goto finish; + if (r < 0) + return r; } -finish: - return r; + return 0; } static int daemon_reload(DBusConnection *bus, char **args) { @@ -3175,7 +3418,7 @@ static int daemon_reload(DBusConnection *bus, char **args) { /* "daemon-reload" */ "Reload"; } - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -3193,23 +3436,28 @@ static int daemon_reload(DBusConnection *bus, char **args) { /* On reexecution, we expect a disconnect, not * a reply */ r = 0; - else if (r) + else if (r < 0) log_error("Failed to issue method call: %s", bus_error_message(&error)); - dbus_error_free(&error); + dbus_error_free(&error); return r; } static int reset_failed(DBusConnection *bus, char **args) { int r = 0; - char **name, *n; + char **name; if (strv_length(args) <= 1) return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { + _cleanup_free_ char *n; + n = unit_name_mangle(*name); - r = bus_method_call_with_reply ( + if (!n) + return log_oom(); + + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -3217,19 +3465,17 @@ static int reset_failed(DBusConnection *bus, char **args) { "ResetFailedUnit", NULL, NULL, - DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); - free(n); - if (r) - goto finish; + if (r < 0) + return r; } -finish: - return r; + return 0; } static int show_enviroment(DBusConnection *bus, char **args) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; int r; const char @@ -3238,7 +3484,7 @@ static int show_enviroment(DBusConnection *bus, char **args) { pager_open_if_enabled(); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -3249,14 +3495,13 @@ static int show_enviroment(DBusConnection *bus, char **args) { DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); - if (r) - goto finish; + 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); @@ -3264,8 +3509,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); @@ -3275,28 +3519,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 (reply) - dbus_message_unref(reply); - - return r; + return 0; } static int switch_root(DBusConnection *bus, char **args) { unsigned l; - const char *root, *init; + const char *root; + _cleanup_free_ char *init = NULL; l = strv_length(args); if (l < 2 || l > 3) { @@ -3305,9 +3543,23 @@ static int switch_root(DBusConnection *bus, char **args) { } root = args[1]; - init = l >= 3 ? args[2] : ""; - return bus_method_call_with_reply ( + if (l >= 3) + init = strdup(args[2]); + else { + parse_env_file("/proc/cmdline", WHITESPACE, + "init", &init, + NULL); + + if (!init) + init = strdup(""); + } + if (!init) + return log_oom(); + + log_debug("switching root - root: %s; init: %s", root, init); + + return bus_method_call_with_reply( bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -3321,12 +3573,14 @@ static int switch_root(DBusConnection *bus, char **args) { } 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); @@ -3334,61 +3588,34 @@ 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; - } + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (!m) + return log_oom(); 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; - } + r = bus_append_strv_iter(&iter, args + 1); + if (r < 0) + return log_oom(); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &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)); - r = -EIO; - goto finish; + dbus_error_free(&error); + return -EIO; } - 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 enable_sysv_units(char **args) { int r = 0; -#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA)) +#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG) const char *verb = args[0]; unsigned f = 1, t = 1; LookupPaths paths; @@ -3567,7 +3794,15 @@ static int mangle_names(char **original_names, char ***mangled_names) { i = l; STRV_FOREACH(name, original_names) { - *i = unit_name_mangle(*name); + + /* 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(); @@ -3587,10 +3822,12 @@ static int enable_unit(DBusConnection *bus, char **args) { UnitFileChange *changes = NULL; unsigned n_changes = 0, i; int carries_install_info = -1; - DBusMessage *m = NULL, *reply = NULL; + DBusMessage _cleanup_dbus_message_unref_ *m = NULL, *reply = NULL; int r; - DBusError error; - char **mangled_names = NULL; + DBusError _cleanup_dbus_error_free_ error; + char _cleanup_strv_free_ **mangled_names = NULL; + + dbus_error_init(&error); r = enable_sysv_units(args); if (r < 0) @@ -3599,8 +3836,6 @@ static int enable_unit(DBusConnection *bus, char **args) { if (!args[1]) return 0; - dbus_error_init(&error); - 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); @@ -3636,6 +3871,7 @@ static int enable_unit(DBusConnection *bus, char **args) { } } + r = 0; } else { const char *method; bool send_force = true, expect_carries_install_info = false; @@ -3768,30 +4004,29 @@ static int enable_unit(DBusConnection *bus, char **args) { } if (carries_install_info == 0) - log_warning("The unit files have no [Install] section. They are not meant to be enabled using systemctl."); + 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); - - strv_free(mangled_names); - return r; } static int unit_is_enabled(DBusConnection *bus, char **args) { - DBusError error; + DBusError _cleanup_dbus_error_free_ error; int r; - DBusMessage *reply = NULL; + DBusMessage _cleanup_dbus_message_unref_ *reply = NULL; bool enabled; char **name; + char *n; dbus_error_init(&error); @@ -3806,11 +4041,16 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { UnitFileState state; - state = unit_file_get_state(arg_scope, arg_root, *name); - if (state < 0) { - r = state; - goto finish; - } + n = unit_name_mangle(*name); + if (!n) + return log_oom(); + + state = unit_file_get_state(arg_scope, arg_root, n); + + free(n); + + if (state < 0) + return state; if (state == UNIT_FILE_ENABLED || state == UNIT_FILE_ENABLED_RUNTIME || @@ -3825,6 +4065,10 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { const char *s; + n = unit_name_mangle(*name); + if (!n) + return log_oom(); + r = bus_method_call_with_reply ( bus, "org.freedesktop.systemd1", @@ -3833,17 +4077,19 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { "GetUnitFileState", &reply, NULL, - DBUS_TYPE_STRING, name, + DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); + + free(n); + if (r) - goto finish; + 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(reply); @@ -3859,14 +4105,7 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { } } - r = enabled ? 0 : 1; - -finish: - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - return r; + return enabled ? 0 : 1; } static int systemctl_help(void) { @@ -3886,6 +4125,8 @@ static int systemctl_help(void) { " pending\n" " --ignore-dependencies\n" " When queueing a new job, ignore all its dependencies\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" @@ -3900,8 +4141,6 @@ 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" @@ -3910,9 +4149,8 @@ static int systemctl_help(void) { " --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" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, json-pretty, cat)\n\n" + " verbose, export, json, json-pretty, json-sse, cat)\n\n" "Unit Commands:\n" " list-units List loaded units\n" " start [NAME...] Start (activate) one or more units\n" @@ -3920,20 +4158,31 @@ static int systemctl_help(void) { " 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" + " 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" + " get-cgroup-attr [NAME] [ATTR] ...\n" + " Get control group attrubute\n" + " set-cgroup-attr [NAME] [ATTR] [VALUE] ...\n" + " Set control group attribute\n" + " unset-cgroup-attr [NAME] [ATTR...]\n" + " Unset control group attribute\n" + " set-cgroup [NAME] [CGROUP...] Add unit to a control group\n" + " unset-cgroup [NAME] [CGROUP...] Remove unit from a control group\n" + " load [NAME...] Load one or more units\n" + " list-dependencies [NAME] Recursively show units which are required\n" + " or wanted by this unit\n\n" "Unit File Commands:\n" " list-unit-files List installed unit files\n" " enable [NAME...] Enable one or more unit files\n" @@ -3951,7 +4200,6 @@ static int systemctl_help(void) { " 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" @@ -3973,7 +4221,8 @@ static int systemctl_help(void) { " 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; @@ -4044,10 +4293,32 @@ 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); + } + + puts("\nAvailable unit load states: "); + for(i = 0; i < _UNIT_LOAD_STATE_MAX; i++) { + t = unit_load_state_to_string(i); + if (t) + puts(t); + } + + return 0; +} + static int systemctl_parse_argv(int argc, char *argv[]) { enum { ARG_FAIL = 0x100, + ARG_IRREVERSIBLE, ARG_IGNORE_DEPENDENCIES, ARG_VERSION, ARG_USER, @@ -4057,8 +4328,6 @@ 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, @@ -4066,7 +4335,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_NO_ASK_PASSWORD, ARG_FAILED, ARG_RUNTIME, - ARG_FOLLOW, ARG_FORCE }; @@ -4079,7 +4347,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "failed", no_argument, NULL, ARG_FAILED }, { "full", no_argument, NULL, ARG_FULL }, { "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 }, @@ -4088,8 +4358,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "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 }, @@ -4100,7 +4368,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "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 } }; @@ -4110,7 +4377,7 @@ 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:aqfs:H:Pn:o:i", options, NULL)) >= 0) { switch (c) { @@ -4120,11 +4387,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) { case ARG_VERSION: puts(PACKAGE_STRING); - puts(DISTRIBUTION); puts(SYSTEMD_FEATURES); return 0; case 't': + if (streq(optarg, "help")) { + help_types(); + return 0; + } + if (unit_type_from_string(optarg) >= 0) { arg_type = optarg; break; @@ -4135,20 +4406,36 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } log_error("Unkown unit type or load state '%s'.", optarg); + log_info("Use -t help to see a list of allowed values."); return -EINVAL; case 'p': { - char **l; + char *word, *state; + size_t size; + /* Make sure that if the empty property list + was specified, we won't show any properties. */ + const char *source = isempty(optarg) ? " " : optarg; + + FOREACH_WORD_SEPARATOR(word, size, source, ",", state) { + char _cleanup_free_ *prop; + char **tmp; + + prop = strndup(word, size); + if (!prop) + return -ENOMEM; - if (!(l = strv_append(arg_property, optarg))) - return -ENOMEM; + tmp = strv_append(arg_property, prop); + if (!tmp) + return -ENOMEM; - strv_free(arg_property); - arg_property = l; + strv_free(arg_property); + arg_property = tmp; + } /* If the user asked for a particular * property, show it to him, even if it is * empty. */ arg_all = true; + break; } @@ -4160,6 +4447,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_job_mode = "fail"; break; + case ARG_IRREVERSIBLE: + arg_job_mode = "replace-irreversibly"; + break; + case ARG_IGNORE_DEPENDENCIES: arg_job_mode = "ignore-dependencies"; break; @@ -4192,14 +4483,6 @@ 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; @@ -4220,14 +4503,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: @@ -4277,6 +4554,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } break; + case 'i': + arg_ignore_inhibitors = true; + break; + case '?': return -EINVAL; @@ -4405,11 +4686,11 @@ static int parse_time_spec(const char *t, usec_t *_u) { 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); @@ -4694,7 +4975,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; @@ -4729,8 +5010,8 @@ static int action_to_runlevel(void) { } static int talk_upstart(void) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + DBusMessage _cleanup_dbus_message_unref_ *m = NULL, *reply = NULL; + DBusError _cleanup_dbus_error_free_ error; int previous, rl, r; char env1_buf[] = "RUNLEVEL=X", @@ -4807,39 +5088,32 @@ static int talk_upstart(void) { 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 = {0}; + int r; + int _cleanup_close_ 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; @@ -4849,9 +5123,7 @@ 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; } @@ -4889,14 +5161,19 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */ { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */ { "isolate", EQUAL, 2, start_unit }, + { "set-cgroup", MORE, 3, set_cgroup }, + { "unset-cgroup", MORE, 3, set_cgroup }, + { "get-cgroup-attr", MORE, 3, get_cgroup_attr }, + { "set-cgroup-attr", MORE, 4, set_cgroup_attr }, + { "unset-cgroup-attr", MORE, 3, set_cgroup }, { "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 }, @@ -4910,6 +5187,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 }, @@ -4924,6 +5202,7 @@ 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 }, }; int left; @@ -5023,7 +5302,7 @@ 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; + int _cleanup_close_ fd; struct msghdr msghdr; struct iovec iovec[2]; union sockaddr_union sockaddr; @@ -5060,12 +5339,9 @@ static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const ch } 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; } @@ -5147,6 +5423,10 @@ static _noreturn_ 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 @@ -5154,7 +5434,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); @@ -5167,7 +5447,7 @@ static int halt_main(DBusConnection *bus) { } if (arg_when > 0) { - char *m; + char _cleanup_free_ *m; m = strv_join(arg_wall, " "); r = send_shutdownd(arg_when, @@ -5178,7 +5458,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)); @@ -5231,10 +5510,11 @@ static int runlevel_main(void) { int main(int argc, char*argv[]) { int r, retval = EXIT_FAILURE; DBusConnection *bus = NULL; - DBusError error; + DBusError _cleanup_dbus_error_free_ error; dbus_error_init(&error); + setlocale(LC_ALL, ""); log_parse_environment(); log_open(); @@ -5333,8 +5613,6 @@ finish: dbus_connection_unref(bus); } - dbus_error_free(&error); - dbus_shutdown(); strv_free(arg_property);