summaryrefslogtreecommitdiffstats
path: root/src/systemctl/systemctl-show.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 13:00:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 13:00:47 +0000
commit2cb7e0aaedad73b076ea18c6900b0e86c5760d79 (patch)
treeda68ca54bb79f4080079bf0828acda937593a4e1 /src/systemctl/systemctl-show.c
parentInitial commit. (diff)
downloadsystemd-2cb7e0aaedad73b076ea18c6900b0e86c5760d79.tar.xz
systemd-2cb7e0aaedad73b076ea18c6900b0e86c5760d79.zip
Adding upstream version 247.3.upstream/247.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/systemctl/systemctl-show.c2135
1 files changed, 2135 insertions, 0 deletions
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
new file mode 100644
index 0000000..d5efecb
--- /dev/null
+++ b/src/systemctl/systemctl-show.c
@@ -0,0 +1,2135 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/mount.h>
+
+#include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
+#include "bus-print-properties.h"
+#include "bus-unit-procs.h"
+#include "cgroup-show.h"
+#include "cpu-set-util.h"
+#include "errno-util.h"
+#include "exec-util.h"
+#include "exit-status.h"
+#include "format-util.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "in-addr-util.h"
+#include "journal-file.h"
+#include "list.h"
+#include "locale-util.h"
+#include "memory-util.h"
+#include "numa-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "sort-util.h"
+#include "string-table.h"
+#include "systemctl-list-machines.h"
+#include "systemctl-list-units.h"
+#include "systemctl-show.h"
+#include "systemctl-sysv-compat.h"
+#include "systemctl-util.h"
+#include "systemctl.h"
+#include "terminal-util.h"
+#include "utf8.h"
+
+static OutputFlags get_output_flags(void) {
+ return
+ arg_all * OUTPUT_SHOW_ALL |
+ (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
+ colors_enabled() * OUTPUT_COLOR |
+ !arg_quiet * OUTPUT_WARN_CUTOFF;
+}
+
+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;
+
+ ExecCommandFlags flags;
+
+ 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, bool is_ex_prop) {
+ _cleanup_strv_free_ char **ex_opts = NULL;
+ 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, is_ex_prop ? "sasasttttuii" : "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 = is_ex_prop ? sd_bus_message_read_strv(m, &ex_opts) : sd_bus_message_read(m, "b", &ignore);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(m,
+ "ttttuii",
+ &start_timestamp, &start_timestamp_monotonic,
+ &exit_timestamp, &exit_timestamp_monotonic,
+ &pid,
+ &code, &status);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (is_ex_prop) {
+ r = exec_command_flags_from_strv(ex_opts, &i->flags);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert strv to ExecCommandFlags: %m");
+
+ i->ignore = FLAGS_SET(i->flags, EXEC_COMMAND_IGNORE_FAILURE);
+ } else
+ 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 UnitCondition {
+ char *name;
+ char *param;
+ bool trigger;
+ bool negate;
+ int tristate;
+
+ LIST_FIELDS(struct UnitCondition, conditions);
+} UnitCondition;
+
+static void unit_condition_free(UnitCondition *c) {
+ if (!c)
+ return;
+
+ free(c->name);
+ free(c->param);
+ free(c);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UnitCondition*, unit_condition_free);
+
+typedef struct UnitStatusInfo {
+ const char *id;
+ const char *load_state;
+ const char *active_state;
+ const char *freezer_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;
+
+ char **triggered_by;
+ char **triggers;
+
+ 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;
+ bool transient;
+
+ /* 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;
+
+ const char *log_namespace;
+
+ usec_t condition_timestamp;
+ bool condition_result;
+ LIST_HEAD(UnitCondition, conditions);
+
+ usec_t assert_timestamp;
+ bool assert_result;
+ bool failed_assert_trigger;
+ bool failed_assert_negate;
+ const char *failed_assert;
+ const char *failed_assert_parameter;
+ usec_t next_elapse_real;
+ usec_t next_elapse_monotonic;
+
+ /* Socket */
+ unsigned n_accepted;
+ unsigned n_connections;
+ unsigned n_refused;
+ 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_min;
+ uint64_t memory_low;
+ uint64_t memory_high;
+ uint64_t memory_max;
+ uint64_t memory_swap_max;
+ uint64_t memory_limit;
+ uint64_t cpu_usage_nsec;
+ uint64_t tasks_current;
+ uint64_t tasks_max;
+ uint64_t ip_ingress_bytes;
+ uint64_t ip_egress_bytes;
+ uint64_t io_read_bytes;
+ uint64_t io_write_bytes;
+
+ uint64_t default_memory_min;
+ uint64_t default_memory_low;
+
+ LIST_HEAD(ExecStatusInfo, exec);
+} UnitStatusInfo;
+
+static void unit_status_info_free(UnitStatusInfo *info) {
+ ExecStatusInfo *p;
+ UnitCondition *c;
+
+ strv_free(info->documentation);
+ strv_free(info->dropin_paths);
+ strv_free(info->triggered_by);
+ strv_free(info->triggers);
+ strv_free(info->listen);
+
+ while ((c = info->conditions)) {
+ LIST_REMOVE(conditions, info->conditions, c);
+ unit_condition_free(c);
+ }
+
+ while ((p = info->exec)) {
+ LIST_REMOVE(exec, info->exec, p);
+ exec_status_info_free(p);
+ }
+}
+
+static void format_active_state(const char *active_state, const char **active_on, const char **active_off) {
+ if (streq_ptr(active_state, "failed")) {
+ *active_on = ansi_highlight_red();
+ *active_off = ansi_normal();
+ } else if (STRPTR_IN_SET(active_state, "active", "reloading")) {
+ *active_on = ansi_highlight_green();
+ *active_off = ansi_normal();
+ } else
+ *active_on = *active_off = "";
+}
+
+static void print_status_info(
+ sd_bus *bus,
+ UnitStatusInfo *i,
+ bool *ellipsized) {
+
+ char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
+ const char *s1, *s2, *active_on, *active_off, *on, *off, *ss, *fs;
+ _cleanup_free_ char *formatted_path = NULL;
+ ExecStatusInfo *p;
+ usec_t timestamp;
+ const char *path;
+ char **t, **t2;
+ int r;
+
+ assert(i);
+
+ /* This shows pretty information about a unit. See print_property() for a low-level property
+ * printer */
+
+ format_active_state(i->active_state, &active_on, &active_off);
+
+ printf("%s%s%s %s", active_on, special_glyph(SPECIAL_GLYPH_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 (STRPTR_IN_SET(i->load_state, "error", "not-found", "bad-setting")) {
+ on = ansi_highlight_red();
+ off = ansi_normal();
+ } else
+ on = off = "";
+
+ path = i->source_path ?: i->fragment_path;
+ if (path && terminal_urlify_path(path, NULL, &formatted_path) >= 0)
+ path = formatted_path;
+
+ if (!isempty(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)) {
+ bool show_preset = !isempty(i->unit_file_preset) &&
+ show_preset_for_state(unit_file_state_from_string(i->unit_file_state));
+
+ printf(" Loaded: %s%s%s (%s; %s%s%s)\n",
+ on, strna(i->load_state), off,
+ path,
+ i->unit_file_state,
+ show_preset ? "; vendor preset: " : "",
+ show_preset ? i->unit_file_preset : "");
+
+ } 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 (i->transient)
+ printf(" Transient: yes\n");
+
+ if (!strv_isempty(i->dropin_paths)) {
+ _cleanup_free_ char *dir = NULL;
+ bool last = false;
+ char ** dropin;
+
+ STRV_FOREACH(dropin, i->dropin_paths) {
+ _cleanup_free_ char *dropin_formatted = NULL;
+ const char *df;
+
+ if (!dir || last) {
+ printf(dir ? " " :
+ " Drop-In: ");
+
+ dir = mfree(dir);
+
+ dir = dirname_malloc(*dropin);
+ if (!dir) {
+ log_oom();
+ return;
+ }
+
+ printf("%s\n"
+ " %s", dir,
+ special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
+ }
+
+ last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
+
+ if (terminal_urlify_path(*dropin, basename(*dropin), &dropin_formatted) >= 0)
+ df = dropin_formatted;
+ else
+ df = *dropin;
+
+ printf("%s%s", df, 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);
+
+ fs = !isempty(i->freezer_state) && !streq(i->freezer_state, "running") ? i->freezer_state : NULL;
+ if (fs)
+ printf(" %s(%s)%s", ansi_highlight_yellow(), fs, ansi_normal());
+
+ if (!isempty(i->result) && !streq(i->result, "success"))
+ printf(" (Result: %s)", i->result);
+
+ timestamp = STRPTR_IN_SET(i->active_state, "active", "reloading") ? i->active_enter_timestamp :
+ STRPTR_IN_SET(i->active_state, "inactive", "failed") ? i->inactive_enter_timestamp :
+ STRPTR_IN_SET(i->active_state, "activating") ? i->inactive_exit_timestamp :
+ i->active_exit_timestamp;
+
+ s1 = format_timestamp_relative(since1, sizeof(since1), timestamp);
+ s2 = format_timestamp_style(since2, sizeof(since2), timestamp, arg_timestamp_style);
+
+ if (s1)
+ printf(" since %s; %s\n", s2, s1);
+ else if (s2)
+ printf(" since %s\n", s2);
+ else
+ printf("\n");
+
+ STRV_FOREACH(t, i->triggered_by) {
+ UnitActiveState state = _UNIT_ACTIVE_STATE_INVALID;
+
+ (void) get_state_one_unit(bus, *t, &state);
+ format_active_state(unit_active_state_to_string(state), &on, &off);
+
+ printf("%s %s%s%s %s\n",
+ t == i->triggered_by ? "TriggeredBy:" : " ",
+ on, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off,
+ *t);
+ }
+
+ if (endswith(i->id, ".timer")) {
+ char tstamp1[FORMAT_TIMESTAMP_RELATIVE_MAX],
+ tstamp2[FORMAT_TIMESTAMP_MAX];
+ const char *next_rel_time, *next_time;
+ dual_timestamp nw, next = {i->next_elapse_real,
+ i->next_elapse_monotonic};
+ usec_t next_elapse;
+
+ printf(" Trigger: ");
+
+ dual_timestamp_get(&nw);
+ next_elapse = calc_next_elapse(&nw, &next);
+ next_rel_time = format_timestamp_relative(tstamp1, sizeof tstamp1, next_elapse);
+ next_time = format_timestamp_style(tstamp2, sizeof tstamp2, next_elapse, arg_timestamp_style);
+
+ if (next_time && next_rel_time)
+ printf("%s; %s\n", next_time, next_rel_time);
+ else
+ printf("n/a\n");
+ }
+
+ STRV_FOREACH(t, i->triggers) {
+ UnitActiveState state = _UNIT_ACTIVE_STATE_INVALID;
+
+ (void) get_state_one_unit(bus, *t, &state);
+ format_active_state(unit_active_state_to_string(state), &on, &off);
+
+ printf("%s %s%s%s %s\n",
+ t == i->triggers ? " Triggers:" : " ",
+ on, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off,
+ *t);
+ }
+
+ if (!i->condition_result && i->condition_timestamp > 0) {
+ UnitCondition *c;
+ int n = 0;
+
+ s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
+ s2 = format_timestamp_style(since2, sizeof(since2), i->condition_timestamp, arg_timestamp_style);
+
+ printf(" Condition: start %scondition failed%s at %s%s%s\n",
+ ansi_highlight_yellow(), ansi_normal(),
+ s2, s1 ? "; " : "", strempty(s1));
+
+ LIST_FOREACH(conditions, c, i->conditions)
+ if (c->tristate < 0)
+ n++;
+
+ LIST_FOREACH(conditions, c, i->conditions)
+ if (c->tristate < 0)
+ printf(" %s %s=%s%s%s was not met\n",
+ --n ? special_glyph(SPECIAL_GLYPH_TREE_BRANCH) : special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
+ c->name,
+ c->trigger ? "|" : "",
+ c->negate ? "!" : "",
+ c->param);
+ }
+
+ if (!i->assert_result && i->assert_timestamp > 0) {
+ s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
+ s2 = format_timestamp_style(since2, sizeof(since2), i->assert_timestamp, arg_timestamp_style);
+
+ printf(" Assert: start %sassertion failed%s at %s%s%s\n",
+ ansi_highlight_red(), ansi_normal(),
+ s2, s1 ? "; " : "", strempty(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) {
+ _cleanup_free_ char *formatted = NULL;
+ const char *q;
+
+ if (terminal_urlify(*t, NULL, &formatted) >= 0)
+ q = formatted;
+ else
+ q = *t;
+
+ printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", q);
+ }
+
+ 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;", i->n_accepted, i->n_connections);
+ if (i->n_refused)
+ printf(" Refused: %u", i->n_refused);
+ printf("\n");
+ }
+
+ LIST_FOREACH(exec, p, i->exec) {
+ _cleanup_free_ char *argv = NULL;
+ bool good;
+
+ /* Only show exited processes here */
+ if (p->code == 0)
+ continue;
+
+ /* Don't print ExecXYZEx= properties here since it will appear as a
+ * duplicate of the non-Ex= variant. */
+ if (endswith(p->name, "Ex"))
+ continue;
+
+ argv = strv_join(p->argv, " ");
+ printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv));
+
+ good = is_clean_exit(p->code, p->status, EXIT_CLEAN_DAEMON, NULL);
+ if (!good) {
+ on = ansi_highlight_red();
+ off = ansi_normal();
+ } 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_LIBC | 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) {
+
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ _cleanup_free_ char *comm = NULL;
+
+ (void) 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_LIBC | EXIT_STATUS_SYSTEMD);
+ if (c)
+ printf("/%s", c);
+
+ } else
+ printf("signal=%s", signal_to_string(i->exit_status));
+ printf(")");
+ }
+ }
+
+ if (i->control_pid > 0) {
+ _cleanup_free_ char *c = NULL;
+
+ if (i->main_pid > 0)
+ fputs("; Control PID: ", stdout);
+ else
+ fputs("Cntrl PID: ", stdout); /* if first in column, abbreviated so it fits alignment */
+
+ printf(PID_FMT, i->control_pid);
+
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ (void) 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_safe(i->status_errno));
+
+ if (i->ip_ingress_bytes != (uint64_t) -1 && i->ip_egress_bytes != (uint64_t) -1) {
+ char buf_in[FORMAT_BYTES_MAX], buf_out[FORMAT_BYTES_MAX];
+
+ printf(" IP: %s in, %s out\n",
+ format_bytes(buf_in, sizeof(buf_in), i->ip_ingress_bytes),
+ format_bytes(buf_out, sizeof(buf_out), i->ip_egress_bytes));
+ }
+
+ if (i->io_read_bytes != UINT64_MAX && i->io_write_bytes != UINT64_MAX) {
+ char buf_in[FORMAT_BYTES_MAX], buf_out[FORMAT_BYTES_MAX];
+
+ printf(" IO: %s read, %s written\n",
+ format_bytes(buf_in, sizeof(buf_in), i->io_read_bytes),
+ format_bytes(buf_out, sizeof(buf_out), i->io_write_bytes));
+ }
+
+ if (i->tasks_current != (uint64_t) -1) {
+ printf(" Tasks: %" PRIu64, i->tasks_current);
+
+ if (i->tasks_max != (uint64_t) -1)
+ printf(" (limit: %" PRIu64 ")\n", i->tasks_max);
+ else
+ printf("\n");
+ }
+
+ 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_min > 0 || i->memory_low > 0 ||
+ i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
+ i->memory_swap_max != CGROUP_LIMIT_MAX ||
+ i->memory_limit != CGROUP_LIMIT_MAX) {
+ const char *prefix = "";
+
+ printf(" (");
+ if (i->memory_min > 0) {
+ printf("%smin: %s", prefix, format_bytes_cgroup_protection(buf, sizeof(buf), i->memory_min));
+ prefix = " ";
+ }
+ if (i->memory_low > 0) {
+ printf("%slow: %s", prefix, format_bytes_cgroup_protection(buf, sizeof(buf), i->memory_low));
+ prefix = " ";
+ }
+ if (i->memory_high != CGROUP_LIMIT_MAX) {
+ printf("%shigh: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_high));
+ prefix = " ";
+ }
+ if (i->memory_max != CGROUP_LIMIT_MAX) {
+ printf("%smax: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_max));
+ prefix = " ";
+ }
+ if (i->memory_swap_max != CGROUP_LIMIT_MAX) {
+ printf("%sswap max: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_swap_max));
+ prefix = " ";
+ }
+ if (i->memory_limit != CGROUP_LIMIT_MAX) {
+ printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
+ prefix = " ";
+ }
+ printf(")");
+ }
+ printf("\n");
+ }
+
+ if (i->cpu_usage_nsec != (uint64_t) -1) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC));
+ }
+
+ if (i->control_group) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ static const char prefix[] = " ";
+ unsigned c;
+
+ printf(" CGroup: %s\n", i->control_group);
+
+ c = columns();
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
+ else
+ c = 0;
+
+ r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error);
+ if (r == -EBADR) {
+ unsigned k = 0;
+ pid_t extra[2];
+
+ /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
+
+ 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, extra, k, get_output_flags());
+ } else if (r < 0)
+ log_warning_errno(r, "Failed to dump process list for '%s', ignoring: %s",
+ i->id, bus_error_message(&error, r));
+ }
+
+ if (i->id && arg_transport == BUS_TRANSPORT_LOCAL)
+ show_journal_by_unit(
+ stdout,
+ i->id,
+ i->log_namespace,
+ 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 map_main_pid(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ UnitStatusInfo *i = userdata;
+ uint32_t u;
+ int r;
+
+ r = sd_bus_message_read(m, "u", &u);
+ if (r < 0)
+ return r;
+
+ i->main_pid = (pid_t) u;
+ i->running = u > 0;
+
+ return 0;
+}
+
+static int map_load_error(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ const char *message, **p = userdata;
+ int r;
+
+ r = sd_bus_message_read(m, "(ss)", NULL, &message);
+ if (r < 0)
+ return r;
+
+ if (!isempty(message))
+ *p = message;
+
+ return 0;
+}
+
+static int map_listen(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ const char *type, *path;
+ char ***p = userdata;
+ int r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) {
+
+ r = strv_extend(p, type);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(p, path);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int map_conditions(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ UnitStatusInfo *i = userdata;
+ const char *cond, *param;
+ int trigger, negate;
+ int32_t state;
+ int r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
+ _cleanup_(unit_condition_freep) UnitCondition *c = NULL;
+
+ c = new(UnitCondition, 1);
+ if (!c)
+ return -ENOMEM;
+
+ *c = (UnitCondition) {
+ .name = strdup(cond),
+ .param = strdup(param),
+ .trigger = trigger,
+ .negate = negate,
+ .tristate = state,
+ };
+
+ if (!c->name || !c->param)
+ return -ENOMEM;
+
+ LIST_PREPEND(conditions, i->conditions, TAKE_PTR(c));
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int map_asserts(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ UnitStatusInfo *i = userdata;
+ const char *cond, *param;
+ int trigger, negate;
+ int32_t state;
+ int r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
+ 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 r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ _cleanup_free_ ExecStatusInfo *info = NULL;
+ ExecStatusInfo *last;
+ UnitStatusInfo *i = userdata;
+ bool is_ex_prop = endswith(member, "Ex");
+ int r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
+ if (r < 0)
+ return r;
+
+ info = new0(ExecStatusInfo, 1);
+ if (!info)
+ return -ENOMEM;
+
+ LIST_FIND_TAIL(exec, i->exec, last);
+
+ while ((r = exec_status_info_deserialize(m, info, is_ex_prop)) > 0) {
+
+ info->name = strdup(member);
+ if (!info->name)
+ return -ENOMEM;
+
+ LIST_INSERT_AFTER(exec, i->exec, last, info);
+ last = info;
+
+ info = new0(ExecStatusInfo, 1);
+ if (!info)
+ return -ENOMEM;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
+ char bus_type;
+ const char *contents;
+ int r;
+
+ assert(name);
+ assert(m);
+
+ /* This is a low-level property printer, see print_status_info() for the nicer output */
+
+ r = sd_bus_message_peek_type(m, &bus_type, &contents);
+ if (r < 0)
+ return r;
+
+ switch (bus_type) {
+
+ case SD_BUS_TYPE_INT32:
+ if (endswith(name, "ActionExitStatus")) {
+ int32_t i;
+
+ r = sd_bus_message_read_basic(m, bus_type, &i);
+ if (r < 0)
+ return r;
+
+ if (i >= 0 && i <= 255)
+ bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i);
+ else if (all)
+ bus_print_property_value(name, expected_value, value, "[not set]");
+
+ return 1;
+ } else if (streq(name, "NUMAPolicy")) {
+ int32_t i;
+
+ r = sd_bus_message_read_basic(m, bus_type, &i);
+ if (r < 0)
+ return r;
+
+ bus_print_property_valuef(name, expected_value, value, "%s", strna(mpol_to_string(i)));
+
+ return 1;
+ }
+ break;
+
+ case SD_BUS_TYPE_STRUCT:
+
+ if (contents[0] == 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)
+ bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
+ else if (all)
+ bus_print_property_value(name, expected_value, value, "");
+
+ return 1;
+
+ } else if (contents[0] == 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 (all || !isempty(s))
+ bus_print_property_value(name, expected_value, value, s);
+
+ return 1;
+
+ } else if (contents[0] == 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 (!isempty(a) || !isempty(b))
+ bus_print_property_valuef(name, expected_value, value, "%s \"%s\"", strempty(a), strempty(b));
+ else if (all)
+ bus_print_property_value(name, expected_value, value, "");
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies")) {
+ _cleanup_strv_free_ char **l = NULL;
+ int allow_list;
+
+ 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", &allow_list);
+ 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 (all || allow_list || !strv_isempty(l)) {
+ bool first = true;
+ char **i;
+
+ if (!value) {
+ fputs(name, stdout);
+ fputc('=', stdout);
+ }
+
+ if (!allow_list)
+ fputc('~', stdout);
+
+ STRV_FOREACH(i, l) {
+ if (first)
+ first = false;
+ else
+ fputc(' ', stdout);
+
+ fputs(*i, stdout);
+ }
+ fputc('\n', stdout);
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "SELinuxContext", "AppArmorProfile", "SmackProcessLabel")) {
+ int ignore;
+ const char *s;
+
+ r = sd_bus_message_read(m, "(bs)", &ignore, &s);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!isempty(s))
+ bus_print_property_valuef(name, expected_value, value, "%s%s", ignore ? "-" : "", s);
+ else if (all)
+ bus_print_property_value(name, expected_value, value, "");
+
+ return 1;
+
+ } else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) {
+ const int32_t *status, *signal;
+ size_t n_status, n_signal, i;
+
+ r = sd_bus_message_enter_container(m, 'r', "aiai");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_array(m, 'i', (const void **) &status, &n_status);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &n_signal);
+ 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);
+
+ n_status /= sizeof(int32_t);
+ n_signal /= sizeof(int32_t);
+
+ if (all || n_status > 0 || n_signal > 0) {
+ bool first = true;
+
+ if (!value) {
+ fputs(name, stdout);
+ fputc('=', stdout);
+ }
+
+ for (i = 0; i < n_status; i++) {
+ if (first)
+ first = false;
+ else
+ fputc(' ', stdout);
+
+ printf("%"PRIi32, status[i]);
+ }
+
+ for (i = 0; i < n_signal; i++) {
+ const char *str;
+
+ str = signal_to_string((int) signal[i]);
+
+ if (first)
+ first = false;
+ else
+ fputc(' ', stdout);
+
+ if (str)
+ fputs(str, stdout);
+ else
+ printf("%"PRIi32, status[i]);
+ }
+
+ fputc('\n', stdout);
+ }
+ return 1;
+ }
+
+ break;
+
+ case SD_BUS_TYPE_ARRAY:
+
+ if (contents[0] == 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)
+ bus_print_property_valuef(name, expected_value, value, "%s (ignore_errors=%s)", 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 1;
+
+ } else if (contents[0] == 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)
+ bus_print_property_valuef(name, expected_value, value, "%s (%s)", path, type);
+ 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 1;
+
+ } else if (contents[0] == 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)
+ bus_print_property_valuef(name, expected_value, value, "%s (%s)", path, type);
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "TimersMonotonic")) {
+ const char *base;
+ uint64_t v, 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, &v, &next_elapse)) > 0) {
+ char timespan1[FORMAT_TIMESPAN_MAX] = "n/a", timespan2[FORMAT_TIMESPAN_MAX] = "n/a";
+
+ (void) format_timespan(timespan1, sizeof timespan1, v, 0);
+ (void) format_timespan(timespan2, sizeof timespan2, next_elapse, 0);
+
+ bus_print_property_valuef(name, expected_value, value,
+ "{ %s=%s ; next_elapse=%s }", base, timespan1, timespan2);
+ }
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "TimersCalendar")) {
+ const char *base, *spec;
+ uint64_t next_elapse;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sst)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(sst)", &base, &spec, &next_elapse)) > 0) {
+ char timestamp[FORMAT_TIMESTAMP_MAX] = "n/a";
+
+ (void) format_timestamp_style(timestamp, sizeof(timestamp), next_elapse, arg_timestamp_style);
+ bus_print_property_valuef(name, expected_value, value,
+ "{ %s=%s ; next_elapse=%s }", base, spec, timestamp);
+ }
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
+ ExecStatusInfo info = {};
+ bool is_ex_prop = endswith(name, "Ex");
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = exec_status_info_deserialize(m, &info, is_ex_prop)) > 0) {
+ char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
+ _cleanup_strv_free_ char **optv = NULL;
+ _cleanup_free_ char *tt, *o = NULL;
+
+ tt = strv_join(info.argv, " ");
+
+ if (is_ex_prop) {
+ r = exec_command_flags_to_strv(info.flags, &optv);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
+
+ o = strv_join(optv, " ");
+
+ bus_print_property_valuef(name, expected_value, value,
+ "{ path=%s ; argv[]=%s ; flags=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+ strna(info.path),
+ strna(tt),
+ strna(o),
+ strna(format_timestamp_style(timestamp1, sizeof(timestamp1), info.start_timestamp, arg_timestamp_style)),
+ strna(format_timestamp_style(timestamp2, sizeof(timestamp2), info.exit_timestamp, arg_timestamp_style)),
+ 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)));
+ } else
+ bus_print_property_valuef(name, expected_value, value,
+ "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+ strna(info.path),
+ strna(tt),
+ yes_no(info.ignore),
+ strna(format_timestamp_style(timestamp1, sizeof(timestamp1), info.start_timestamp, arg_timestamp_style)),
+ strna(format_timestamp_style(timestamp2, sizeof(timestamp2), info.exit_timestamp, arg_timestamp_style)),
+ 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);
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "DeviceAllow")) {
+ const char *path, *rwm;
+
+ 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)", &path, &rwm)) > 0)
+ bus_print_property_valuef(name, expected_value, value, "%s %s", strna(path), strna(rwm));
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ STR_IN_SET(name, "IODeviceWeight", "BlockIODeviceWeight")) {
+ const char *path;
+ uint64_t weight;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0)
+ bus_print_property_valuef(name, expected_value, value, "%s %"PRIu64, strna(path), weight);
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ (cgroup_io_limit_type_from_string(name) >= 0 ||
+ STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth"))) {
+ const char *path;
+ uint64_t bandwidth;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0)
+ bus_print_property_valuef(name, expected_value, value, "%s %"PRIu64, strna(path), bandwidth);
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN &&
+ streq(name, "IODeviceLatencyTargetUSec")) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ const char *path;
+ uint64_t target;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(st)", &path, &target)) > 0)
+ bus_print_property_valuef(name, expected_value, value, "%s %s", strna(path),
+ format_timespan(ts, sizeof(ts), target, 1));
+ 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 1;
+
+ } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "StandardInputData", "RootHashSignature")) {
+ _cleanup_free_ char *h = NULL;
+ const void *p;
+ size_t sz;
+ ssize_t n;
+
+ r = sd_bus_message_read_array(m, 'y', &p, &sz);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ n = base64mem(p, sz, &h);
+ if (n < 0)
+ return log_oom();
+
+ bus_print_property_value(name, expected_value, value, h);
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "IPAddressAllow", "IPAddressDeny")) {
+ _cleanup_free_ char *addresses = NULL;
+
+ r = sd_bus_message_enter_container(m, 'a', "(iayu)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ _cleanup_free_ char *str = NULL;
+ uint32_t prefixlen;
+ int32_t family;
+ const void *ap;
+ size_t an;
+
+ r = sd_bus_message_enter_container(m, 'r', "iayu");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(m, "i", &family);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_array(m, 'y', &ap, &an);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(m, "u", &prefixlen);
+ 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 (!IN_SET(family, AF_INET, AF_INET6))
+ continue;
+
+ if (an != FAMILY_ADDRESS_SIZE(family))
+ continue;
+
+ if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8)
+ continue;
+
+ if (in_addr_prefix_to_string(family, (union in_addr_union *) ap, prefixlen, &str) < 0)
+ continue;
+
+ if (!strextend_with_separator(&addresses, " ", str, NULL))
+ return log_oom();
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (all || !isempty(addresses))
+ bus_print_property_value(name, expected_value, value, strempty(addresses));
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "BindPaths", "BindReadOnlyPaths")) {
+ _cleanup_free_ char *paths = NULL;
+ const char *source, *dest;
+ int ignore_enoent;
+ uint64_t rbind;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssbt)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ssbt)", &source, &dest, &ignore_enoent, &rbind)) > 0) {
+ _cleanup_free_ char *str = NULL;
+
+ if (isempty(source))
+ continue;
+
+ if (asprintf(&str, "%s%s%s%s%s",
+ ignore_enoent ? "-" : "",
+ source,
+ isempty(dest) ? "" : ":",
+ strempty(dest),
+ rbind == MS_REC ? ":rbind" : "") < 0)
+ return log_oom();
+
+ if (!strextend_with_separator(&paths, " ", str, NULL))
+ return 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);
+
+ if (all || !isempty(paths))
+ bus_print_property_value(name, expected_value, value, strempty(paths));
+
+ return 1;
+
+ } else if (streq(name, "TemporaryFileSystem")) {
+ _cleanup_free_ char *paths = NULL;
+ const char *target, *option;
+
+ 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)", &target, &option)) > 0) {
+ _cleanup_free_ char *str = NULL;
+
+ if (isempty(target))
+ continue;
+
+ if (asprintf(&str, "%s%s%s", target, isempty(option) ? "" : ":", strempty(option)) < 0)
+ return log_oom();
+
+ if (!strextend_with_separator(&paths, " ", str, NULL))
+ return 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);
+
+ if (all || !isempty(paths))
+ bus_print_property_value(name, expected_value, value, strempty(paths));
+
+ return 1;
+
+ } else if (streq(name, "LogExtraFields")) {
+ _cleanup_free_ char *fields = NULL;
+ const void *p;
+ size_t sz;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "ay");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read_array(m, 'y', &p, &sz)) > 0) {
+ _cleanup_free_ char *str = NULL;
+ const char *eq;
+
+ if (memchr(p, 0, sz))
+ continue;
+
+ eq = memchr(p, '=', sz);
+ if (!eq)
+ continue;
+
+ if (!journal_field_valid(p, eq - (const char*) p, false))
+ continue;
+
+ str = malloc(sz + 1);
+ if (!str)
+ return log_oom();
+
+ memcpy(str, p, sz);
+ str[sz] = '\0';
+
+ if (!utf8_is_valid(str))
+ continue;
+
+ if (!strextend_with_separator(&fields, " ", str, NULL))
+ return 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);
+
+ if (all || !isempty(fields))
+ bus_print_property_value(name, expected_value, value, strempty(fields));
+
+ return 1;
+ } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "CPUAffinity", "NUMAMask", "AllowedCPUs", "AllowedMemoryNodes", "EffectiveCPUs", "EffectiveMemoryNodes")) {
+ _cleanup_free_ char *affinity = NULL;
+ _cleanup_(cpu_set_reset) CPUSet set = {};
+ const void *a;
+ size_t n;
+
+ r = sd_bus_message_read_array(m, 'y', &a, &n);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = cpu_set_from_dbus(a, n, &set);
+ if (r < 0)
+ return log_error_errno(r, "Failed to deserialize %s: %m", name);
+
+ affinity = cpu_set_to_range_string(&set);
+ if (!affinity)
+ return log_oom();
+
+ bus_print_property_value(name, expected_value, value, affinity);
+
+ return 1;
+ } else if (streq(name, "MountImages")) {
+ _cleanup_free_ char *paths = NULL;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssba(ss))");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ _cleanup_free_ char *str = NULL;
+ const char *source, *destination, *partition, *mount_options;
+ int ignore_enoent;
+
+ r = sd_bus_message_enter_container(m, 'r', "ssba(ss)");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "ssb", &source, &destination, &ignore_enoent);
+ if (r <= 0)
+ break;
+
+ str = strjoin(ignore_enoent ? "-" : "",
+ source,
+ ":",
+ destination);
+ if (!str)
+ return log_oom();
+
+ r = sd_bus_message_enter_container(m, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "(ss)", &partition, &mount_options)) > 0) {
+ _cleanup_free_ char *previous = NULL;
+
+ previous = TAKE_PTR(str);
+ str = strjoin(strempty(previous), previous ? ":" : "", partition, ":", mount_options);
+ if (!str)
+ return log_oom();
+ }
+ if (r < 0)
+ return r;
+
+ if (!strextend_with_separator(&paths, " ", str, NULL))
+ return log_oom();
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ 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);
+
+ if (all || !isempty(paths))
+ bus_print_property_value(name, expected_value, value, strempty(paths));
+
+ return 1;
+
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+typedef enum SystemctlShowMode{
+ SYSTEMCTL_SHOW_PROPERTIES,
+ SYSTEMCTL_SHOW_STATUS,
+ SYSTEMCTL_SHOW_HELP,
+ _SYSTEMCTL_SHOW_MODE_MAX,
+ _SYSTEMCTL_SHOW_MODE_INVALID = -1,
+} SystemctlShowMode;
+
+static const char* const systemctl_show_mode_table[_SYSTEMCTL_SHOW_MODE_MAX] = {
+ [SYSTEMCTL_SHOW_PROPERTIES] = "show",
+ [SYSTEMCTL_SHOW_STATUS] = "status",
+ [SYSTEMCTL_SHOW_HELP] = "help",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(systemctl_show_mode, SystemctlShowMode);
+
+static int show_one(
+ sd_bus *bus,
+ const char *path,
+ const char *unit,
+ SystemctlShowMode show_mode,
+ bool *new_line,
+ bool *ellipsized) {
+
+ static const struct bus_properties_map property_map[] = {
+ { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
+ { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
+ { "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) },
+ { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
+ {}
+ }, status_map[] = {
+ { "Id", "s", NULL, offsetof(UnitStatusInfo, id) },
+ { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
+ { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
+ { "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) },
+ { "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) },
+ { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
+ { "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) },
+ { "Description", "s", NULL, offsetof(UnitStatusInfo, description) },
+ { "Following", "s", NULL, offsetof(UnitStatusInfo, following) },
+ { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
+ { "FragmentPath", "s", NULL, offsetof(UnitStatusInfo, fragment_path) },
+ { "SourcePath", "s", NULL, offsetof(UnitStatusInfo, source_path) },
+ { "ControlGroup", "s", NULL, offsetof(UnitStatusInfo, control_group) },
+ { "DropInPaths", "as", NULL, offsetof(UnitStatusInfo, dropin_paths) },
+ { "LoadError", "(ss)", map_load_error, offsetof(UnitStatusInfo, load_error) },
+ { "Result", "s", NULL, offsetof(UnitStatusInfo, result) },
+ { "TriggeredBy", "as", NULL, offsetof(UnitStatusInfo, triggered_by) },
+ { "Triggers", "as", NULL, offsetof(UnitStatusInfo, triggers) },
+ { "InactiveExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_exit_timestamp) },
+ { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitStatusInfo, inactive_exit_timestamp_monotonic) },
+ { "ActiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, active_enter_timestamp) },
+ { "ActiveExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, active_exit_timestamp) },
+ { "InactiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_enter_timestamp) },
+ { "NeedDaemonReload", "b", NULL, offsetof(UnitStatusInfo, need_daemon_reload) },
+ { "Transient", "b", NULL, offsetof(UnitStatusInfo, transient) },
+ { "ExecMainPID", "u", NULL, offsetof(UnitStatusInfo, main_pid) },
+ { "MainPID", "u", map_main_pid, 0 },
+ { "ControlPID", "u", NULL, offsetof(UnitStatusInfo, control_pid) },
+ { "StatusText", "s", NULL, offsetof(UnitStatusInfo, status_text) },
+ { "PIDFile", "s", NULL, offsetof(UnitStatusInfo, pid_file) },
+ { "StatusErrno", "i", NULL, offsetof(UnitStatusInfo, status_errno) },
+ { "ExecMainStartTimestamp", "t", NULL, offsetof(UnitStatusInfo, start_timestamp) },
+ { "ExecMainExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, exit_timestamp) },
+ { "ExecMainCode", "i", NULL, offsetof(UnitStatusInfo, exit_code) },
+ { "ExecMainStatus", "i", NULL, offsetof(UnitStatusInfo, exit_status) },
+ { "LogNamespace", "s", NULL, offsetof(UnitStatusInfo, log_namespace) },
+ { "ConditionTimestamp", "t", NULL, offsetof(UnitStatusInfo, condition_timestamp) },
+ { "ConditionResult", "b", NULL, offsetof(UnitStatusInfo, condition_result) },
+ { "Conditions", "a(sbbsi)", map_conditions, 0 },
+ { "AssertTimestamp", "t", NULL, offsetof(UnitStatusInfo, assert_timestamp) },
+ { "AssertResult", "b", NULL, offsetof(UnitStatusInfo, assert_result) },
+ { "Asserts", "a(sbbsi)", map_asserts, 0 },
+ { "NextElapseUSecRealtime", "t", NULL, offsetof(UnitStatusInfo, next_elapse_real) },
+ { "NextElapseUSecMonotonic", "t", NULL, offsetof(UnitStatusInfo, next_elapse_monotonic) },
+ { "NAccepted", "u", NULL, offsetof(UnitStatusInfo, n_accepted) },
+ { "NConnections", "u", NULL, offsetof(UnitStatusInfo, n_connections) },
+ { "NRefused", "u", NULL, offsetof(UnitStatusInfo, n_refused) },
+ { "Accept", "b", NULL, offsetof(UnitStatusInfo, accept) },
+ { "Listen", "a(ss)", map_listen, offsetof(UnitStatusInfo, listen) },
+ { "SysFSPath", "s", NULL, offsetof(UnitStatusInfo, sysfs_path) },
+ { "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
+ { "What", "s", NULL, offsetof(UnitStatusInfo, what) },
+ { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
+ { "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
+ { "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
+ { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
+ { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) },
+ { "MemoryHigh", "t", NULL, offsetof(UnitStatusInfo, memory_high) },
+ { "MemoryMax", "t", NULL, offsetof(UnitStatusInfo, memory_max) },
+ { "MemorySwapMax", "t", NULL, offsetof(UnitStatusInfo, memory_swap_max) },
+ { "MemoryLimit", "t", NULL, offsetof(UnitStatusInfo, memory_limit) },
+ { "CPUUsageNSec", "t", NULL, offsetof(UnitStatusInfo, cpu_usage_nsec) },
+ { "TasksCurrent", "t", NULL, offsetof(UnitStatusInfo, tasks_current) },
+ { "TasksMax", "t", NULL, offsetof(UnitStatusInfo, tasks_max) },
+ { "IPIngressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_ingress_bytes) },
+ { "IPEgressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_egress_bytes) },
+ { "IOReadBytes", "t", NULL, offsetof(UnitStatusInfo, io_read_bytes) },
+ { "IOWriteBytes", "t", NULL, offsetof(UnitStatusInfo, io_write_bytes) },
+ { "ExecCondition", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecConditionEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStartPre", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStartPreEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStart", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStartEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStartPost", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStartPostEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecReload", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecReloadEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStopPre", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStop", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStopPost", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopPostEx", "a(sasasttttuii)", map_exec, 0 },
+ {}
+ };
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_set_free_ Set *found_properties = NULL;
+ _cleanup_(unit_status_info_free) UnitStatusInfo info = {
+ .memory_current = (uint64_t) -1,
+ .memory_high = CGROUP_LIMIT_MAX,
+ .memory_max = CGROUP_LIMIT_MAX,
+ .memory_swap_max = CGROUP_LIMIT_MAX,
+ .memory_limit = (uint64_t) -1,
+ .cpu_usage_nsec = (uint64_t) -1,
+ .tasks_current = (uint64_t) -1,
+ .tasks_max = (uint64_t) -1,
+ .ip_ingress_bytes = (uint64_t) -1,
+ .ip_egress_bytes = (uint64_t) -1,
+ .io_read_bytes = UINT64_MAX,
+ .io_write_bytes = UINT64_MAX,
+ };
+ char **pp;
+ int r;
+
+ assert(path);
+ assert(new_line);
+
+ log_debug("Showing one %s", path);
+
+ r = bus_map_all_properties(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ show_mode == SYSTEMCTL_SHOW_STATUS ? status_map : property_map,
+ BUS_MAP_BOOLEAN_AS_BOOL,
+ &error,
+ &reply,
+ &info);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
+
+ if (unit && streq_ptr(info.load_state, "not-found") && streq_ptr(info.active_state, "inactive")) {
+ log_full(show_mode == SYSTEMCTL_SHOW_STATUS ? LOG_ERR : LOG_DEBUG,
+ "Unit %s could not be found.", unit);
+
+ if (show_mode == SYSTEMCTL_SHOW_STATUS)
+ return EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN;
+ else if (show_mode == SYSTEMCTL_SHOW_HELP)
+ return -ENOENT;
+ }
+
+ if (*new_line)
+ printf("\n");
+
+ *new_line = true;
+
+ if (show_mode == SYSTEMCTL_SHOW_STATUS) {
+ print_status_info(bus, &info, ellipsized);
+
+ if (info.active_state && !STR_IN_SET(info.active_state, "active", "reloading"))
+ return EXIT_PROGRAM_NOT_RUNNING;
+
+ return EXIT_PROGRAM_RUNNING_OR_SERVICE_OK;
+
+ } else if (show_mode == SYSTEMCTL_SHOW_HELP) {
+ show_unit_help(&info);
+ return 0;
+ }
+
+ r = sd_bus_message_rewind(reply, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to rewind: %s", bus_error_message(&error, r));
+
+ r = bus_message_print_all_properties(reply, print_property, arg_properties, arg_value, arg_all, &found_properties);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ STRV_FOREACH(pp, arg_properties)
+ if (!set_contains(found_properties, *pp))
+ log_debug("Property %s does not exist.", *pp);
+
+ return 0;
+}
+
+static int get_unit_dbus_path_by_pid(
+ sd_bus *bus,
+ uint32_t pid,
+ char **unit) {
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ char *u;
+ int r;
+
+ r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "o", &u);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ u = strdup(u);
+ if (!u)
+ return log_oom();
+
+ *unit = u;
+ return 0;
+}
+
+static int show_all(
+ sd_bus *bus,
+ bool *new_line,
+ bool *ellipsized) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ UnitInfo *unit_infos = NULL;
+ const UnitInfo *u;
+ unsigned c;
+ int r, ret = 0;
+
+ r = get_unit_list(bus, NULL, NULL, &unit_infos, 0, &reply);
+ if (r < 0)
+ return r;
+
+ (void) pager_open(arg_pager_flags);
+
+ c = (unsigned) r;
+
+ typesafe_qsort(unit_infos, c, unit_info_compare);
+
+ for (u = unit_infos; u < unit_infos + c; u++) {
+ _cleanup_free_ char *p = NULL;
+
+ p = unit_dbus_path_from_name(u->id);
+ if (!p)
+ return log_oom();
+
+ r = show_one(bus, p, u->id, SYSTEMCTL_SHOW_STATUS, new_line, ellipsized);
+ if (r < 0)
+ return r;
+ else if (r > 0 && ret == 0)
+ ret = r;
+ }
+
+ return ret;
+}
+
+static int show_system_status(sd_bus *bus) {
+ char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(machine_info_clear) struct machine_info mi = {};
+ _cleanup_free_ char *hn = NULL;
+ const char *on, *off;
+ int r;
+
+ hn = gethostname_malloc();
+ if (!hn)
+ return log_oom();
+
+ r = bus_map_all_properties(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ machine_info_property_map,
+ BUS_MAP_STRDUP,
+ &error,
+ NULL,
+ &mi);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read server status: %s", bus_error_message(&error, r));
+
+ if (streq_ptr(mi.state, "degraded")) {
+ on = ansi_highlight_red();
+ off = ansi_normal();
+ } else if (streq_ptr(mi.state, "running")) {
+ on = ansi_highlight_green();
+ off = ansi_normal();
+ } else {
+ on = ansi_highlight_yellow();
+ off = ansi_normal();
+ }
+
+ printf("%s%s%s %s\n", on, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE), off, arg_host ? arg_host : hn);
+
+ printf(" State: %s%s%s\n",
+ on, strna(mi.state), off);
+
+ printf(" Jobs: %" PRIu32 " queued\n", mi.n_jobs);
+ printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units);
+
+ printf(" Since: %s; %s\n",
+ format_timestamp_style(since2, sizeof(since2), mi.timestamp, arg_timestamp_style),
+ format_timestamp_relative(since1, sizeof(since1), mi.timestamp));
+
+ printf(" CGroup: %s\n", mi.control_group ?: "/");
+ if (IN_SET(arg_transport,
+ BUS_TRANSPORT_LOCAL,
+ BUS_TRANSPORT_MACHINE)) {
+ static const char prefix[] = " ";
+ unsigned c;
+
+ c = columns();
+ if (c > sizeof(prefix) - 1)
+ c -= sizeof(prefix) - 1;
+ else
+ c = 0;
+
+ show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags());
+ }
+
+ return 0;
+}
+
+int show(int argc, char *argv[], void *userdata) {
+ bool new_line = false, ellipsized = false;
+ SystemctlShowMode show_mode;
+ int r, ret = 0;
+ sd_bus *bus;
+
+ assert(argv);
+
+ show_mode = systemctl_show_mode_from_string(argv[0]);
+ if (show_mode < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid argument.");
+
+ if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "'help' command expects one or more unit names.\n"
+ "(Alternatively, help for systemctl itself may be shown with --help)");
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ (void) pager_open(arg_pager_flags);
+
+ /* If no argument is specified inspect the manager itself */
+ if (show_mode == SYSTEMCTL_SHOW_PROPERTIES && argc <= 1)
+ return show_one(bus, "/org/freedesktop/systemd1", NULL, show_mode, &new_line, &ellipsized);
+
+ if (show_mode == SYSTEMCTL_SHOW_STATUS && argc <= 1) {
+
+ show_system_status(bus);
+ new_line = true;
+
+ if (arg_all)
+ ret = show_all(bus, &new_line, &ellipsized);
+ } else {
+ _cleanup_free_ char **patterns = NULL;
+ char **name;
+
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ _cleanup_free_ char *path = NULL, *unit = NULL;
+ uint32_t id;
+
+ if (safe_atou32(*name, &id) < 0) {
+ if (strv_push(&patterns, *name) < 0)
+ return log_oom();
+
+ continue;
+ } else if (show_mode == SYSTEMCTL_SHOW_PROPERTIES) {
+ /* Interpret as job id */
+ if (asprintf(&path, "/org/freedesktop/systemd1/job/%u", id) < 0)
+ return log_oom();
+
+ } else {
+ /* Interpret as PID */
+ r = get_unit_dbus_path_by_pid(bus, id, &path);
+ if (r < 0) {
+ ret = r;
+ continue;
+ }
+
+ r = unit_name_from_dbus_path(path, &unit);
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = show_one(bus, path, unit, show_mode, &new_line, &ellipsized);
+ if (r < 0)
+ return r;
+ else if (r > 0 && ret == 0)
+ ret = r;
+ }
+
+ if (!strv_isempty(patterns)) {
+ _cleanup_strv_free_ char **names = NULL;
+
+ r = expand_unit_names(bus, patterns, NULL, &names, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
+
+ r = maybe_extend_with_unit_dependencies(bus, &names);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(name, names) {
+ _cleanup_free_ char *path;
+
+ path = unit_dbus_path_from_name(*name);
+ if (!path)
+ return log_oom();
+
+ r = show_one(bus, path, *name, show_mode, &new_line, &ellipsized);
+ if (r < 0)
+ return r;
+ if (r > 0 && ret == 0)
+ ret = r;
+ }
+ }
+ }
+
+ if (ellipsized && !arg_quiet)
+ printf("Hint: Some lines were ellipsized, use -l to show in full.\n");
+
+ return ret;
+}