X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=5ccbbbc0c72987abde5d416a5f1811e4ba72f526;hp=fd9f580dc2e6438b4fe9222d44cee62c4ddd90c3;hb=76d5a71de99b6fe0ecc9bfd82ec641a5d408e191;hpb=b92bea5d2a9481de69bb627a7b442a9f58fca43d diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index fd9f580dc..5ccbbbc0c 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -68,10 +68,16 @@ #include "socket-util.h" #include "fileio.h" -static const char *arg_type = NULL; -static const char *arg_load_state = NULL; -static char **arg_property = NULL; +static char **arg_types = NULL; +static char **arg_load_states = NULL; +static char **arg_properties = NULL; static bool arg_all = false; +static enum dependency { + DEPENDENCY_FORWARD, + DEPENDENCY_REVERSE, + DEPENDENCY_AFTER, + DEPENDENCY_BEFORE, +} arg_dependency = DEPENDENCY_FORWARD; static const char *arg_job_mode = "replace"; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; static bool arg_no_block = false; @@ -80,6 +86,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_show_types = false; static bool arg_ignore_inhibitors = false; static bool arg_dry = false; static bool arg_quiet = false; @@ -125,6 +132,7 @@ static enum transport { static const char *arg_host = NULL; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; +static bool arg_plain = false; static bool private_bus = false; @@ -295,9 +303,9 @@ static bool output_show_unit(const struct unit_info *u) { if (arg_failed) return streq(u->active_state, "failed"); - return (!arg_type || ((dot = strrchr(u->id, '.')) && - streq(dot+1, arg_type))) && - (!arg_load_state || streq(u->load_state, arg_load_state)) && + return (!arg_types || ((dot = strrchr(u->id, '.')) && + strv_find(arg_types, dot+1))) && + (!arg_load_states || strv_find(arg_load_states, u->load_state)) && (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0); } @@ -352,9 +360,9 @@ 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 _cleanup_free_ *e = NULL; - const char *on_loaded, *off_loaded; - const char *on_active, *off_active; + _cleanup_free_ char *e = NULL; + const char *on_loaded, *off_loaded, *on = ""; + const char *on_active, *off_active, *off = ""; if (!output_show_unit(u)) continue; @@ -373,21 +381,21 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { n_shown++; if (streq(u->load_state, "error")) { - on_loaded = ansi_highlight_red(true); - off_loaded = ansi_highlight_red(false); + on_loaded = on = ansi_highlight_red(true); + off_loaded = off = ansi_highlight_red(false); } else on_loaded = off_loaded = ""; if (streq(u->active_state, "failed")) { - on_active = ansi_highlight_red(true); - off_active = ansi_highlight_red(false); + on_active = on = ansi_highlight_red(true); + off_active = off = ansi_highlight_red(false); } else on_active = off_active = ""; e = arg_full ? NULL : ellipsize(u->id, id_len, 33); - printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s", - id_len, e ? e : u->id, + printf("%s%-*s%s %s%-6s%s %s%-*s %-*s%s %-*s", + on, id_len, e ? e : u->id, off, on_loaded, u->load_state, off_loaded, on_active, active_len, u->active_state, sub_len, u->sub_state, off_active, @@ -429,7 +437,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { static int get_unit_list(DBusConnection *bus, DBusMessage **reply, struct unit_info **unit_infos, unsigned *c) { DBusMessageIter iter, sub; - unsigned n_units = 0; + size_t size = 0; int r; assert(bus); @@ -458,47 +466,304 @@ static int get_unit_list(DBusConnection *bus, DBusMessage **reply, dbus_message_iter_recurse(&iter, &sub); while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - struct unit_info *u; + if (!GREEDY_REALLOC(*unit_infos, size, *c + 1)) + return log_oom(); - if (*c >= n_units) { - struct unit_info *w; + bus_parse_unit_info(&sub, *unit_infos + *c); + (*c)++; - n_units = MAX(2 * *c, 16u); - w = realloc(*unit_infos, sizeof(struct unit_info) * n_units); - if (!w) - return log_oom(); + dbus_message_iter_next(&sub); + } + + return 0; +} + +static int list_units(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ struct unit_info *unit_infos = NULL; + unsigned c = 0; + int r; + + pager_open_if_enabled(); + + r = get_unit_list(bus, &reply, &unit_infos, &c); + if (r < 0) + return r; + + qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); - *unit_infos = w; + output_units_list(unit_infos, c); + + return 0; +} + +static int get_triggered_units(DBusConnection *bus, const char* unit_path, + char*** triggered) +{ + const char *interface = "org.freedesktop.systemd1.Unit", + *triggers_property = "Triggers"; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + int r; + + r = bus_method_call_with_reply(bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &triggers_property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EBADMSG; + } + + dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_recurse(&sub, &iter); + sub = iter; + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *unit; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); + return -EBADMSG; + } + + dbus_message_iter_get_basic(&sub, &unit); + r = strv_extend(triggered, unit); + if (r < 0) + return r; + + dbus_message_iter_next(&sub); + } + + return 0; +} + +static int get_listening(DBusConnection *bus, const char* unit_path, + char*** listen, unsigned *c) +{ + const char *interface = "org.freedesktop.systemd1.Socket", + *listen_property = "Listen"; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + int r; + + r = bus_method_call_with_reply(bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &listen_property, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EBADMSG; + } + + dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_recurse(&sub, &iter); + sub = iter; + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + DBusMessageIter sub2; + const char *type, *path; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + return -EBADMSG; } - u = *unit_infos + *c; + 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) { + r = strv_extend(listen, type); + if (r < 0) + return r; + + r = strv_extend(listen, path); + if (r < 0) + return r; - bus_parse_unit_info(&sub, u); + (*c) ++; + } dbus_message_iter_next(&sub); - (*c)++; } - if (*c > 0) - qsort(*unit_infos, *c, sizeof(struct unit_info), compare_unit_info); + return 0; +} + +struct socket_info { + const char* id; + + char* type; + char* path; + + /* Note: triggered is a list here, although it almost certainly + * will always be one unit. Nevertheless, dbus API allows for multiple + * values, so let's follow that.*/ + char** triggered; + + /* The strv above is shared. free is set only in the first one. */ + bool own_triggered; +}; + +static int socket_info_compare(struct socket_info *a, struct socket_info *b) { + int o = strcmp(a->path, b->path); + if (o == 0) + o = strcmp(a->type, b->type); + return o; +} + +static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { + struct socket_info *s; + unsigned pathlen = sizeof("LISTEN") - 1, + typelen = (sizeof("TYPE") - 1) * arg_show_types, + socklen = sizeof("UNIT") - 1, + servlen = sizeof("ACTIVATES") - 1; + const char *on, *off; + + for (s = socket_infos; s < socket_infos + cs; s++) { + char **a; + unsigned tmp = 0; + + socklen = MAX(socklen, strlen(s->id)); + if (arg_show_types) + typelen = MAX(typelen, strlen(s->type)); + pathlen = MAX(pathlen, strlen(s->path)); + + STRV_FOREACH(a, s->triggered) + tmp += strlen(*a) + 2*(a != s->triggered); + servlen = MAX(servlen, tmp); + } + + if (cs) { + if (!arg_no_legend) + printf("%-*s %-*.*s%-*s %s\n", + pathlen, "LISTEN", + typelen + arg_show_types, typelen + arg_show_types, "TYPE ", + socklen, "UNIT", + "ACTIVATES"); + + for (s = socket_infos; s < socket_infos + cs; s++) { + char **a; + + if (arg_show_types) + printf("%-*s %-*s %-*s", + pathlen, s->path, typelen, s->type, socklen, s->id); + else + printf("%-*s %-*s", + pathlen, s->path, socklen, s->id); + STRV_FOREACH(a, s->triggered) + printf("%s %s", + a == s->triggered ? "" : ",", *a); + printf("\n"); + } + + on = ansi_highlight(true); + off = ansi_highlight(false); + if (!arg_no_legend) + printf("\n"); + } else { + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); + } + + if (!arg_no_legend) { + printf("%s%u sockets listed.%s\n", on, cs, off); + if (!arg_all) + printf("Pass --all to see loaded but inactive sockets, too.\n"); + } return 0; } -static int list_units(DBusConnection *bus, char **args) { +static int list_sockets(DBusConnection *bus, char **args) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; _cleanup_free_ struct unit_info *unit_infos = NULL; - unsigned c = 0; + struct socket_info *socket_infos = NULL; + const struct unit_info *u; + struct socket_info *s; + unsigned cu = 0, cs = 0; + size_t size = 0; int r; pager_open_if_enabled(); - r = get_unit_list(bus, &reply, &unit_infos, &c); + r = get_unit_list(bus, &reply, &unit_infos, &cu); if (r < 0) return r; - if (c > 0) - output_units_list(unit_infos, c); + for (u = unit_infos; u < unit_infos + cu; u++) { + const char *dot; + _cleanup_strv_free_ char **listen = NULL, **triggered = NULL; + unsigned c = 0, i; + + if (!output_show_unit(u)) + continue; + + if ((dot = strrchr(u->id, '.')) && !streq(dot+1, "socket")) + continue; + + r = get_triggered_units(bus, u->unit_path, &triggered); + if (r < 0) + goto cleanup; + + r = get_listening(bus, u->unit_path, &listen, &c); + if (r < 0) + goto cleanup; + + if (!GREEDY_REALLOC(socket_infos, size, cs + c)) { + r = log_oom(); + goto cleanup; + } + + for (i = 0; i < c; i++) + socket_infos[cs + i] = (struct socket_info) { + .id = u->id, + .type = listen[i*2], + .path = listen[i*2 + 1], + .triggered = triggered, + .own_triggered = i==0, + }; + + /* from this point on we will cleanup those socket_infos */ + cs += c; + free(listen); + listen = triggered = NULL; /* avoid cleanup */ + } + + qsort(socket_infos, cs, sizeof(struct socket_info), + (__compar_fn_t) socket_info_compare); + + output_sockets_list(socket_infos, cs); + + cleanup: + assert(cs == 0 || socket_infos); + for (s = socket_infos; s < socket_infos + cs; s++) { + free(s->type); + free(s->path); + if (s->own_triggered) + strv_free(s->triggered); + } + free(socket_infos); return 0; } @@ -524,7 +789,7 @@ static int compare_unit_file_list(const void *a, const void *b) { static bool output_show_unit_file(const UnitFileList *u) { const char *dot; - return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type)); + return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1)); } static void output_unit_file_list(const UnitFileList *units, unsigned c) { @@ -554,7 +819,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 _cleanup_free_ *e = NULL; + _cleanup_free_ char *e = NULL; const char *on, *off; const char *id; @@ -696,20 +961,22 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra size_t len = 0; size_t max_len = MAX(columns(),20u); - for (i = level - 1; i >= 0; i--) { + if (!arg_plain) { + for (i = level - 1; i >= 0; i--) { + len += 2; + if(len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; + } + printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERT : DRAW_TREE_SPACE)); + } len += 2; if(len > max_len - 3 && !arg_full) { printf("%s...\n",max_len % 2 ? "" : " "); return 0; } - printf("%s", draw_special_char(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)); } - printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); if(arg_full){ printf("%s\n", name); @@ -725,12 +992,19 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra } 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"; + static const char *dependencies[] = { + [DEPENDENCY_FORWARD] = "Requires\0" + "RequiresOverridable\0" + "Requisite\0" + "RequisiteOverridable\0" + "Wants\0", + [DEPENDENCY_REVERSE] = "RequiredBy\0" + "RequiredByOverridable\0" + "WantedBy\0" + "PartOf\0", + [DEPENDENCY_AFTER] = "After\0", + [DEPENDENCY_BEFORE] = "Before\0", + }; _cleanup_free_ char *path; const char *interface = "org.freedesktop.systemd1.Unit"; @@ -795,7 +1069,8 @@ static int list_dependencies_get_dependencies(DBusConnection *bus, const char *n dbus_message_iter_recurse(&sub2, &sub3); dbus_message_iter_next(&sub); - if (!nulstr_contains(dependencies, prop)) + assert(arg_dependency < ELEMENTSOF(dependencies)); + if (!nulstr_contains(dependencies[arg_dependency], prop)) continue; if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) { @@ -837,12 +1112,12 @@ static int list_dependencies_compare(const void *_a, const void *_b) { return strcasecmp(*a, *b); } -static int list_dependencies_one(DBusConnection *bus, const char *name, int level, char **units, unsigned int branches) { - char _cleanup_strv_free_ **deps = NULL, **u; +static int list_dependencies_one(DBusConnection *bus, const char *name, int level, char ***units, unsigned int branches) { + _cleanup_strv_free_ char **deps = NULL, **u; char **c; int r = 0; - u = strv_append(units, name); + u = strv_append(*units, name); if (!u) return log_oom(); @@ -854,9 +1129,11 @@ static int list_dependencies_one(DBusConnection *bus, const char *name, int leve 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; + if (!arg_plain) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); + if (r < 0) + return r; + } continue; } @@ -865,17 +1142,22 @@ static int list_dependencies_one(DBusConnection *bus, const char *name, int leve return r; if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) { - r = list_dependencies_one(bus, *c, level + 1, u, (branches << 1) | (c[1] == NULL ? 0 : 1)); + r = list_dependencies_one(bus, *c, level + 1, &u, (branches << 1) | (c[1] == NULL ? 0 : 1)); if(r < 0) return r; } } - + if (arg_plain) { + strv_free(*units); + *units = u; + u = NULL; + } return 0; } static int list_dependencies(DBusConnection *bus, char **args) { _cleanup_free_ char *unit = NULL; + _cleanup_strv_free_ char **units = NULL; const char *u; assert(bus); @@ -892,16 +1174,140 @@ static int list_dependencies(DBusConnection *bus, char **args) { puts(u); - return list_dependencies_one(bus, u, 0, NULL, 0); + return list_dependencies_one(bus, u, 0, &units, 0); +} + +static int get_default(DBusConnection *bus, char **args) { + char *path = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + int r; + _cleanup_dbus_error_free_ DBusError error; + + dbus_error_init(&error); + + if (!bus || avoid_bus()) { + r = unit_file_get_default(arg_scope, arg_root, &path); + + if (r < 0) { + log_error("Operation failed: %s", strerror(-r)); + goto finish; + } + + r = 0; + } else { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetDefaultTarget", + &reply, + NULL, + DBUS_TYPE_INVALID); + + if (r < 0) { + log_error("Operation failed: %s", strerror(-r)); + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } + } + + if (path) + printf("%s\n", path); + +finish: + if ((!bus || avoid_bus()) && path) + free(path); + + return r; + +} + +struct job_info { + uint32_t id; + char *name, *type, *state; +}; + +static void list_jobs_print(struct job_info* jobs, size_t n) { + size_t i; + struct job_info *j; + const char *on, *off; + bool shorten = false; + + assert(n == 0 || jobs); + + if (n == 0) { + on = ansi_highlight_green(true); + off = ansi_highlight_green(false); + + printf("%sNo jobs running.%s\n", on, off); + return; + } + + pager_open_if_enabled(); + + { + /* JOB UNIT TYPE STATE */ + unsigned l0 = 3, l1 = 4, l2 = 4, l3 = 5; + + for (i = 0, j = jobs; i < n; i++, j++) { + assert(j->name && j->type && j->state); + l0 = MAX(l0, DECIMAL_STR_WIDTH(j->id)); + l1 = MAX(l1, strlen(j->name)); + l2 = MAX(l2, strlen(j->type)); + l3 = MAX(l3, strlen(j->state)); + } + + if (!arg_full && l0 + 1 + l1 + l2 + 1 + l3 > columns()) { + l1 = MAX(33u, columns() - l0 - l2 - l3 - 3); + shorten = true; + } + + if (on_tty()) + printf("%*s %-*s %-*s %-*s\n", + l0, "JOB", + l1, "UNIT", + l2, "TYPE", + l3, "STATE"); + + for (i = 0, j = jobs; i < n; i++, j++) { + _cleanup_free_ char *e = NULL; + + if (streq(j->state, "running")) { + on = ansi_highlight(true); + off = ansi_highlight(false); + } else + on = off = ""; + + e = shorten ? ellipsize(j->name, l1, 33) : NULL; + printf("%*u %s%-*s%s %-*s %s%-*s%s\n", + l0, j->id, + on, l1, e ? e : j->name, off, + l2, j->type, + on, l3, j->state, off); + } + } + + on = ansi_highlight(true); + off = ansi_highlight(false); + + if (on_tty()) + printf("\n%s%zu jobs listed%s.\n", on, n, off); } static int list_jobs(DBusConnection *bus, char **args) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; - unsigned k = 0; int r; - - pager_open_if_enabled(); + struct job_info *jobs = NULL; + size_t size = 0, used = 0; r = bus_method_call_with_reply( bus, @@ -924,13 +1330,9 @@ static int list_jobs(DBusConnection *bus, char **args) { dbus_message_iter_recurse(&iter, &sub); - if (on_tty()) - printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE"); - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { const char *name, *type, *state, *job_path, *unit_path; uint32_t id; - char _cleanup_free_ *e = NULL; if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { log_error("Failed to parse reply."); @@ -946,19 +1348,36 @@ 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."); - return -EIO; + r = -EIO; + goto finish; } - e = arg_full ? NULL : ellipsize(name, 25, 33); - printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state); + if (!GREEDY_REALLOC(jobs, size, used + 1)) { + r = log_oom(); + goto finish; + } - k++; + jobs[used++] = (struct job_info) { id, + strdup(name), + strdup(type), + strdup(state) }; + if (!jobs[used-1].name || !jobs[used-1].type || !jobs[used-1].state) { + r = log_oom(); + goto finish; + } dbus_message_iter_next(&sub); } - if (on_tty()) - printf("\n%u jobs listed.\n", k); + list_jobs_print(jobs, used); + + finish: + while (used--) { + free(jobs[used].name); + free(jobs[used].type); + free(jobs[used].state); + } + free(jobs); return 0; } @@ -1100,7 +1519,7 @@ typedef struct WaitData { } WaitData; static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) { - DBusError _cleanup_dbus_error_free_ error; + _cleanup_dbus_error_free_ DBusError error; WaitData *d = data; dbus_error_init(&error); @@ -1336,7 +1755,7 @@ static void check_triggering_units( *load_state_property = "LoadState", *triggered_by_property = "TriggeredBy", *state; - char _cleanup_free_ *unit_path = NULL, *n = NULL; + _cleanup_free_ char *unit_path = NULL, *n = NULL; bool print_warning_label = true; int r; @@ -1504,9 +1923,8 @@ static int start_unit_one( if (!p) return log_oom(); - r = set_put(s, p); + r = set_consume(s, p); if (r < 0) { - free(p); log_error("Failed to add path to set."); return r; } @@ -1550,8 +1968,8 @@ static int start_unit(DBusConnection *bus, char **args) { int r, ret = 0; const char *method, *mode, *one_name; - Set _cleanup_set_free_free_ *s = NULL; - DBusError _cleanup_dbus_error_free_ error; + _cleanup_set_free_free_ Set *s = NULL; + _cleanup_dbus_error_free_ DBusError error; char **name; dbus_error_init(&error); @@ -2577,7 +2995,7 @@ static void show_unit_help(UnitStatusInfo *i) { if (startswith(*p, "man:")) { size_t k; char *e = NULL; - char _cleanup_free_ *page = NULL, *section = NULL; + _cleanup_free_ char *page = NULL, *section = NULL; const char *args[4] = { "man", NULL, NULL, NULL }; pid_t pid; @@ -2861,7 +3279,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { /* This is a low-level property printer, see * print_status_info() for the nicer output */ - if (arg_property && !strv_find(arg_property, name)) + if (arg_properties && !strv_find(arg_properties, name)) return 0; switch (dbus_message_iter_get_arg_type(iter)) { @@ -3022,7 +3440,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { if (exec_status_info_deserialize(&sub, &info) >= 0) { char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; - char _cleanup_free_ *t; + _cleanup_free_ char *t; t = strv_join(info.argv, " "); @@ -3062,7 +3480,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 _cleanup_free_ *reply = NULL; + _cleanup_free_ DBusMessage *reply = NULL; const char *interface = ""; int r; DBusMessageIter iter, sub, sub2, sub3; @@ -3155,7 +3573,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 _cleanup_dbus_error_free_ error; + _cleanup_dbus_error_free_ DBusError error; int r; dbus_error_init(&error); @@ -3195,8 +3613,10 @@ static int show_all(const char* verb, DBusConnection *bus, bool show_properties, if (r < 0) return r; + qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); + for (u = unit_infos; u < unit_infos + c; u++) { - char _cleanup_free_ *p = NULL; + _cleanup_free_ char *p = NULL; if (!output_show_unit(u)) continue; @@ -3668,11 +4088,11 @@ static int enable_sysv_units(char **args) { r = 0; for (f = 1; args[f]; f++) { const char *name; - char *p; + _cleanup_free_ char *p = NULL, *q = NULL; bool found_native = false, found_sysv; unsigned c = 1; const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL }; - char **k, *l, *q = NULL; + char **k, *l; int j; pid_t pid; siginfo_t status; @@ -3686,8 +4106,6 @@ static int enable_sysv_units(char **args) { continue; STRV_FOREACH(k, paths.unit_path) { - p = NULL; - if (!isempty(arg_root)) asprintf(&p, "%s/%s/%s", arg_root, *k, name); else @@ -3700,6 +4118,7 @@ static int enable_sysv_units(char **args) { found_native = access(p, F_OK) >= 0; free(p); + p = NULL; if (found_native) break; @@ -3708,7 +4127,6 @@ static int enable_sysv_units(char **args) { if (found_native) continue; - p = NULL; if (!isempty(arg_root)) asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name); else @@ -3721,10 +4139,8 @@ static int enable_sysv_units(char **args) { p[strlen(p) - sizeof(".service") + 1] = 0; found_sysv = access(p, F_OK) >= 0; - if (!found_sysv) { - free(p); + if (!found_sysv) continue; - } /* Mark this entry, so that we don't try enabling it as native unit */ args[f] = (char*) ""; @@ -3742,8 +4158,6 @@ static int enable_sysv_units(char **args) { l = strv_join((char**)argv, " "); if (!l) { - free(q); - free(p); r = log_oom(); goto finish; } @@ -3754,8 +4168,6 @@ static int enable_sysv_units(char **args) { pid = fork(); if (pid < 0) { log_error("Failed to fork: %m"); - free(p); - free(q); r = -errno; goto finish; } else if (pid == 0) { @@ -3765,9 +4177,6 @@ static int enable_sysv_units(char **args) { _exit(EXIT_FAILURE); } - free(p); - free(q); - j = wait_for_terminate(pid, &status); if (j < 0) { log_error("Failed to wait for child: %s", strerror(-r)); @@ -3851,10 +4260,10 @@ static int enable_unit(DBusConnection *bus, char **args) { UnitFileChange *changes = NULL; unsigned n_changes = 0, i; int carries_install_info = -1; - DBusMessage _cleanup_dbus_message_unref_ *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; int r; - DBusError _cleanup_dbus_error_free_ error; - char _cleanup_strv_free_ **mangled_names = NULL; + _cleanup_dbus_error_free_ DBusError error; + _cleanup_strv_free_ char **mangled_names = NULL; dbus_error_init(&error); @@ -3865,24 +4274,30 @@ static int enable_unit(DBusConnection *bus, char **args) { if (!args[1]) return 0; + r = mangle_names(args+1, &mangled_names); + if (r < 0) + goto finish; + if (!bus || avoid_bus()) { if (streq(verb, "enable")) { - r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_enable(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "disable")) - r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes); + r = unit_file_disable(arg_scope, arg_runtime, arg_root, mangled_names, &changes, &n_changes); else if (streq(verb, "reenable")) { - r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_reenable(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "link")) - r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_link(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); else if (streq(verb, "preset")) { - r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_preset(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "mask")) - r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); + r = unit_file_mask(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) - r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes); + r = unit_file_unmask(arg_scope, arg_runtime, arg_root, mangled_names, &changes, &n_changes); + else if (streq(verb, "set-default")) + r = unit_file_set_default(arg_scope, arg_root, args[1], &changes, &n_changes); else assert_not_reached("Unknown verb"); @@ -3926,6 +4341,8 @@ static int enable_unit(DBusConnection *bus, char **args) { else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; + } else if (streq(verb, "set-default")) { + method = "SetDefaultTarget"; } else assert_not_reached("Unknown verb"); @@ -3941,10 +4358,6 @@ static int enable_unit(DBusConnection *bus, char **args) { dbus_message_iter_init_append(m, &iter); - r = mangle_names(args+1, &mangled_names); - if(r < 0) - goto finish; - r = bus_append_strv_iter(&iter, mangled_names); if (r < 0) { log_error("Failed to append unit files."); @@ -4049,10 +4462,55 @@ finish: return r; } +static int set_log_level(DBusConnection *bus, char **args) { + _cleanup_dbus_error_free_ DBusError error; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub; + const char* property = "LogLevel"; + const char* interface = "org.freedesktop.systemd1.Manager"; + const char* value; + + assert(bus); + assert(args); + + value = args[1]; + dbus_error_init(&error); + + m = dbus_message_new_method_call("org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties", + "Set"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &sub)) + return log_oom(); + + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &value)) { + dbus_message_iter_abandon_container(&iter, &sub); + return log_oom(); + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + return log_oom(); + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + return -EIO; + } + + return 0; +} + static int unit_is_enabled(DBusConnection *bus, char **args) { - DBusError _cleanup_dbus_error_free_ error; + _cleanup_dbus_error_free_ DBusError error; int r; - DBusMessage _cleanup_dbus_message_unref_ *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; bool enabled; char **name; char *n; @@ -4147,11 +4605,16 @@ static int systemctl_help(void) { " --version Show package version\n" " -t --type=TYPE List only units of a particular type\n" " -p --property=NAME Show only properties by this name\n" - " -a --all Show all units/properties, including dead/empty ones\n" + " -a --all Show all loaded units/properties, including dead/empty\n" + " ones. To list all units installed on the system, use\n" + " the 'list-unit-files' command instead.\n" + " --reverse Show reverse dependencies with 'list-dependencies'\n" " --failed Show only failed units\n" " --full Don't ellipsize unit names on output\n" " --fail When queueing a new job, fail if conflicting jobs are\n" " pending\n" + " --irreversible Create jobs which cannot be implicitly cancelled\n" + " --show-types When showing sockets, explicitly show their type\n" " --ignore-dependencies\n" " When queueing a new job, ignore all its dependencies\n" " -i --ignore-inhibitors\n" @@ -4211,7 +4674,8 @@ static int systemctl_help(void) { " 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" + " or wanted by this unit or by which this\n" + " unit is required or wanted\n\n" "Unit File Commands:\n" " list-unit-files List installed unit files\n" " enable [NAME...] Enable one or more unit files\n" @@ -4223,6 +4687,8 @@ static int systemctl_help(void) { " unmask [NAME...] Unmask one or more units\n" " link [PATH...] Link one or more units files into\n" " the search path\n" + " get-default Get the name of the default target\n" + " set-default NAME Set the default target\n" " is-enabled [NAME...] Check whether unit files are enabled\n\n" "Job Commands:\n" " list-jobs List jobs\n" @@ -4347,6 +4813,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { enum { ARG_FAIL = 0x100, + ARG_REVERSE, + ARG_AFTER, + ARG_BEFORE, + ARG_SHOW_TYPES, ARG_IRREVERSIBLE, ARG_IGNORE_DEPENDENCIES, ARG_VERSION, @@ -4364,7 +4834,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_NO_ASK_PASSWORD, ARG_FAILED, ARG_RUNTIME, - ARG_FORCE + ARG_FORCE, + ARG_PLAIN }; static const struct option options[] = { @@ -4373,6 +4844,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "type", required_argument, NULL, 't' }, { "property", required_argument, NULL, 'p' }, { "all", no_argument, NULL, 'a' }, + { "reverse", no_argument, NULL, ARG_REVERSE }, + { "after", no_argument, NULL, ARG_AFTER }, + { "before", no_argument, NULL, ARG_BEFORE }, + { "show-types", no_argument, NULL, ARG_SHOW_TYPES }, { "failed", no_argument, NULL, ARG_FAILED }, { "full", no_argument, NULL, ARG_FULL }, { "fail", no_argument, NULL, ARG_FAIL }, @@ -4398,6 +4873,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "runtime", no_argument, NULL, ARG_RUNTIME }, { "lines", required_argument, NULL, 'n' }, { "output", required_argument, NULL, 'o' }, + { "plain", no_argument, NULL, ARG_PLAIN }, { NULL, 0, NULL, 0 } }; @@ -4419,45 +4895,67 @@ static int systemctl_parse_argv(int argc, char *argv[]) { puts(SYSTEMD_FEATURES); return 0; - case 't': - if (streq(optarg, "help")) { - help_types(); - return 0; - } + case 't': { + char *word, *state; + size_t size; - if (unit_type_from_string(optarg) >= 0) { - arg_type = optarg; - break; - } - if (unit_load_state_from_string(optarg) >= 0) { - arg_load_state = optarg; - break; + FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { + _cleanup_free_ char *type; + + type = strndup(word, size); + if (!type) + return -ENOMEM; + + if (streq(type, "help")) { + help_types(); + return 0; + } + + if (unit_type_from_string(type) >= 0) { + if (strv_push(&arg_types, type)) + return log_oom(); + type = NULL; + continue; + } + + if (unit_load_state_from_string(optarg) >= 0) { + if (strv_push(&arg_load_states, type)) + return log_oom(); + type = NULL; + continue; + } + + log_error("Unknown unit type or load state '%s'.", type); + log_info("Use -t help to see a list of allowed values."); + return -EINVAL; } - log_error("Unkown unit type or load state '%s'.", - optarg); - log_info("Use -t help to see a list of allowed values."); - return -EINVAL; + + break; + } + case 'p': { - 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; + if (isempty(optarg) && !arg_properties) { + arg_properties = strv_new(NULL, NULL); + if (!arg_properties) + return log_oom(); + } else { + char *word, *state; + size_t size; - prop = strndup(word, size); - if (!prop) - return -ENOMEM; + FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { + char *prop; - tmp = strv_append(arg_property, prop); - if (!tmp) - return -ENOMEM; + prop = strndup(word, size); + if (!prop) + return log_oom(); - strv_free(arg_property); - arg_property = tmp; + if (strv_push(&arg_properties, prop)) { + free(prop); + return log_oom(); + } + } } /* If the user asked for a particular @@ -4472,6 +4970,22 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_all = true; break; + case ARG_REVERSE: + arg_dependency = DEPENDENCY_REVERSE; + break; + + case ARG_AFTER: + arg_dependency = DEPENDENCY_AFTER; + break; + + case ARG_BEFORE: + arg_dependency = DEPENDENCY_BEFORE; + break; + + case ARG_SHOW_TYPES: + arg_show_types = true; + break; + case ARG_FAIL: arg_job_mode = "fail"; break; @@ -4587,6 +5101,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_ignore_inhibitors = true; break; + case ARG_PLAIN: + arg_plain = true; + break; + case '?': return -EINVAL; @@ -5019,7 +5537,7 @@ static int parse_argv(int argc, char *argv[]) { return systemctl_parse_argv(argc, argv); } -static int action_to_runlevel(void) { +_pure_ static int action_to_runlevel(void) { static const char table[_ACTION_MAX] = { [ACTION_HALT] = '0', @@ -5038,8 +5556,8 @@ static int action_to_runlevel(void) { } static int talk_upstart(void) { - DBusMessage _cleanup_dbus_message_unref_ *m = NULL, *reply = NULL; - DBusError _cleanup_dbus_error_free_ error; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_error_free_ DBusError error; int previous, rl, r; char env1_buf[] = "RUNLEVEL=X", @@ -5128,7 +5646,7 @@ finish: static int talk_initctl(void) { struct init_request request = {}; int r; - int _cleanup_close_ fd = -1; + _cleanup_close_ int fd = -1; char rl; rl = action_to_runlevel(); @@ -5153,7 +5671,7 @@ static int talk_initctl(void) { r = loop_write(fd, &request, sizeof(request), false) != sizeof(request); if (r) { log_error("Failed to write to "INIT_FIFO": %m"); - return errno ? -errno : -EIO; + return errno > 0 ? -errno : -EIO; } return 1; @@ -5173,6 +5691,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError } verbs[] = { { "list-units", LESS, 1, list_units }, { "list-unit-files", EQUAL, 1, list_unit_files }, + { "list-sockets", LESS, 1, list_sockets }, { "list-jobs", EQUAL, 1, list_jobs }, { "clear-jobs", EQUAL, 1, daemon_reload }, { "load", MORE, 2, load_unit }, @@ -5231,6 +5750,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "link", MORE, 2, enable_unit }, { "switch-root", MORE, 2, switch_root }, { "list-dependencies", LESS, 2, list_dependencies }, + { "set-default", EQUAL, 2, enable_unit }, + { "get-default", LESS, 1, get_default }, + { "set-log-level", EQUAL, 2, set_log_level }, }; int left; @@ -5302,7 +5824,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError !streq(verbs[i].verb, "preset") && !streq(verbs[i].verb, "mask") && !streq(verbs[i].verb, "unmask") && - !streq(verbs[i].verb, "link")) { + !streq(verbs[i].verb, "link") && + !streq(verbs[i].verb, "set-default") && + !streq(verbs[i].verb, "get-default")) { if (running_in_chroot() > 0) { log_info("Running in chroot, ignoring request."); @@ -5330,7 +5854,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 _cleanup_close_ fd; + _cleanup_close_ int fd; struct sd_shutdown_command c = { .usec = t, .mode = mode, @@ -5472,7 +5996,7 @@ static int halt_main(DBusConnection *bus) { } if (arg_when > 0) { - char _cleanup_free_ *m; + _cleanup_free_ char *m; m = strv_join(arg_wall, " "); r = send_shutdownd(arg_when, @@ -5535,7 +6059,7 @@ static int runlevel_main(void) { int main(int argc, char*argv[]) { int r, retval = EXIT_FAILURE; DBusConnection *bus = NULL; - DBusError _cleanup_dbus_error_free_ error; + _cleanup_dbus_error_free_ DBusError error; dbus_error_init(&error); @@ -5640,7 +6164,9 @@ finish: dbus_shutdown(); - strv_free(arg_property); + strv_free(arg_types); + strv_free(arg_load_states); + strv_free(arg_properties); pager_close(); ask_password_agent_close();