+ 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;
+ const char *active_state;
+ const char *sub_state;
+
+ const char *description;
+
+ const char *path;
+ const char *default_control_group;
+
+ usec_t inactive_exit_timestamp;
+ usec_t active_enter_timestamp;
+ usec_t active_exit_timestamp;
+ usec_t inactive_enter_timestamp;
+
+ bool need_daemon_reload;
+
+ /* Service */
+ pid_t main_pid;
+ pid_t control_pid;
+ const char *status_text;
+ bool running:1;
+ bool is_sysv:1;
+
+ usec_t start_timestamp;
+ usec_t exit_timestamp;
+
+ int exit_code, exit_status;
+
+ /* Socket */
+ unsigned n_accepted;
+ unsigned n_connections;
+ bool accept;
+
+ /* Device */
+ const char *sysfs_path;
+
+ /* Mount, Automount */
+ const char *where;
+
+ /* Swap */
+ const char *what;
+
+ LIST_HEAD(ExecStatusInfo, exec);
+} UnitStatusInfo;
+
+static void print_status_info(UnitStatusInfo *i) {
+ ExecStatusInfo *p;
+ const char *on, *off, *ss;
+ usec_t timestamp;
+ char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
+ char since2[FORMAT_TIMESTAMP_MAX], *s2;
+
+ 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 (streq_ptr(i->load_state, "failed")) {
+ on = ansi_highlight(true);
+ off = ansi_highlight(false);
+ } else
+ on = off = "";
+
+ if (i->path)
+ printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
+ else
+ printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off);
+
+ ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
+
+ if (streq_ptr(i->active_state, "failed")) {
+ on = ansi_highlight(true);
+ off = ansi_highlight(false);
+ } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+ on = ansi_highlight_green(true);
+ off = ansi_highlight_green(false);
+ } else
+ on = off = "";
+
+ if (ss)
+ printf("\t Active: %s%s (%s)%s",
+ on,
+ strna(i->active_state),
+ ss,
+ off);
+ else
+ printf("\t Active: %s%s%s",
+ on,
+ strna(i->active_state),
+ off);
+
+ timestamp = (streq_ptr(i->active_state, "active") ||
+ streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
+ (streq_ptr(i->active_state, "inactive") ||
+ streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
+ streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
+ i->active_exit_timestamp;
+
+ s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), timestamp);
+
+ if (s1)
+ printf(" since [%s; %s]\n", s2, s1);
+ else if (s2)
+ printf(" since [%s]\n", s2);
+ else
+ printf("\n");
+
+ 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->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 Process: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code));
+ free(t);
+
+ if (p->code == CLD_EXITED) {
+ const char *c;
+
+ printf("status=%i", p->status);
+
+ if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+ printf("/%s", c);
+
+ } 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("Main PID: %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 if (i->exit_code > 0) {
+ printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
+
+ if (i->exit_code == CLD_EXITED) {
+ const char *c;
+
+ printf("status=%i", i->exit_status);
+
+ if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+ printf("/%s", c);
+
+ } else
+ printf("signal=%s", signal_to_string(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->status_text)
+ printf("\t Status: \"%s\"\n", i->status_text);
+
+ 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_by_path(i->default_control_group, "\t\t ", c);
+ }
+
+ if (i->need_daemon_reload)
+ printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
+ ansi_highlight(true),
+ ansi_highlight(false),
+ arg_session ? "--session" : "--system");
+}
+
+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->path = s;
+ else if (streq(name, "SysVPath")) {
+ i->is_sysv = true;
+ i->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_BOOLEAN: {
+ dbus_bool_t b;
+
+ dbus_message_iter_get_basic(iter, &b);
+
+ if (streq(name, "Accept"))
+ i->accept = b;
+ else if (streq(name, "NeedDaemonReload"))
+ i->need_daemon_reload = b;
+
+ 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;
+ else if (streq(name, "ActiveEnterTimestamp"))
+ i->active_enter_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveEnterTimestamp"))
+ i->inactive_enter_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveExitTimestamp"))
+ i->inactive_exit_timestamp = (usec_t) u;
+ else if (streq(name, "ActiveExitTimestamp"))
+ i->active_exit_timestamp = (usec_t) u;
+
+ 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;
+}
+
+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 && !strv_find(arg_property, name))
+ 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);