+ if (one_name)
+ names = strv_new(one_name, NULL);
+ else {
+ r = expand_names(bus, args + 1, suffix, &names);
+ if (r < 0)
+ log_error_errno(r, "Failed to expand names: %m");
+ }
+
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ }
+
+ STRV_FOREACH(name, names) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ int q;
+
+ q = start_unit_one(bus, method, *name, mode, &error, w);
+ if (r >= 0 && q < 0)
+ r = translate_bus_error_to_exit_status(q, &error);
+ }
+
+ if (!arg_no_block) {
+ int q;
+
+ q = bus_wait_for_jobs(w, arg_quiet);
+ if (q < 0)
+ return q;
+
+ /* When stopping units, warn if they can still be triggered by
+ * another active unit (socket, path, timer) */
+ if (!arg_quiet && streq(method, "StopUnit"))
+ STRV_FOREACH(name, names)
+ check_triggering_units(bus, *name);
+ }
+
+ return r;
+}
+
+/* Ask systemd-logind, which might grant access to unprivileged users
+ * through PolicyKit */
+static int reboot_with_logind(sd_bus *bus, enum action a) {
+#ifdef HAVE_LOGIND
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *method;
+ int r;
+
+ if (!bus)
+ return -EIO;
+
+ polkit_agent_open_if_enabled();
+
+ switch (a) {
+
+ case ACTION_REBOOT:
+ method = "Reboot";
+ break;
+
+ case ACTION_POWEROFF:
+ method = "PowerOff";
+ break;
+
+ case ACTION_SUSPEND:
+ method = "Suspend";
+ break;
+
+ case ACTION_HIBERNATE:
+ method = "Hibernate";
+ break;
+
+ case ACTION_HYBRID_SLEEP:
+ method = "HybridSleep";
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ method,
+ &error,
+ NULL,
+ "b", arg_ask_password);
+ if (r < 0)
+ log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+
+ return r;
+#else
+ return -ENOSYS;
+#endif
+}
+
+static int check_inhibitors(sd_bus *bus, enum action a) {
+#ifdef HAVE_LOGIND
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_strv_free_ char **sessions = NULL;
+ const char *what, *who, *why, *mode;
+ uint32_t uid, pid;
+ unsigned c = 0;
+ char **s;
+ int r;
+
+ if (!bus)
+ return 0;
+
+ if (arg_ignore_inhibitors || arg_force > 0)
+ return 0;
+
+ if (arg_when > 0)
+ return 0;
+
+ if (geteuid() == 0)
+ return 0;
+
+ if (!on_tty())
+ return 0;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListInhibitors",
+ NULL,
+ &reply,
+ NULL);
+ if (r < 0)
+ /* If logind is not around, then there are no inhibitors... */
+ return 0;
+
+ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
+ _cleanup_free_ char *comm = NULL, *user = NULL;
+ _cleanup_strv_free_ char **sv = NULL;
+
+ if (!streq(mode, "block"))
+ continue;
+
+ sv = strv_split(what, ":");
+ if (!sv)
+ return log_oom();
+
+ if (!strv_contains(sv,
+ a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_KEXEC ? "shutdown" : "sleep"))
+ continue;
+
+ get_process_comm(pid, &comm);
+ user = uid_to_name(uid);
+
+ log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
+ who, pid, strna(comm), strna(user), why);
+
+ c++;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ /* Check for current sessions */
+ sd_get_sessions(&sessions);
+ STRV_FOREACH(s, sessions) {
+ _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL;
+
+ if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid())
+ continue;
+
+ if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
+ continue;
+
+ if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty")))
+ continue;
+
+ sd_session_get_tty(*s, &tty);
+ sd_session_get_seat(*s, &seat);
+ sd_session_get_service(*s, &service);
+ user = uid_to_name(uid);
+
+ log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty);
+ c++;
+ }
+
+ if (c <= 0)
+ return 0;
+
+ log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.",
+ action_table[a].verb);
+
+ return -EPERM;
+#else
+ return 0;
+#endif
+}
+
+static int start_special(sd_bus *bus, char **args) {
+ enum action a;
+ int r;
+
+ assert(args);
+
+ a = verb_to_action(args[0]);
+
+ r = check_inhibitors(bus, a);
+ if (r < 0)
+ return r;
+
+ if (arg_force >= 2 && geteuid() != 0) {
+ log_error("Must be root.");
+ return -EPERM;
+ }
+
+ if (a == ACTION_REBOOT) {
+ r = update_reboot_param_file(args[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_force >= 2 &&
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT))
+ return halt_now(a);
+
+ if (arg_force >= 1 &&
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_KEXEC ||
+ a == ACTION_EXIT))
+ return daemon_reload(bus, args);
+
+ /* first try logind, to allow authentication with polkit */
+ if (geteuid() != 0 &&
+ (a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_SUSPEND ||
+ a == ACTION_HIBERNATE ||
+ a == ACTION_HYBRID_SLEEP)) {
+ r = reboot_with_logind(bus, a);
+ if (r >= 0 || IN_SET(r, -ENOTSUP, -EINPROGRESS))
+ return r;
+ }
+
+ r = start_unit(bus, args);
+ if (r == EXIT_SUCCESS)
+ warn_wall(a);
+
+ return r;
+}
+
+static int check_unit_generic(sd_bus *bus, int code, const char *good_states, char **args) {
+ _cleanup_strv_free_ char **names = NULL;
+ char **name;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ r = expand_names(bus, args, NULL, &names);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
+
+ STRV_FOREACH(name, names) {
+ int state;
+
+ state = check_one_unit(bus, *name, good_states, arg_quiet);
+ if (state < 0)
+ return state;
+ if (state == 0)
+ r = code;
+ }
+
+ return r;
+}
+
+static int check_unit_active(sd_bus *bus, char **args) {
+ /* According to LSB: 3, "program is not running" */
+ return check_unit_generic(bus, 3, "active\0reloading\0", args + 1);
+}
+
+static int check_unit_failed(sd_bus *bus, char **args) {
+ return check_unit_generic(bus, 1, "failed\0", args + 1);
+}
+
+static int kill_unit(sd_bus *bus, char **args) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ char **name;
+ int r, q;
+
+ assert(bus);
+ assert(args);
+
+ polkit_agent_open_if_enabled();
+
+ if (!arg_kill_who)
+ arg_kill_who = "all";
+
+ r = expand_names(bus, args + 1, NULL, &names);
+ if (r < 0)
+ log_error_errno(r, "Failed to expand names: %m");
+
+ STRV_FOREACH(name, names) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ q = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KillUnit");
+ if (q < 0)
+ return bus_log_create_error(q);
+
+ q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (q < 0)
+ return bus_log_create_error(q);
+
+ q = sd_bus_message_append(m, "ssi", *names, arg_kill_who, arg_signal);
+ if (q < 0)
+ return bus_log_create_error(q);
+
+ q = sd_bus_call(bus, m, 0, &error, NULL);
+ if (q < 0) {
+ log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
+ if (r == 0)
+ r = q;
+ }
+ }
+
+ return r;
+}
+
+typedef struct ExecStatusInfo {
+ char *name;
+
+ 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->name);
+ free(i->path);
+ strv_free(i->argv);
+ free(i);
+}
+
+static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
+ uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
+ const char *path;
+ uint32_t pid;
+ int32_t code, status;
+ int ignore, r;
+
+ assert(m);
+ assert(i);
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ else if (r == 0)
+ return 0;
+
+ r = sd_bus_message_read(m, "s", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ i->path = strdup(path);
+ if (!i->path)
+ return log_oom();
+
+ r = sd_bus_message_read_strv(m, &i->argv);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(m,
+ "bttttuii",
+ &ignore,
+ &start_timestamp, &start_timestamp_monotonic,
+ &exit_timestamp, &exit_timestamp_monotonic,
+ &pid,
+ &code, &status);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ 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;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 1;
+}
+
+typedef struct UnitStatusInfo {
+ const char *id;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *unit_file_state;
+ const char *unit_file_preset;
+
+ const char *description;
+ const char *following;
+
+ char **documentation;
+
+ const char *fragment_path;
+ const char *source_path;
+ const char *control_group;
+
+ char **dropin_paths;
+
+ const char *load_error;
+ const char *result;
+
+ usec_t inactive_exit_timestamp;
+ usec_t inactive_exit_timestamp_monotonic;
+ 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;
+ const char *pid_file;
+ bool running:1;
+ int status_errno;
+
+ usec_t start_timestamp;
+ usec_t exit_timestamp;
+
+ int exit_code, exit_status;
+
+ usec_t condition_timestamp;
+ bool condition_result;
+ bool failed_condition_trigger;
+ bool failed_condition_negate;
+ const char *failed_condition;
+ const char *failed_condition_parameter;
+
+ usec_t assert_timestamp;
+ bool assert_result;
+ bool failed_assert_trigger;
+ bool failed_assert_negate;
+ const char *failed_assert;
+ const char *failed_assert_parameter;
+
+ /* Socket */
+ unsigned n_accepted;
+ unsigned n_connections;
+ bool accept;
+
+ /* Pairs of type, path */
+ char **listen;
+
+ /* Device */
+ const char *sysfs_path;
+
+ /* Mount, Automount */
+ const char *where;
+
+ /* Swap */
+ const char *what;
+
+ /* CGroup */
+ uint64_t memory_current;
+ uint64_t memory_limit;
+
+ LIST_HEAD(ExecStatusInfo, exec);
+} UnitStatusInfo;
+
+static void print_status_info(
+ UnitStatusInfo *i,
+ bool *ellipsized) {
+
+ ExecStatusInfo *p;
+ const char *active_on, *active_off, *on, *off, *ss;
+ usec_t timestamp;
+ char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
+ char since2[FORMAT_TIMESTAMP_MAX], *s2;
+ const char *path;
+ char **t, **t2;
+
+ assert(i);
+
+ /* This shows pretty information about a unit. See
+ * print_property() for a low-level property printer */
+
+ if (streq_ptr(i->active_state, "failed")) {
+ active_on = ansi_highlight_red();
+ active_off = ansi_highlight_off();
+ } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+ active_on = ansi_highlight_green();
+ active_off = ansi_highlight_off();
+ } else
+ active_on = active_off = "";
+
+ printf("%s%s%s %s", active_on, draw_special_char(DRAW_BLACK_CIRCLE), active_off, strna(i->id));
+
+ if (i->description && !streq_ptr(i->id, i->description))
+ printf(" - %s", i->description);
+
+ printf("\n");
+
+ if (i->following)
+ printf(" Follow: unit currently follows state of %s\n", i->following);
+
+ if (streq_ptr(i->load_state, "error")) {
+ on = ansi_highlight_red();
+ off = ansi_highlight_off();
+ } else
+ on = off = "";
+
+ path = i->source_path ? i->source_path : i->fragment_path;
+
+ if (i->load_error)
+ printf(" Loaded: %s%s%s (Reason: %s)\n",
+ on, strna(i->load_state), off, i->load_error);
+ else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
+ printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n",
+ on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset);
+ else if (path && !isempty(i->unit_file_state))
+ printf(" Loaded: %s%s%s (%s; %s)\n",
+ on, strna(i->load_state), off, path, i->unit_file_state);
+ else if (path)
+ printf(" Loaded: %s%s%s (%s)\n",
+ on, strna(i->load_state), off, path);
+ else
+ printf(" Loaded: %s%s%s\n",
+ on, strna(i->load_state), off);
+
+ if (!strv_isempty(i->dropin_paths)) {
+ _cleanup_free_ char *dir = NULL;
+ bool last = false;
+ char ** dropin;
+
+ STRV_FOREACH(dropin, i->dropin_paths) {
+ if (! dir || last) {
+ printf(dir ? " " : " Drop-In: ");
+
+ free(dir);
+ dir = NULL;
+
+ if (path_get_parent(*dropin, &dir) < 0) {
+ log_oom();
+ return;
+ }
+
+ printf("%s\n %s", dir,
+ draw_special_char(DRAW_TREE_RIGHT));
+ }
+
+ last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
+
+ printf("%s%s", basename(*dropin), last ? "\n" : ", ");
+ }
+ }
+
+ ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
+ if (ss)
+ printf(" Active: %s%s (%s)%s",
+ active_on, strna(i->active_state), ss, active_off);
+ else
+ printf(" Active: %s%s%s",
+ active_on, strna(i->active_state), active_off);
+
+ if (!isempty(i->result) && !streq(i->result, "success"))
+ printf(" (Result: %s)", i->result);
+
+ 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_relative(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->condition_result && i->condition_timestamp > 0) {
+ s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
+
+ printf("Condition: start %scondition failed%s at %s%s%s\n",
+ ansi_highlight_yellow(), ansi_highlight_off(),
+ s2, s1 ? "; " : "", s1 ? s1 : "");
+ if (i->failed_condition_trigger)
+ printf(" none of the trigger conditions were met\n");
+ else if (i->failed_condition)
+ printf(" %s=%s%s was not met\n",
+ i->failed_condition,
+ i->failed_condition_negate ? "!" : "",
+ i->failed_condition_parameter);
+ }
+
+ if (!i->assert_result && i->assert_timestamp > 0) {
+ s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp);
+
+ printf(" Assert: start %sassertion failed%s at %s%s%s\n",
+ ansi_highlight_red(), ansi_highlight_off(),
+ s2, s1 ? "; " : "", s1 ? s1 : "");
+ if (i->failed_assert_trigger)
+ printf(" none of the trigger assertions were met\n");
+ else if (i->failed_assert)
+ printf(" %s=%s%s was not met\n",
+ i->failed_assert,
+ i->failed_assert_negate ? "!" : "",
+ i->failed_assert_parameter);
+ }
+
+ if (i->sysfs_path)
+ printf(" Device: %s\n", i->sysfs_path);
+ if (i->where)
+ printf(" Where: %s\n", i->where);
+ if (i->what)
+ printf(" What: %s\n", i->what);
+
+ STRV_FOREACH(t, i->documentation)
+ printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t);
+
+ STRV_FOREACH_PAIR(t, t2, i->listen)
+ printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t);
+
+ if (i->accept)
+ printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
+
+ LIST_FOREACH(exec, p, i->exec) {
+ _cleanup_free_ char *argv = NULL;
+ bool good;
+
+ /* Only show exited processes here */
+ if (p->code == 0)
+ continue;
+
+ argv = strv_join(p->argv, " ");
+ printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv));
+
+ good = is_clean_exit_lsb(p->code, p->status, NULL);
+ if (!good) {
+ on = ansi_highlight_red();
+ off = ansi_highlight_off();
+ } else
+ on = off = "";
+
+ printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
+
+ if (p->code == CLD_EXITED) {
+ const char *c;
+
+ printf("status=%i", p->status);
+
+ c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
+ if (c)
+ printf("/%s", c);
+
+ } else
+ printf("signal=%s", signal_to_string(p->status));
+
+ printf(")%s\n", off);
+
+ 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) {
+ if (i->main_pid > 0) {
+ printf(" Main PID: "PID_FMT, i->main_pid);
+
+ if (i->running) {
+ _cleanup_free_ char *comm = NULL;
+ get_process_comm(i->main_pid, &comm);
+ if (comm)
+ printf(" (%s)", comm);
+ } 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);
+
+ c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
+ if (c)
+ printf("/%s", c);
+
+ } else
+ printf("signal=%s", signal_to_string(i->exit_status));
+ printf(")");
+ }
+
+ if (i->control_pid > 0)
+ printf(";");
+ }
+
+ if (i->control_pid > 0) {
+ _cleanup_free_ char *c = NULL;
+
+ printf(" %8s: "PID_FMT, i->main_pid ? "" : " Control", i->control_pid);
+
+ get_process_comm(i->control_pid, &c);
+ if (c)
+ printf(" (%s)", c);
+ }
+
+ printf("\n");
+ }
+
+ if (i->status_text)
+ printf(" Status: \"%s\"\n", i->status_text);
+ if (i->status_errno > 0)
+ printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno));
+
+ if (i->memory_current != (uint64_t) -1) {
+ char buf[FORMAT_BYTES_MAX];
+
+ printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current));
+
+ if (i->memory_limit != (uint64_t) -1)
+ printf(" (limit: %s)\n", format_bytes(buf, sizeof(buf), i->memory_limit));
+ else
+ printf("\n");
+ }
+
+ if (i->control_group &&
+ (i->main_pid > 0 || i->control_pid > 0 ||
+ ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) {
+ unsigned c;
+
+ printf(" CGroup: %s\n", i->control_group);
+
+ if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_MACHINE) {
+ unsigned k = 0;
+ pid_t extra[2];
+ static const char prefix[] = " ";
+
+ c = columns();
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
+ else
+ c = 0;
+
+ if (i->main_pid > 0)
+ extra[k++] = i->main_pid;
+
+ if (i->control_pid > 0)
+ extra[k++] = i->control_pid;
+
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags());
+ }
+ }
+
+ if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) {
+ show_journal_by_unit(
+ stdout,
+ i->id,
+ arg_output,
+ 0,
+ i->inactive_exit_timestamp_monotonic,
+ arg_lines,
+ getuid(),
+ get_output_flags() | OUTPUT_BEGIN_NEWLINE,
+ SD_JOURNAL_LOCAL_ONLY,
+ arg_scope == UNIT_FILE_SYSTEM,
+ ellipsized);
+ }
+
+ if (i->need_daemon_reload)
+ warn_unit_file_changed(i->id);
+}
+
+static void show_unit_help(UnitStatusInfo *i) {
+ char **p;
+
+ assert(i);
+
+ if (!i->documentation) {
+ log_info("Documentation for %s not known.", i->id);
+ return;
+ }
+
+ STRV_FOREACH(p, i->documentation)
+ if (startswith(*p, "man:"))
+ show_man_page(*p + 4, false);
+ else
+ log_info("Can't show: %s", *p);
+}
+
+static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) {
+ int r;
+
+ assert(name);
+ assert(m);
+ assert(i);
+
+ switch (contents[0]) {
+
+ case SD_BUS_TYPE_STRING: {
+ const char *s;
+
+ r = sd_bus_message_read(m, "s", &s);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!isempty(s)) {
+ 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, "SourcePath"))
+ i->source_path = s;
+#ifndef NOLEGACY
+ else if (streq(name, "DefaultControlGroup")) {
+ const char *e;
+ e = startswith(s, SYSTEMD_CGROUP_CONTROLLER ":");
+ if (e)
+ i->control_group = e;
+ }
+#endif
+ else if (streq(name, "ControlGroup"))
+ i->control_group = s;
+ else if (streq(name, "StatusText"))
+ i->status_text = s;
+ else if (streq(name, "PIDFile"))
+ i->pid_file = 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;
+ else if (streq(name, "Following"))
+ i->following = s;
+ else if (streq(name, "UnitFileState"))
+ i->unit_file_state = s;
+ else if (streq(name, "UnitFilePreset"))
+ i->unit_file_preset = s;
+ else if (streq(name, "Result"))
+ i->result = s;
+ }
+
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN: {
+ int b;
+
+ r = sd_bus_message_read(m, "b", &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (streq(name, "Accept"))
+ i->accept = b;
+ else if (streq(name, "NeedDaemonReload"))
+ i->need_daemon_reload = b;
+ else if (streq(name, "ConditionResult"))
+ i->condition_result = b;
+ else if (streq(name, "AssertResult"))
+ i->assert_result = b;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT32: {
+ uint32_t u;
+
+ r = sd_bus_message_read(m, "u", &u);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ 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 SD_BUS_TYPE_INT32: {
+ int32_t j;
+
+ r = sd_bus_message_read(m, "i", &j);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (streq(name, "ExecMainCode"))
+ i->exit_code = (int) j;
+ else if (streq(name, "ExecMainStatus"))
+ i->exit_status = (int) j;
+ else if (streq(name, "StatusErrno"))
+ i->status_errno = (int) j;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_UINT64: {
+ uint64_t u;
+
+ r = sd_bus_message_read(m, "t", &u);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ 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, "InactiveExitTimestampMonotonic"))
+ i->inactive_exit_timestamp_monotonic = (usec_t) u;
+ else if (streq(name, "ActiveExitTimestamp"))
+ i->active_exit_timestamp = (usec_t) u;
+ else if (streq(name, "ConditionTimestamp"))
+ i->condition_timestamp = (usec_t) u;
+ else if (streq(name, "AssertTimestamp"))
+ i->assert_timestamp = (usec_t) u;
+ else if (streq(name, "MemoryCurrent"))
+ i->memory_current = u;
+ else if (streq(name, "MemoryLimit"))
+ i->memory_limit = u;
+
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY:
+
+ if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
+ _cleanup_free_ ExecStatusInfo *info = NULL;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ info = new0(ExecStatusInfo, 1);
+ if (!info)
+ return log_oom();
+
+ while ((r = exec_status_info_deserialize(m, info)) > 0) {
+
+ info->name = strdup(name);
+ if (!info->name)
+ log_oom();
+
+ LIST_PREPEND(exec, i->exec, info);
+
+ info = new0(ExecStatusInfo, 1);
+ if (!info)
+ log_oom();
+ }
+
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) {
+ const char *type, *path;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) {
+
+ r = strv_extend(&i->listen, type);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(&i->listen, path);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "DropInPaths")) {
+
+ r = sd_bus_message_read_strv(m, &i->dropin_paths);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Documentation")) {
+
+ r = sd_bus_message_read_strv(m, &i->documentation);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Conditions")) {
+ const char *cond, *param;
+ int trigger, negate;
+ int32_t state;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) {
+ log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
+ if (state < 0 && (!trigger || !i->failed_condition)) {
+ i->failed_condition = cond;
+ i->failed_condition_trigger = trigger;
+ i->failed_condition_negate = negate;
+ i->failed_condition_parameter = param;
+ }
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) {
+ const char *cond, *param;
+ int trigger, negate;
+ int32_t state;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) {
+ log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
+ if (state < 0 && (!trigger || !i->failed_assert)) {
+ i->failed_assert = cond;
+ i->failed_assert_trigger = trigger;
+ i->failed_assert_negate = negate;
+ i->failed_assert_parameter = param;
+ }
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ } else
+ goto skip;
+
+ break;
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+
+ if (streq(name, "LoadError")) {
+ const char *n, *message;
+
+ r = sd_bus_message_read(m, "(ss)", &n, &message);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!isempty(message))
+ i->load_error = message;
+ } else
+ goto skip;
+
+ break;
+
+ default:
+ goto skip;
+ }
+
+ return 0;
+
+skip:
+ r = sd_bus_message_skip(m, contents);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+}
+
+static int print_property(const char *name, sd_bus_message *m, const char *contents) {
+ int r;
+
+ assert(name);
+ assert(m);
+
+ /* This is a low-level property printer, see
+ * print_status_info() for the nicer output */
+
+ if (arg_properties && !strv_find(arg_properties, name)) {
+ /* skip what we didn't read */
+ r = sd_bus_message_skip(m, contents);
+ return r;
+ }
+
+ switch (contents[0]) {
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+
+ if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) {
+ uint32_t u;
+
+ r = sd_bus_message_read(m, "(uo)", &u, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (u > 0)
+ printf("%s=%"PRIu32"\n", name, u);
+ else if (arg_all)
+ printf("%s=\n", name);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Unit")) {
+ const char *s;
+
+ r = sd_bus_message_read(m, "(so)", &s, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_all || !isempty(s))
+ printf("%s=%s\n", name, s);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "LoadError")) {
+ const char *a = NULL, *b = NULL;
+
+ r = sd_bus_message_read(m, "(ss)", &a, &b);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_all || !isempty(a) || !isempty(b))
+ printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+
+ return 0;
+ } else if (streq_ptr(name, "SystemCallFilter")) {
+ _cleanup_strv_free_ char **l = NULL;
+ int whitelist;
+
+ r = sd_bus_message_enter_container(m, 'r', "bas");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(m, "b", &whitelist);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_strv(m, &l);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_all || whitelist || !strv_isempty(l)) {
+ bool first = true;
+ char **i;
+
+ fputs(name, stdout);
+ fputc('=', stdout);
+
+ if (!whitelist)
+ fputc('~', stdout);
+
+ STRV_FOREACH(i, l) {
+ if (first)
+ first = false;
+ else
+ fputc(' ', stdout);
+
+ fputs(*i, stdout);
+ }
+ fputc('\n', stdout);
+ }
+
+ return 0;
+ }
+
+ break;
+
+ case SD_BUS_TYPE_ARRAY:
+
+ if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "EnvironmentFiles")) {
+ const char *path;
+ int ignore;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sb)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0)
+ printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
+
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Paths")) {
+ const char *type, *path;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
+ printf("%s=%s\n", type, path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) {
+ const char *type, *path;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
+ printf("Listen%s=%s\n", type, path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Timers")) {
+ const char *base;
+ uint64_t value, next_elapse;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(stt)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 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, 0),
+ format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+
+ } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
+ ExecStatusInfo info = {};
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = exec_status_info_deserialize(m, &info)) > 0) {
+ char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
+ _cleanup_free_ char *tt;
+
+ tt = strv_join(info.argv, " ");
+
+ printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }\n",
+ name,
+ strna(info.path),
+ strna(tt),
+ yes_no(info.ignore),
+ strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ 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)));
+
+ free(info.path);
+ strv_free(info.argv);
+ zero(info);