X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl.c;h=03143379030cf2d7e10213a6fc071dfe8d530262;hp=226ecee3adfaeb82d603864c59850a534b3fd8ec;hb=bc8c2f5c549084df93c061b061f1e5b0d9d7046c;hpb=552e4331bf165290eb02596355d9570c1ef9af47 diff --git a/src/systemctl.c b/src/systemctl.c index 226ecee3a..031433790 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -41,6 +41,8 @@ #include "special.h" #include "initreq.h" #include "strv.h" +#include "dbus-common.h" +#include "cgroup-show.h" static const char *arg_type = NULL; static const char *arg_property = NULL; @@ -74,6 +76,8 @@ enum action { _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; +static bool private_bus = false; + static bool error_is_no_service(DBusError *error) { assert(error); @@ -106,57 +110,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; - - if (parsed_columns > 0) - return parsed_columns; - - if ((e = getenv("COLUMNS"))) - parsed_columns = atoi(e); - - if (parsed_columns <= 0) { - struct winsize ws; - zero(ws); - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) - parsed_columns = ws.ws_col; - } - - if (parsed_columns <= 0) - parsed_columns = 80; - - return parsed_columns; - -} - static void warn_wall(enum action action) { static const char *table[_ACTION_MAX] = { [ACTION_HALT] = "The system is going down for system halt NOW!", @@ -262,6 +215,9 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { int a = 0, b = 0; + if (streq(active_state, "maintenance")) + fputs(ANSI_HIGHLIGHT_ON, stdout); + printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a); if (job_id != 0) @@ -273,9 +229,12 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { if (job_id == 0) printf(" "); - printf("%.*s", columns() - a - b - 2, description); + printf(" %.*s", columns() - a - b - 2, description); } + if (streq(active_state, "maintenance")) + fputs(ANSI_HIGHLIGHT_OFF, stdout); + fputs("\n", stdout); k++; } @@ -546,10 +505,10 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me dbus_error_init(&error); - /* log_debug("Got D-Bus request: %s.%s() on %s", */ - /* dbus_message_get_interface(message), */ - /* dbus_message_get_member(message), */ - /* dbus_message_get_path(message)); */ + log_debug("Got D-Bus request: %s.%s() on %s", + dbus_message_get_interface(message), + dbus_message_get_member(message), + dbus_message_get_path(message)); if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { log_error("Warning! D-Bus connection terminated."); @@ -583,13 +542,13 @@ 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); + if (private_bus) + return 0; + dbus_error_init(&error); dbus_bus_add_match(bus, "type='signal'," "sender='org.freedesktop.systemd1'," @@ -600,40 +559,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) { @@ -912,6 +843,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) { if (!arg_quiet) puts("unknown"); + dbus_error_free(&error); continue; } @@ -990,10 +922,237 @@ finish: return r; } +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) { + unsigned c; + + printf("\t CGroup: %s\n", i->default_control_group); + + if ((c = columns()) > 18) + c -= 18; + else + c = 0; + + show_cgroup_recursive(i->default_control_group, "\t\t ", c); + } +} + +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; @@ -1231,7 +1390,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { (unsigned) pid, sigchld_code_to_string(code), status, - strna(code == CLD_EXITED ? NULL : strsignal(status))); + strempty(code == CLD_EXITED ? NULL : strsignal(status))); } printf(" }\n"); @@ -1251,16 +1410,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( @@ -1297,6 +1459,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; @@ -1322,7 +1489,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; @@ -1331,6 +1503,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: @@ -1350,17 +1525,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; } @@ -1368,7 +1546,7 @@ 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", @@ -1388,6 +1566,42 @@ static int show(DBusConnection *bus, char **args, unsigned n) { goto finish; } + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + + if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) { + log_error("Failed to issue method call: %s", error.message); + r = -EIO; + goto finish; + } + + dbus_error_free(&error); + + dbus_message_unref(m); + 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; + } + } + } else { if (!(m = dbus_message_new_method_call( @@ -1407,12 +1621,12 @@ static int show(DBusConnection *bus, char **args, unsigned n) { 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 (!(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, @@ -1423,7 +1637,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); @@ -1454,10 +1668,10 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage dbus_error_init(&error); - /* log_debug("Got D-Bus request: %s.%s() on %s", */ - /* dbus_message_get_interface(message), */ - /* dbus_message_get_member(message), */ - /* dbus_message_get_path(message)); */ + log_debug("Got D-Bus request: %s.%s() on %s", + dbus_message_get_interface(message), + dbus_message_get_member(message), + dbus_message_get_path(message)); if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { log_error("Warning! D-Bus connection terminated."); @@ -1583,43 +1797,45 @@ static int monitor(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); - dbus_bus_add_match(bus, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "path='/org/freedesktop/systemd1'", - &error); + if (!private_bus) { + dbus_bus_add_match(bus, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "path='/org/freedesktop/systemd1'", + &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to add match: %s", error.message); - r = -EIO; - goto finish; - } + if (dbus_error_is_set(&error)) { + log_error("Failed to add match: %s", error.message); + r = -EIO; + goto finish; + } - dbus_bus_add_match(bus, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Unit'," - "member='Changed'", - &error); + dbus_bus_add_match(bus, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Unit'," + "member='Changed'", + &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to add match: %s", error.message); - r = -EIO; - goto finish; - } + if (dbus_error_is_set(&error)) { + log_error("Failed to add match: %s", error.message); + r = -EIO; + goto finish; + } - dbus_bus_add_match(bus, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Job'," - "member='Changed'", - &error); + dbus_bus_add_match(bus, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Job'," + "member='Changed'", + &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to add match: %s", error.message); - r = -EIO; - goto finish; + if (dbus_error_is_set(&error)) { + log_error("Failed to add match: %s", error.message); + r = -EIO; + goto finish; + } } if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) { @@ -2129,7 +2345,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/jobs/manager\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" @@ -2811,6 +3028,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 }, @@ -2826,7 +3044,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) { { "reboot", EQUAL, 1, start_special }, { "default", EQUAL, 1, start_special }, { "rescue", EQUAL, 1, start_special }, - { "emergency", EQUAL, 1, start_special }, + { "emergency", EQUAL, 1, start_special } }; int left; @@ -2938,7 +3156,12 @@ static int start_with_fallback(DBusConnection *bus) { static int halt_main(DBusConnection *bus) { int r; - if (!arg_immediate) + if (geteuid() != 0) { + log_error("Must to be root."); + return -EPERM; + } + + if (!arg_dry && !arg_immediate) return start_with_fallback(bus); if (!arg_no_wtmp) @@ -3018,19 +3241,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, &private_bus, &error); switch (arg_action) {