X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl.c;h=73b0fc48cc1d0fc93cd652b0cd13c0c1e36ea110;hp=d6f4709c42d34a34023392cbd6a139be5f1ce4c5;hb=9a1ac7b9ae2fb218170d1bd106d5351a76d03a95;hpb=5e374895934d6b8960e391a2b3a1fe57e153a7f8 diff --git a/src/systemctl.c b/src/systemctl.c index d6f4709c4..73b0fc48c 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -41,8 +41,10 @@ #include "special.h" #include "initreq.h" #include "strv.h" +#include "dbus-common.h" static const char *arg_type = NULL; +static const char *arg_property = NULL; static bool arg_all = false; static bool arg_replace = false; static bool arg_session = false; @@ -105,32 +107,6 @@ static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *da return 0; } -static int bus_check_peercred(DBusConnection *c) { - int fd; - struct ucred ucred; - socklen_t l; - - assert(c); - - assert_se(dbus_connection_get_unix_fd(c, &fd)); - - l = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) { - log_error("SO_PEERCRED failed: %m"); - return -errno; - } - - if (l != sizeof(struct ucred)) { - log_error("SO_PEERCRED returned wrong size."); - return -E2BIG; - } - - if (ucred.uid != 0) - return -EPERM; - - return 1; -} - static int columns(void) { static int parsed_columns = 0; const char *e; @@ -582,13 +558,10 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me static int enable_wait_for_jobs(DBusConnection *bus) { DBusError error; - DBusMessage *m = NULL, *reply = NULL; - int r; assert(bus); dbus_error_init(&error); - dbus_bus_add_match(bus, "type='signal'," "sender='org.freedesktop.systemd1'," @@ -599,40 +572,12 @@ static int enable_wait_for_jobs(DBusConnection *bus) { if (dbus_error_is_set(&error)) { log_error("Failed to add match: %s", error.message); - r = -EIO; - goto finish; - } - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Subscribe"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", error.message); - r = -EIO; - goto finish; + dbus_error_free(&error); + return -EIO; } - r = 0; - -finish: /* This is slightly dirty, since we don't undo the match registrations. */ - - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return 0; } static int wait_for_jobs(DBusConnection *bus, Set *s) { @@ -969,7 +914,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) { if (!arg_quiet) puts(state); - if (streq(state, "active") || startswith(state, "active-")) + if (streq(state, "active") || startswith(state, "reloading")) r = 0; dbus_message_unref(m); @@ -987,7 +932,736 @@ finish: dbus_error_free(&error); return r; +} + +static void show_cgroup(const char *name) { + char *fn, *pids; + int r; + char *p; + + if (!startswith(name, "name=systemd:")) + return; + + if (asprintf(&fn, "/cgroup/systemd/%s/tasks", name + 13) < 0) + return; + + r = read_one_line_file(fn, &pids); + free(fn); + + if (r < 0) + return; + + p = pids; + while (p[0]) { + unsigned long ul; + char *t = NULL; + + p += strspn(p, WHITESPACE); + + errno = 0; + ul = strtoul(p, &p, 0); + if (errno != 0 || ul <= 0) + break; + + get_process_cmdline((pid_t) ul, 60, &t); + printf("\t\t%lu %s\n", ul, strna(t)); + free(t); + } + + free(pids); +} + +typedef struct UnitStatusInfo { + const char *id; + const char *load_state; + const char *active_state; + const char *sub_state; + + const char *description; + + const char *fragment_path; + const char *default_control_group; + + /* Service */ + pid_t main_pid; + pid_t control_pid; + const char *status_text; + bool running; + + usec_t start_timestamp; + usec_t exit_timestamp; + + int exit_code, exit_status; + + /* Socket */ + unsigned n_accepted; + unsigned n_connections; + + /* Device */ + const char *sysfs_path; + + /* Mount, Automount */ + const char *where; + + /* Swap */ + const char *what; +} UnitStatusInfo; + +static void print_status_info(UnitStatusInfo *i) { + assert(i); + + /* This shows pretty information about a unit. See + * print_property() for a low-level property printer */ + + printf("%s", strna(i->id)); + + if (i->description && !streq_ptr(i->id, i->description)) + printf(" - %s", i->description); + + printf("\n"); + + if (i->fragment_path) + printf("\t Loaded: %s (%s)\n", strna(i->load_state), i->fragment_path); + else if (streq_ptr(i->load_state, "failed")) + printf("\t Loaded: " ANSI_HIGHLIGHT_ON "%s" ANSI_HIGHLIGHT_OFF "\n", strna(i->load_state)); + else + printf("\t Loaded: %s\n", strna(i->load_state)); + + if (streq_ptr(i->active_state, "maintenance")) + printf("\t Active: " ANSI_HIGHLIGHT_ON "%s (%s)" ANSI_HIGHLIGHT_OFF "\n", + strna(i->active_state), + strna(i->sub_state)); + else + printf("\t Active: %s (%s)\n", + strna(i->active_state), + strna(i->sub_state)); + + if (i->sysfs_path) + printf("\t Device: %s\n", i->sysfs_path); + else if (i->where) + printf("\t Where: %s\n", i->where); + else if (i->what) + printf("\t What: %s\n", i->what); + + if (i->status_text) + printf("\t Status: \"%s\"\n", i->status_text); + + if (i->id && endswith(i->id, ".socket")) + printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); + + if (i->main_pid > 0 || i->control_pid > 0) { + printf("\t"); + + if (i->main_pid > 0) { + printf(" Process: %u", (unsigned) i->main_pid); + + if (i->running) { + char *t = NULL; + get_process_name(i->main_pid, &t); + if (t) { + printf(" (%s)", t); + free(t); + } + } else { + printf(" (code=%s, ", sigchld_code_to_string(i->exit_code)); + + if (i->exit_code == CLD_EXITED) + printf("status=%i", i->exit_status); + else + printf("signal=%s", strsignal(i->exit_status)); + printf(")"); + } + } + + if (i->main_pid > 0 && i->control_pid > 0) + printf(";"); + + if (i->control_pid > 0) { + char *t = NULL; + + printf(" Control: %u", (unsigned) i->control_pid); + + get_process_name(i->control_pid, &t); + if (t) { + printf(" (%s)", t); + free(t); + } + } + + printf("\n"); + } + + if (i->default_control_group) { + printf("\t CGroup: %s\n", i->default_control_group); + show_cgroup(i->default_control_group); + } +} + +static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) { + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + + dbus_message_iter_get_basic(iter, &s); + + if (s[0]) { + if (streq(name, "Id")) + i->id = s; + else if (streq(name, "LoadState")) + i->load_state = s; + else if (streq(name, "ActiveState")) + i->active_state = s; + else if (streq(name, "SubState")) + i->sub_state = s; + else if (streq(name, "Description")) + i->description = s; + else if (streq(name, "FragmentPath")) + i->fragment_path = s; + else if (streq(name, "DefaultControlGroup")) + i->default_control_group = s; + else if (streq(name, "StatusText")) + i->status_text = s; + else if (streq(name, "SysFSPath")) + i->sysfs_path = s; + else if (streq(name, "Where")) + i->where = s; + else if (streq(name, "What")) + i->what = s; + } + + break; + } + + case DBUS_TYPE_UINT32: { + uint32_t u; + + dbus_message_iter_get_basic(iter, &u); + + if (streq(name, "MainPID")) { + if (u > 0) { + i->main_pid = (pid_t) u; + i->running = true; + } + } else if (streq(name, "ControlPID")) + i->control_pid = (pid_t) u; + else if (streq(name, "ExecMainPID")) { + if (u > 0) + i->main_pid = (pid_t) u; + } else if (streq(name, "NAccepted")) + i->n_accepted = u; + else if (streq(name, "NConnections")) + i->n_connections = u; + + break; + } + + case DBUS_TYPE_INT32: { + int32_t j; + + dbus_message_iter_get_basic(iter, &j); + + if (streq(name, "ExecMainCode")) + i->exit_code = (int) j; + else if (streq(name, "ExecMainStatus")) + i->exit_status = (int) j; + + break; + } + + case DBUS_TYPE_UINT64: { + uint64_t u; + + dbus_message_iter_get_basic(iter, &u); + + if (streq(name, "ExecMainStartTimestamp")) + i->start_timestamp = (usec_t) u; + else if (streq(name, "ExecMainExitTimestamp")) + i->exit_timestamp = (usec_t) u; + + break; + } + } + + return 0; +} + +static int print_property(const char *name, DBusMessageIter *iter) { + assert(name); + assert(iter); + + /* This is a low-level property printer, see + * print_status_info() for the nicer output */ + + if (arg_property && !streq(name, arg_property)) + return 0; + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + dbus_message_iter_get_basic(iter, &s); + + if (arg_all || s[0]) + printf("%s=%s\n", name, s); + + return 0; + } + + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t b; + dbus_message_iter_get_basic(iter, &b); + printf("%s=%s\n", name, yes_no(b)); + + return 0; + } + + case DBUS_TYPE_UINT64: { + uint64_t u; + dbus_message_iter_get_basic(iter, &u); + + /* Yes, heuristics! But we can change this check + * should it turn out to not be sufficient */ + + if (strstr(name, "Timestamp")) { + char timestamp[FORMAT_TIMESTAMP_MAX], *t; + + if ((t = format_timestamp(timestamp, sizeof(timestamp), u)) || arg_all) + printf("%s=%s\n", name, strempty(t)); + } else if (strstr(name, "USec")) { + char timespan[FORMAT_TIMESPAN_MAX]; + + printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u)); + } else + printf("%s=%llu\n", name, (unsigned long long) u); + + return 0; + } + + case DBUS_TYPE_UINT32: { + uint32_t u; + dbus_message_iter_get_basic(iter, &u); + + if (strstr(name, "UMask") || strstr(name, "Mode")) + printf("%s=%04o\n", name, u); + else + printf("%s=%u\n", name, (unsigned) u); + + return 0; + } + + case DBUS_TYPE_INT32: { + int32_t i; + dbus_message_iter_get_basic(iter, &i); + + printf("%s=%i\n", name, (int) i); + return 0; + } + + case DBUS_TYPE_STRUCT: { + DBusMessageIter sub; + dbus_message_iter_recurse(iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) { + uint32_t u; + + dbus_message_iter_get_basic(&sub, &u); + + if (u) + printf("%s=%u\n", name, (unsigned) u); + else if (arg_all) + printf("%s=\n", name); + + return 0; + } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) { + const char *s; + + dbus_message_iter_get_basic(&sub, &s); + + if (arg_all || s[0]) + printf("%s=%s\n", name, s); + + return 0; + } + + break; + } + + case DBUS_TYPE_ARRAY: + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { + DBusMessageIter sub; + bool space = false; + + dbus_message_iter_recurse(iter, &sub); + + if (arg_all || + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + printf("%s=", name); + + 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("%s%s", space ? " " : "", s); + + space = true; + dbus_message_iter_next(&sub); + } + + puts(""); + } + + return 0; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + + if (arg_all || + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + printf("%s=", name); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + uint8_t u; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE); + dbus_message_iter_get_basic(&sub, &u); + printf("%02x", u); + + dbus_message_iter_next(&sub); + } + + puts(""); + } + + return 0; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) { + 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("%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; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *base; + uint64_t value, next_elapse; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) { + char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; + + printf("%s={ value=%s ; next_elapse=%s }\n", + base, + format_timespan(timespan1, sizeof(timespan1), value), + format_timespan(timespan2, sizeof(timespan2), next_elapse)); + } + + dbus_message_iter_next(&sub); + } + + return 0; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) { + + DBusMessageIter sub, sub2, sub3; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *path; + uint64_t start_time, exit_time; + uint32_t pid; + int32_t code, status; + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0) + continue; + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING) + continue; + + printf("%s={ path=%s ; argv[]=", name, path); + + dbus_message_iter_recurse(&sub2, &sub3); + + while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) { + const char *s; + + assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub3, &s); + printf("%s ", s); + dbus_message_iter_next(&sub3); + } + + if (dbus_message_iter_next(&sub2) && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_time, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_time, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) >= 0 && + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) >= 0) { + + char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; + + printf("; start=%s ; stop=%s ; pid=%u ; code=%s ; status=%i/%s", + strna(format_timestamp(timestamp1, sizeof(timestamp1), start_time)), + strna(format_timestamp(timestamp2, sizeof(timestamp2), exit_time)), + (unsigned) pid, + sigchld_code_to_string(code), + status, + strempty(code == CLD_EXITED ? NULL : strsignal(status))); + } + + printf(" }\n"); + + dbus_message_iter_next(&sub); + } + + return 0; + } + + break; + } + + if (arg_all) + printf("%s=[unprintable]\n", name); + + return 0; +} + +static int show_one(DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { + DBusMessage *m = NULL, *reply = NULL; + const char *interface = ""; + int r; + DBusError error; + DBusMessageIter iter, sub, sub2, sub3; + UnitStatusInfo info; + + assert(bus); + assert(path); + assert(new_line); + + zero(info); + dbus_error_init(&error); + + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to issue method call: %s", error.message); + r = -EIO; + 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) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (*new_line) + printf("\n"); + + *new_line = true; + + 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; + } + + 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) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + if (show_properties) + 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; + } + + dbus_message_iter_next(&sub); + } + + if (!show_properties) + print_status_info(&info); + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int show(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + int r; + DBusError error; + unsigned i; + bool show_properties, new_line = false; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + show_properties = !streq(args[0], "status"); + + if (show_properties && n <= 1) { + /* If not argument is specified inspect the manager + * itself */ + + r = show_one(bus, "/org/freedesktop/systemd1", show_properties, &new_line); + goto finish; + } + + for (i = 1; i < n; i++) { + const char *path = NULL; + uint32_t id; + + if (!show_properties || safe_atou32(args[i], &id) < 0) { + + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LoadUnit"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + } else { + + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetJob"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + } + + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to issue method call: %s", error.message); + r = -EIO; + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", error.message); + r = -EIO; + goto finish; + } + + if ((r = show_one(bus, path, show_properties, &new_line)) < 0) + goto finish; + + dbus_message_unref(m); + dbus_message_unref(reply); + m = reply = NULL; + } + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; } static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) { @@ -1363,6 +2037,89 @@ finish: return r; } +static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + int r; + DBusError error; + unsigned i; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + for (i = 1; i < n; i++) { + const char *path = NULL; + + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to issue method call: %s", error.message); + r = -EIO; + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", error.message); + r = -EIO; + goto finish; + } + + dbus_message_unref(m); + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Snapshot", + "Remove"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + dbus_message_unref(reply); + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to issue method call: %s", error.message); + r = -EIO; + goto finish; + } + + dbus_message_unref(m); + dbus_message_unref(reply); + m = reply = NULL; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + static int clear_jobs(DBusConnection *bus, char **args, unsigned n) { DBusMessage *m = NULL, *reply = NULL; DBusError error; @@ -1573,30 +2330,34 @@ static int systemctl_help(void) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Send control commands to the systemd manager.\n\n" - " -h --help Show this help\n" - " -t --type=TYPE List only units of a particular type\n" - " -a --all Show all units, including dead ones\n" - " --replace When installing a new job, replace existing conflicting ones\n" - " --system Connect to system bus\n" - " --session Connect to session bus\n" - " -q --quiet Suppress output\n" - " --no-block Do not wait until operation finished\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n\n" + " -h --help Show this help\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" + " --replace When installing a new job, replace existing conflicting ones\n" + " --system Connect to system bus\n" + " --session Connect to session bus\n" + " -q --quiet Suppress output\n" + " --no-block Do not wait until operation finished\n" + " --no-wall Don't send wall message before halt/power-off/reboot\n\n" "Commands:\n" " list-units List units\n" - " list-jobs List jobs\n" - " clear-jobs Cancel all jobs\n" - " load [NAME...] Load one or more units\n" - " cancel [JOB...] Cancel one or more jobs\n" " start [NAME...] Start one or more units\n" " stop [NAME...] Stop one or more units\n" " restart [NAME...] Restart one or more units\n" " reload [NAME...] Reload one or more units\n" " isolate [NAME] Start one unit and stop all others\n" " check [NAME...] Check whether any of the passed units are active\n" + " status [NAME...] Show status of one or more units\n" + " show [NAME...|JOB...] Show properties of one or more units/jobs/manager\n" + " load [NAME...] Load one or more units\n" + " list-jobs List jobs\n" + " cancel [JOB...] Cancel one or more jobs\n" + " clear-jobs Cancel all jobs\n" " monitor Monitor unit/job changes\n" " dump Dump server status\n" " snapshot [NAME] Create a snapshot\n" + " delete [NAME...] Remove one or more snapshots\n" " daemon-reload Reload systemd manager configuration\n" " daemon-reexec Reexecute systemd manager\n" " daemon-exit Ask the systemd manager to quit\n" @@ -1692,6 +2453,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "type", required_argument, NULL, 't' }, + { "property", required_argument, NULL, 'p' }, { "all", no_argument, NULL, 'a' }, { "replace", no_argument, NULL, ARG_REPLACE }, { "session", no_argument, NULL, ARG_SESSION }, @@ -1707,7 +2469,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "htaq", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "ht:p:aq", options, NULL)) >= 0) { switch (c) { @@ -1719,6 +2481,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_type = optarg; break; + case 'p': + arg_property = optarg; + + /* If the user asked for a particular + * property, show it to him, even if it is + * empty. */ + arg_all = true; + break; + case 'a': arg_all = true; break; @@ -2259,9 +3030,12 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) { { "restart", MORE, 2, start_unit }, { "isolate", EQUAL, 2, start_unit }, { "check", MORE, 2, check_unit }, + { "show", MORE, 1, show }, + { "status", MORE, 2, show }, { "monitor", EQUAL, 1, monitor }, { "dump", EQUAL, 1, dump }, { "snapshot", LESS, 2, snapshot }, + { "delete", MORE, 2, delete_snapshot }, { "daemon-reload", EQUAL, 1, clear_jobs }, { "daemon-reexec", EQUAL, 1, clear_jobs }, { "daemon-exit", EQUAL, 1, clear_jobs }, @@ -2465,19 +3239,7 @@ int main(int argc, char*argv[]) { goto finish; } - /* If we are root, then let's not go via the bus */ - if (geteuid() == 0 && !arg_session) { - bus = dbus_connection_open("unix:abstract=/org/freedesktop/systemd1/private", &error); - - if (bus && bus_check_peercred(bus) < 0) { - log_error("Failed to verify owner of bus."); - goto finish; - } - } else - bus = dbus_bus_get(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error); - - if (bus) - dbus_connection_set_exit_on_disconnect(bus, FALSE); + bus_connect(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &error); switch (arg_action) {