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=b66901b9ed2a073ae72345c098a5e4c8401c14aa;hb=9a1ac7b9ae2fb218170d1bd106d5351a76d03a95;hpb=48220598fed70bd2c310c52a4f9af9224b87abf4 diff --git a/src/systemctl.c b/src/systemctl.c index b66901b9e..73b0fc48c 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -41,6 +41,7 @@ #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; @@ -106,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; @@ -583,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'," @@ -600,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) { @@ -990,10 +934,266 @@ finish: 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; @@ -1029,6 +1229,10 @@ static int print_property(const char *name, DBusMessageIter *iter) { 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); @@ -1039,7 +1243,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { uint32_t u; dbus_message_iter_get_basic(iter, &u); - if (strstr(name, "UMask")) + if (strstr(name, "UMask") || strstr(name, "Mode")) printf("%s=%04o\n", name, u); else printf("%s=%u\n", name, (unsigned) u); @@ -1059,7 +1263,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { DBusMessageIter sub; dbus_message_iter_recurse(iter, &sub); - if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && strstr(name, "Job")) { + if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) { uint32_t u; dbus_message_iter_get_basic(&sub, &u); @@ -1070,7 +1274,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { printf("%s=\n", name); return 0; - } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && strstr(name, "Unit")) { + } 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); @@ -1110,6 +1314,131 @@ static int print_property(const char *name, DBusMessageIter *iter) { 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; } @@ -1122,16 +1451,19 @@ static int print_property(const char *name, DBusMessageIter *iter) { return 0; } -static int show_one(DBusConnection *bus, const char *path) { +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( @@ -1168,6 +1500,11 @@ static int show_one(DBusConnection *bus, const char *path) { 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; @@ -1193,7 +1530,12 @@ static int show_one(DBusConnection *bus, const char *path) { dbus_message_iter_recurse(&sub2, &sub3); - if (print_property(name, &sub3) < 0) { + 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; @@ -1202,6 +1544,9 @@ static int show_one(DBusConnection *bus, const char *path) { dbus_message_iter_next(&sub); } + if (!show_properties) + print_status_info(&info); + r = 0; finish: @@ -1221,17 +1566,20 @@ static int show(DBusConnection *bus, char **args, unsigned n) { int r; DBusError error; unsigned i; + bool show_properties, new_line = false; assert(bus); assert(args); dbus_error_init(&error); - if (n <= 1) { + 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"); + r = show_one(bus, "/org/freedesktop/systemd1", show_properties, &new_line); goto finish; } @@ -1239,13 +1587,13 @@ static int show(DBusConnection *bus, char **args, unsigned n) { const char *path = NULL; uint32_t id; - if (safe_atou32(args[i], &id) < 0) { + 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", - "GetUnit"))) { + "LoadUnit"))) { log_error("Could not allocate message."); r = -ENOMEM; goto finish; @@ -1294,7 +1642,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) { goto finish; } - if ((r = show_one(bus, path)) < 0) + if ((r = show_one(bus, path, show_properties, &new_line)) < 0) goto finish; dbus_message_unref(m); @@ -1984,7 +2332,7 @@ static int systemctl_help(void) { "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" - " -p --property=NAME Show only property by this name\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" @@ -2000,7 +2348,8 @@ static int systemctl_help(void) { " 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" - " show [NAME...|JOB...] Show information about one or more units\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" @@ -2682,6 +3031,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) { { "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 }, @@ -2889,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) {