Systemd/src/systemctl/systemctl-show.c
Lennart Poettering daf71ef61c systemctl: split up humungous systemctl.c file
This is just some refactoring: shifting around of code, not change in
codeflow.

This splits up the way too huge systemctl.c in multiple more easily
digestable files. It roughly follows the rule that each family of verbs
gets its own .c/.h file pair, and so do all the compat executable names
we support. Plus three extra files for sysv compat (which existed before
already, but I renamed slightly, to get the systemctl- prefix lik
everything else), a -util file with generic stuff everything uses, and a
-logind file with everything that talks directly to logind instead of
PID1.

systemctl is still a bit too complex for my taste, but I think this way
itc omes in a more digestable bits at least.

No change of behaviour, just reshuffling of some code.
2020-10-07 23:12:15 +02:00

2136 lines
92 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
#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-util.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;
}