X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=509651c1fd35a435a1a5c0404c212d89641683c2;hp=66d031dae82aeaf28d366a56c1a9b56098d251c2;hb=033a842c36e7629f81d05d12a4ed8c298ad4d3f2;hpb=69bd386c4c0618d686e1b5ac01c0e9658b1539b6 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 66d031dae..509651c1f 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -66,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; @@ -116,11 +117,6 @@ 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, @@ -275,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; @@ -369,7 +352,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { id_len = max_id_len; 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; @@ -413,8 +396,6 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { printf("%.*s\n", desc_len, u->description); else printf("%s\n", u->description); - - free(e); } if (!arg_no_legend) { @@ -445,14 +426,15 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { } } -static int list_units(DBusConnection *bus, char **args) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - _cleanup_free_ struct unit_info *unit_infos = NULL; - DBusMessageIter iter, sub, sub2; - unsigned c = 0, n_units = 0; +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; - pager_open_if_enabled(); + assert(bus); + assert(unit_infos); + assert(c); r = bus_method_call_with_reply( bus, @@ -460,13 +442,13 @@ static int list_units(DBusConnection *bus, char **args) { "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "ListUnits", - &reply, + reply, NULL, DBUS_TYPE_INVALID); 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."); @@ -478,45 +460,45 @@ static int list_units(DBusConnection *bus, char **args) { while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { struct unit_info *u; - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT); - - 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); + n_units = MAX(2 * *c, 16); + 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."); - return -EIO; - } + 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); + if (*c > 0) + qsort(*unit_infos, *c, sizeof(struct unit_info), compare_unit_info); + + return 0; +} + +static int list_units(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ struct unit_info *unit_infos = NULL; + unsigned c = 0; + int r; + + pager_open_if_enabled(); + + 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; } @@ -572,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; @@ -600,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) @@ -672,7 +652,7 @@ static int list_unit_files(DBusConnection *bus, char **args) { UnitFileList *u; const char *state; - assert(dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT); + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT); if (c >= n_units) { UnitFileList *w; @@ -710,92 +690,86 @@ static int list_unit_files(DBusConnection *bus, char **args) { 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(),20); - 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; - - switch (dbus_message_iter_get_arg_type(iter)) { - - case DBUS_TYPE_ARRAY: - - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; - - dbus_message_iter_recurse(iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); - dbus_message_iter_get_basic(&sub, &s); - printf("\t\"%s\"->\"%s\" %s;\n", name, s, c); - - dbus_message_iter_next(&sub); - } - - return 0; - } + if(arg_full){ + printf("%s\n", name); + return 0; } + n = ellipsize(name, max_len-len, 100); + if(!n) + return log_oom(); + + printf("%s\n", n); return 0; } -static int dot_one(DBusConnection *bus, const char *name, const char *path) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; +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"; + + _cleanup_free_ char *path; const char *interface = "org.freedesktop.systemd1.Unit"; - int r; + + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2, sub3; + int r = 0; + char **ret = NULL; + assert(bus); - assert(path); + assert(name); + assert(deps); + + path = unit_dbus_path_from_name(name); + if (path == NULL) { + r = -EINVAL; + goto finish; + } r = bus_method_call_with_reply( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &reply, - NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID); + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); if (r < 0) - return r; + 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."); - return -EIO; + r = -EIO; + goto finish; } dbus_message_iter_recurse(&iter, &sub); @@ -803,95 +777,122 @@ 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; - assert(dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY); + 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 || - dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) { log_error("Failed to parse reply."); - return -EIO; + r = -EIO; + goto finish; } - dbus_message_iter_recurse(&sub2, &sub3); - r = dot_one_property(name, prop, &sub3); - if (r < 0) - return r; + 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); - } - return 0; -} + if (!nulstr_contains(dependencies, prop)) + continue; -static int dot(DBusConnection *bus, char **args) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - DBusMessageIter iter, sub, sub2; - int r; + 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); - r = bus_method_call_with_reply( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &reply, - NULL, - DBUS_TYPE_INVALID); - if (r < 0) - return r; + while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) { + const char *s; - 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; + 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; +} - printf("digraph systemd {\n"); +static int list_dependencies_compare(const void *_a, const void *_b) { + const char **a = (const char**) _a, **b = (const char**) _b; + if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) + return 1; + if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET) + return -1; + return strcasecmp(*a, *b); +} - dbus_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; +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_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { - log_error("Failed to parse reply."); - return -EIO; - } + u = strv_append(units, name); + if (!u) + return log_oom(); - dbus_message_iter_recurse(&sub, &sub2); + r = list_dependencies_get_dependencies(bus, name, &deps); + 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."); - return -EIO; + qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); + + 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; } - r = dot_one(bus, id, unit_path); + r = list_dependencies_print(*c, level, branches, c[1] == NULL); if (r < 0) return r; - /* printf("\t\"%s\";\n", id); */ - dbus_message_iter_next(&sub); + 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; + } } - 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); - return 0; + if (args[1]) { + unit = unit_name_mangle(args[1]); + if (!unit) + return log_oom(); + u = unit; + } else + u = SPECIAL_DEFAULT_TARGET; + + pager_open_if_enabled(); + + puts(u); + + return list_dependencies_one(bus, u, 0, NULL, 0); } static int list_jobs(DBusConnection *bus, char **args) { @@ -929,7 +930,7 @@ 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."); @@ -950,7 +951,6 @@ static int list_jobs(DBusConnection *bus, char **args) { e = arg_full ? NULL : ellipsize(name, 25, 33); printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state); - free(e); k++; @@ -1100,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), @@ -1137,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); @@ -1154,15 +1154,13 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me if (*result) d->result = strdup(result); - 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; } @@ -1338,7 +1336,9 @@ static void check_triggering_units( _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub; 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; @@ -1355,6 +1355,41 @@ static void check_triggering_units( return; } + 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", @@ -1532,8 +1567,8 @@ static int start_unit(DBusConnection *bus, char **args) { 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); @@ -1587,14 +1622,12 @@ static int start_unit(DBusConnection *bus, char **args) { 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) { @@ -1613,10 +1646,8 @@ 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) */ @@ -1629,10 +1660,6 @@ static int start_unit(DBusConnection *bus, char **args) { } } -finish: - set_free_free(s); - dbus_error_free(&error); - return ret; } @@ -1868,7 +1895,7 @@ static int start_special(DBusConnection *bus, char **args) { } r = start_unit(bus, args); - if (r >= 0) + if (r == EXIT_SUCCESS) warn_wall(a); return r; @@ -1967,6 +1994,7 @@ static int set_cgroup(DBusConnection *bus, char **args) { DBusMessageIter iter; int r; _cleanup_free_ char *n = NULL; + const char *runtime; assert(bus); assert(args); @@ -1998,6 +2026,10 @@ static int set_cgroup(DBusConnection *bus, char **args) { if (r < 0) return log_oom(); + runtime = arg_runtime ? "runtime" : "persistent"; + 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)); @@ -2014,6 +2046,7 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) { DBusMessageIter iter, sub, sub2; char **x, **y; _cleanup_free_ char *n = NULL; + const char *runtime; assert(bus); assert(args); @@ -2050,8 +2083,54 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) { return log_oom(); } - if (!dbus_message_iter_close_container(&iter, &sub)) - return -ENOMEM; + runtime = arg_runtime ? "runtime" : "persistent"; + if (!dbus_message_iter_close_container(&iter, &sub) || + !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 *m = NULL, *reply = NULL; + DBusError error; + DBusMessageIter iter; + int r; + _cleanup_free_ char *n = NULL; + _cleanup_strv_free_ char **list = NULL; + char **a; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + 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", + "GetUnitControlGroupAttributes"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n)) + return log_oom(); + + r = bus_append_strv_iter(&iter, args + 2); + if (r < 0) + return log_oom(); reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); if (!reply) { @@ -2060,6 +2139,20 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) { return -EIO; } + dbus_message_iter_init(reply, &iter); + 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; } @@ -2231,6 +2324,12 @@ static void print_status_info(UnitStatusInfo *i) { 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; assert(i); @@ -2454,25 +2553,29 @@ 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, "\t\t ", c, false, extra, k, flags); } } if (i->id && arg_transport != TRANSPORT_SSH) { - int flags = - arg_all * OUTPUT_SHOW_ALL | - (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR | - !arg_quiet * OUTPUT_WARN_CUTOFF; - printf("\n"); - show_journal_by_unit(stdout, - i->id, - arg_output, - 0, - i->inactive_exit_timestamp_monotonic, - arg_lines, - flags); + if(arg_scope == UNIT_FILE_SYSTEM) + show_journal_by_unit(stdout, + i->id, + arg_output, + 0, + i->inactive_exit_timestamp_monotonic, + arg_lines, + flags); + else + show_journal_by_user_unit(stdout, + i->id, + arg_output, + 0, + i->inactive_exit_timestamp_monotonic, + arg_lines, + getuid(), + flags); } if (i->need_daemon_reload) @@ -2497,7 +2600,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; @@ -2508,14 +2611,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; } @@ -2528,8 +2625,6 @@ static void show_unit_help(UnitStatusInfo *i) { pid = fork(); if (pid < 0) { log_error("Failed to fork: %m"); - free(page); - free(section); continue; } @@ -2540,9 +2635,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); @@ -2885,7 +2977,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); @@ -2906,7 +2998,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, " "); @@ -2922,8 +3014,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); @@ -3041,7 +3131,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, bool *new_line) { _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); @@ -3057,33 +3147,60 @@ static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID); if (r < 0) - 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; } r = show_one(verb, bus, path, false, new_line); + return r; +} -finish: - dbus_error_free(&error); +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; - return r; + 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 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(); @@ -3093,6 +3210,9 @@ static int show(DBusConnection *bus, char **args) { 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; @@ -3708,10 +3828,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) @@ -3720,8 +3842,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); @@ -3902,25 +4022,15 @@ static int enable_unit(DBusConnection *bus, char **args) { " 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; @@ -3938,10 +4048,8 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { UnitFileState state; state = unit_file_get_state(arg_scope, arg_root, *name); - if (state < 0) { - r = state; - goto finish; - } + if (state < 0) + return state; if (state == UNIT_FILE_ENABLED || state == UNIT_FILE_ENABLED_RUNTIME || @@ -3967,14 +4075,13 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { DBUS_TYPE_STRING, name, DBUS_TYPE_INVALID); 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); @@ -3990,14 +4097,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) { @@ -4033,8 +4133,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" @@ -4063,16 +4161,20 @@ static int systemctl_help(void) { " status [NAME...|PID...] Show runtime status of one or more units\n" " show [NAME...|JOB...] Show properties of one or more\n" " units/jobs or the manager\n" - " 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" - " set-cgroup [NAME] [CGROUP...] Add unit to a control group\n" - " unset-cgroup [NAME] [CGROUP...] Remove unit from a control group\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" - " load [NAME...] Load one or more units\n\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" @@ -4090,7 +4192,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" @@ -4213,8 +4314,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, @@ -4244,8 +4343,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 }, @@ -4297,18 +4394,33 @@ static int systemctl_parse_argv(int argc, char *argv[]) { 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; } @@ -4352,14 +4464,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; @@ -4887,8 +4991,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", @@ -4965,20 +5069,12 @@ 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; } @@ -5049,6 +5145,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "isolate", EQUAL, 2, start_unit }, { "set-cgroup", MORE, 2, set_cgroup }, { "unset-cgroup", MORE, 2, set_cgroup }, + { "get-cgroup-attr", MORE, 2, get_cgroup_attr }, { "set-cgroup-attr", MORE, 2, set_cgroup_attr }, { "unset-cgroup-attr", MORE, 2, set_cgroup }, { "kill", MORE, 2, kill_unit }, @@ -5056,10 +5153,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "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 }, @@ -5088,6 +5184,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; @@ -5187,7 +5284,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; @@ -5224,12 +5321,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; } @@ -5335,7 +5429,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, @@ -5346,7 +5440,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)); @@ -5399,7 +5492,7 @@ 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); @@ -5502,8 +5595,6 @@ finish: dbus_connection_unref(bus); } - dbus_error_free(&error); - dbus_shutdown(); strv_free(arg_property);