X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl.c;h=45e900c5fe0b9f67db7959a4a866e96bd270e28f;hp=03143379030cf2d7e10213a6fc071dfe8d530262;hb=b708e7cea941538bfd5e20ce0a723c19b7da7d1d;hpb=bc8c2f5c549084df93c061b061f1e5b0d9d7046c diff --git a/src/systemctl.c b/src/systemctl.c index 031433790..45e900c5f 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -43,11 +43,13 @@ #include "strv.h" #include "dbus-common.h" #include "cgroup-show.h" +#include "cgroup-util.h" +#include "list.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_fail = false; static bool arg_session = false; static bool arg_no_block = false; static bool arg_immediate = false; @@ -740,8 +742,8 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) { (streq(args[0], "isolate") || streq(args[0], "rescue") || streq(args[0], "emergency")) ? "isolate" : - arg_replace ? "replace" : - "fail"; + arg_fail ? "fail" : + "replace"; one_name = table[verb_to_action(args[0])]; @@ -792,12 +794,17 @@ finish: } static int start_special(DBusConnection *bus, char **args, unsigned n) { + int r; + assert(bus); assert(args); - warn_wall(verb_to_action(args[0])); + r = start_unit(bus, args, n); + + if (r >= 0) + warn_wall(verb_to_action(args[0])); - return start_unit(bus, args, n); + return r; } static int check_unit(DBusConnection *bus, char **args, unsigned n) { @@ -922,6 +929,100 @@ finish: return r; } +typedef struct ExecStatusInfo { + char *path; + char **argv; + + bool ignore; + + usec_t start_timestamp; + usec_t exit_timestamp; + pid_t pid; + int code; + int status; + + LIST_FIELDS(struct ExecStatusInfo, exec); +} ExecStatusInfo; + +static void exec_status_info_free(ExecStatusInfo *i) { + assert(i); + + free(i->path); + strv_free(i->argv); + free(i); +} + +static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) { + uint64_t start_timestamp, exit_timestamp; + DBusMessageIter sub2, sub3; + const char*path; + unsigned n; + uint32_t pid; + int32_t code, status; + dbus_bool_t ignore; + + assert(i); + assert(i); + + if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT) + return -EIO; + + dbus_message_iter_recurse(sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0) + return -EIO; + + if (!(i->path = strdup(path))) + return -ENOMEM; + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING) + return -EIO; + + n = 0; + dbus_message_iter_recurse(&sub2, &sub3); + while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) { + assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING); + dbus_message_iter_next(&sub3); + n++; + } + + + if (!(i->argv = new0(char*, n+1))) + return -ENOMEM; + + n = 0; + 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); + dbus_message_iter_next(&sub3); + + if (!(i->argv[n++] = strdup(s))) + return -ENOMEM; + } + + if (!dbus_message_iter_next(&sub2) || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, 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) + return -EIO; + + i->ignore = ignore; + i->start_timestamp = (usec_t) start_timestamp; + i->exit_timestamp = (usec_t) exit_timestamp; + i->pid = (pid_t) pid; + i->code = code; + i->status = status; + + return 0; +} + typedef struct UnitStatusInfo { const char *id; const char *load_state; @@ -947,6 +1048,7 @@ typedef struct UnitStatusInfo { /* Socket */ unsigned n_accepted; unsigned n_connections; + bool accept; /* Device */ const char *sysfs_path; @@ -956,9 +1058,14 @@ typedef struct UnitStatusInfo { /* Swap */ const char *what; + + LIST_HEAD(ExecStatusInfo, exec); } UnitStatusInfo; static void print_status_info(UnitStatusInfo *i) { + ExecStatusInfo *p; + int r; + assert(i); /* This shows pretty information about a unit. See @@ -978,14 +1085,23 @@ static void print_status_info(UnitStatusInfo *i) { 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 (streq_ptr(i->active_state, "maintenance")) { + if (streq_ptr(i->active_state, i->sub_state)) + printf("\t Active: " ANSI_HIGHLIGHT_ON "%s" ANSI_HIGHLIGHT_OFF "\n", + strna(i->active_state)); + else + printf("\t Active: " ANSI_HIGHLIGHT_ON "%s (%s)" ANSI_HIGHLIGHT_OFF "\n", + strna(i->active_state), + strna(i->sub_state)); + } else { + if (streq_ptr(i->active_state, i->sub_state)) + printf("\t Active: %s\n", + strna(i->active_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); @@ -994,17 +1110,41 @@ static void print_status_info(UnitStatusInfo *i) { 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")) + if (i->accept) printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); + LIST_FOREACH(exec, p, i->exec) { + char *t; + + /* Only show exited processes here */ + if (p->code == 0) + continue; + + t = strv_join(p->argv, " "); + printf("\t Exited: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code)); + free(t); + + if (p->code == CLD_EXITED) + printf("status=%i", p->status); + else + printf("signal=%s", signal_to_string(p->status)); + printf(")\n"); + + if (i->main_pid == p->pid && + i->start_timestamp == p->start_timestamp && + i->exit_timestamp == p->start_timestamp) + /* Let's not show this twice */ + i->main_pid = 0; + + if (p->pid == i->control_pid) + i->control_pid = 0; + } + if (i->main_pid > 0 || i->control_pid > 0) { printf("\t"); if (i->main_pid > 0) { - printf(" Process: %u", (unsigned) i->main_pid); + printf(" Main: %u", (unsigned) i->main_pid); if (i->running) { char *t = NULL; @@ -1019,7 +1159,7 @@ static void print_status_info(UnitStatusInfo *i) { if (i->exit_code == CLD_EXITED) printf("status=%i", i->exit_status); else - printf("signal=%s", strsignal(i->exit_status)); + printf("signal=%s", signal_to_string(i->exit_status)); printf(")"); } } @@ -1042,6 +1182,9 @@ static void print_status_info(UnitStatusInfo *i) { printf("\n"); } + if (i->status_text) + printf("\t Status: \"%s\"\n", i->status_text); + if (i->default_control_group) { unsigned c; @@ -1052,7 +1195,10 @@ static void print_status_info(UnitStatusInfo *i) { else c = 0; - show_cgroup_recursive(i->default_control_group, "\t\t ", c); + if ((r = cg_init()) < 0) + log_error("Failed to initialize libcg: %s", strerror(-r)); + else + show_cgroup_recursive(i->default_control_group, "\t\t ", c); } } @@ -1093,6 +1239,17 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn break; } + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t b; + + dbus_message_iter_get_basic(iter, &b); + + if (streq(name, "Accept")) + i->accept = b; + + break; + } + case DBUS_TYPE_UINT32: { uint32_t u; @@ -1141,6 +1298,34 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn break; } + + case DBUS_TYPE_ARRAY: { + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && + startswith(name, "Exec")) { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + ExecStatusInfo *info; + int r; + + if (!(info = new0(ExecStatusInfo, 1))) + return -ENOMEM; + + if ((r = exec_status_info_deserialize(&sub, info)) < 0) { + free(info); + return r; + } + + LIST_PREPEND(ExecStatusInfo, exec, i->exec, info); + + dbus_message_iter_next(&sub); + } + } + + break; + } } return 0; @@ -1254,7 +1439,6 @@ static int print_property(const char *name, DBusMessageIter *iter) { 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); @@ -1274,11 +1458,11 @@ static int print_property(const char *name, DBusMessageIter *iter) { } 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); @@ -1297,11 +1481,11 @@ static int print_property(const char *name, DBusMessageIter *iter) { } 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; @@ -1315,11 +1499,11 @@ static int print_property(const char *name, DBusMessageIter *iter) { } 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; @@ -1341,59 +1525,39 @@ static int print_property(const char *name, DBusMessageIter *iter) { } return 0; - } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) { - DBusMessageIter sub, sub2, sub3; + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) { + DBusMessageIter sub; 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) { + ExecStatusInfo info; + zero(info); + if (exec_status_info_deserialize(&sub, &info) >= 0) { char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; + char *t; + + t = strv_join(info.argv, " "); + + printf("%s={ path=%s ; argv[]=%s ; ignore=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n", + name, + strna(info.path), + strna(t), + yes_no(info.ignore), + strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), + strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), + (unsigned) info. pid, + sigchld_code_to_string(info.code), + info.status, + info.code == CLD_EXITED ? "" : "/", + strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status))); - 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))); + free(t); } - printf(" }\n"); + free(info.path); + strv_free(info.argv); dbus_message_iter_next(&sub); } @@ -1417,6 +1581,7 @@ static int show_one(DBusConnection *bus, const char *path, bool show_properties, DBusError error; DBusMessageIter iter, sub, sub2, sub3; UnitStatusInfo info; + ExecStatusInfo *p; assert(bus); assert(path); @@ -1506,6 +1671,11 @@ static int show_one(DBusConnection *bus, const char *path, bool show_properties, if (!show_properties) print_status_info(&info); + while ((p = info.exec)) { + LIST_REMOVE(ExecStatusInfo, exec, info.exec, p); + exec_status_info_free(p); + } + r = 0; finish: @@ -2331,7 +2501,7 @@ static int systemctl_help(void) { " -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" + " --fail When installing a new job, fail if conflicting jobs are pending\n" " --system Connect to system bus\n" " --session Connect to session bus\n" " -q --quiet Suppress output\n" @@ -2440,7 +2610,7 @@ static int runlevel_help(void) { static int systemctl_parse_argv(int argc, char *argv[]) { enum { - ARG_REPLACE = 0x100, + ARG_FAIL = 0x100, ARG_SESSION, ARG_SYSTEM, ARG_NO_BLOCK, @@ -2452,7 +2622,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "type", required_argument, NULL, 't' }, { "property", required_argument, NULL, 'p' }, { "all", no_argument, NULL, 'a' }, - { "replace", no_argument, NULL, ARG_REPLACE }, + { "fail", no_argument, NULL, ARG_FAIL }, { "session", no_argument, NULL, ARG_SESSION }, { "system", no_argument, NULL, ARG_SYSTEM }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, @@ -2491,8 +2661,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_all = true; break; - case ARG_REPLACE: - arg_replace = true; + case ARG_FAIL: + arg_fail = true; break; case ARG_SESSION: @@ -2897,7 +3067,7 @@ static int talk_upstart(void) { if (utmp_get_runlevel(&previous, NULL) < 0) previous = 'N'; - if (!(bus = dbus_connection_open("unix:abstract=/com/ubuntu/upstart", &error))) { + if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) { if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) { r = 0; goto finish; @@ -2961,8 +3131,10 @@ finish: if (reply) dbus_message_unref(reply); - if (bus) + if (bus) { + dbus_connection_close(bus); dbus_connection_unref(bus); + } dbus_error_free(&error); @@ -3131,26 +3303,29 @@ static int reload_with_fallback(DBusConnection *bus) { static int start_with_fallback(DBusConnection *bus) { int r; - warn_wall(arg_action); if (bus) { /* First, try systemd via D-Bus. */ if ((r = start_unit(bus, NULL, 0)) > 0) - return 0; + goto done; /* Hmm, talking to systemd via D-Bus didn't work. Then * let's try to talk to Upstart via D-Bus. */ if ((r = talk_upstart()) > 0) - return 0; + goto done; } /* Nothing else worked, so let's try * /dev/initctl */ if ((r = talk_initctl()) != 0) - return 0; + goto done; log_error("Failed to talk to init daemon."); return -EIO; + +done: + warn_wall(arg_action); + return 0; } static int halt_main(DBusConnection *bus) { @@ -3285,8 +3460,10 @@ int main(int argc, char*argv[]) { finish: - if (bus) + if (bus) { + dbus_connection_close(bus); dbus_connection_unref(bus); + } dbus_error_free(&error);