#include "bus-unit-util.h"
#include "bus-util.h"
#include "bus-wait-for-jobs.h"
+#include "bus-wait-for-units.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
#include "copy.h"
#include "cpu-set-util.h"
+#include "dirent-util.h"
#include "dropin.h"
#include "efivars.h"
#include "env-util.h"
#include "escape.h"
+#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
#include "format-util.h"
static bool arg_now = false;
static bool arg_jobs_before = false;
static bool arg_jobs_after = false;
+static char **arg_clean_what = NULL;
+
+/* This is a global cache that will be constructed on first use. */
+static Hashmap *cached_id_map = NULL;
+static Hashmap *cached_name_map = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(cached_id_map, hashmap_freep);
+STATIC_DESTRUCTOR_REGISTER(cached_name_map, hashmap_freep);
static int daemon_reload(int argc, char *argv[], void* userdata);
static int trivial_method(int argc, char *argv[], void *userdata);
off = ansi_normal();
}
- if (arg_all)
+ if (arg_all || strv_contains(arg_states, "inactive"))
printf("%s%u loaded units listed.%s\n"
"To show all installed unit files use 'systemctl list-unit-files'.\n",
on, n_shown, off);
- else
+ else if (!arg_states)
printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
"To show all installed unit files use 'systemctl list-unit-files'.\n",
on, n_shown, off);
+ else
+ printf("%u loaded units listed.\n", n_shown);
}
return 0;
assert(nw);
assert(next);
- if (next->monotonic != USEC_INFINITY && next->monotonic > 0) {
+ if (timestamp_is_set(next->monotonic)) {
usec_t converted;
if (next->monotonic > nw->monotonic)
else
converted = nw->realtime - (nw->monotonic - next->monotonic);
- if (next->realtime != USEC_INFINITY && next->realtime > 0)
+ if (timestamp_is_set(next->realtime))
next_elapse = MIN(converted, next->realtime);
else
next_elapse = converted;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_strv_free_ char **ret = NULL;
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *dbus_path = NULL;
int i, r;
assert(bus);
assert(name);
assert(deps);
- path = unit_dbus_path_from_name(name);
- if (!path)
+ dbus_path = unit_dbus_path_from_name(name);
+ if (!dbus_path)
return log_oom();
r = bus_map_all_properties(bus,
"org.freedesktop.systemd1",
- path,
+ dbus_path,
map[arg_dependency],
BUS_MAP_STRDUP,
&error,
statelen = STRLEN("STATE"),
failedlen = STRLEN("FAILED"),
jobslen = STRLEN("JOBS");
+ bool state_missing = false;
assert(machine_infos || n == 0);
failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
- if (!arg_plain && !streq_ptr(m->state, "running"))
+ if (!arg_plain && m->state && !streq(m->state, "running"))
circle_len = 2;
}
if (circle_len > 0)
printf("%s%s%s ", on_state, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_state);
+ if (!m->state)
+ state_missing = true;
+
if (m->is_host)
printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
- (int) (namelen - (STRLEN(" (host)"))),
+ (int) (namelen - strlen(" (host)")),
strna(m->name),
on_state, statelen, strna(m->state), off_state,
on_failed, failedlen, m->n_failed_units, off_failed,
jobslen, m->n_jobs);
}
- if (!arg_no_legend)
- printf("\n%u machines listed.\n", n);
+ if (!arg_no_legend) {
+ printf("\n");
+ if (state_missing && geteuid() != 0)
+ printf("Notice: some information only available to privileged users was not shown.\n");
+ printf("%u machines listed.\n", n);
+ }
}
static int list_machines(int argc, char *argv[], void *userdata) {
int r;
/**
- * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is found, and
- * sets:
+ * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is
+ * found, and sets:
* - the path to the unit in *ret_frament_path, if it exists on disk,
- * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins were found.
+ * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins
+ * were found.
*
- * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for some
- * reason (the latter only applies if we are going through the service manager)
+ * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for
+ * some reason (the latter only applies if we are going through the service manager).
*/
assert(unit_name);
!install_client_side() &&
!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *load_state = NULL, *unit = NULL;
+ _cleanup_free_ char *load_state = NULL, *dbus_path = NULL;
- unit = unit_dbus_path_from_name(unit_name);
- if (!unit)
+ dbus_path = unit_dbus_path_from_name(unit_name);
+ if (!dbus_path)
return log_oom();
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
- unit,
+ dbus_path,
"org.freedesktop.systemd1.Unit",
"LoadState",
&error,
r = 0;
goto not_found;
}
- if (!streq(load_state, "loaded"))
+ if (!STR_IN_SET(load_state, "loaded", "bad-setting"))
return -EKEYREJECTED;
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
- unit,
+ dbus_path,
"org.freedesktop.systemd1.Unit",
"FragmentPath",
&error,
r = sd_bus_get_property_strv(
bus,
"org.freedesktop.systemd1",
- unit,
+ dbus_path,
"org.freedesktop.systemd1.Unit",
"DropInPaths",
&error,
return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r));
}
} else {
- _cleanup_set_free_ Set *names = NULL;
- _cleanup_free_ char *template = NULL;
+ const char *_path;
+ _cleanup_set_free_free_ Set *names = NULL;
- names = set_new(NULL);
- if (!names)
- return log_oom();
+ if (!cached_name_map) {
+ r = unit_file_build_name_map(lp, &cached_id_map, &cached_name_map, NULL);
+ if (r < 0)
+ return r;
+ }
- r = unit_find_template_path(unit_name, lp, &path, &template);
+ r = unit_file_find_fragment(cached_id_map, cached_name_map, unit_name, &_path, &names);
if (r < 0)
return r;
- if (r > 0) {
- if (null_or_empty_path(path))
- /* The template is masked. Let's cut the process short. */
- return -ERFKILL;
-
- /* We found the unit file. If we followed symlinks, this name might be
- * different then the unit_name with started with. Look for dropins matching
- * that "final" name. */
- r = set_put(names, basename(path));
- } else if (!template)
- /* No unit file, let's look for dropins matching the original name.
- * systemd has fairly complicated rules (based on unit type and provenience),
- * which units are allowed not to have the main unit file. We err on the
- * side of including too many files, and always try to load dropins. */
- r = set_put(names, unit_name);
- else
- /* The cases where we allow a unit to exist without the main file are
- * never valid for templates. Don't try to load dropins in this case. */
- goto not_found;
- if (r < 0)
- return log_error_errno(r, "Failed to add unit name: %m");
+ if (_path) {
+ path = strdup(_path);
+ if (!path)
+ return log_oom();
+ }
if (ret_dropin_paths) {
- r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path, NULL, names, &dropins);
+ r = unit_file_find_dropin_paths(arg_root, lp->search_path, NULL,
+ ".d", ".conf",
+ names, &dropins);
if (r < 0)
return r;
}
}
+ if (isempty(path)) {
+ *ret_fragment_path = NULL;
r = 0;
-
- if (!isempty(path)) {
+ } else {
*ret_fragment_path = TAKE_PTR(path);
r = 1;
- } else
- *ret_fragment_path = NULL;
+ }
if (ret_dropin_paths) {
if (!strv_isempty(dropins)) {
static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *buf = NULL, *path = NULL;
+ _cleanup_free_ char *buf = NULL, *dbus_path = NULL;
UnitActiveState state;
int r;
assert(name);
assert(active_state);
- path = unit_dbus_path_from_name(name);
- if (!path)
+ dbus_path = unit_dbus_path_from_name(name);
+ if (!dbus_path)
return log_oom();
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
- path,
+ dbus_path,
"org.freedesktop.systemd1.Unit",
"ActiveState",
&error,
static int check_triggering_units(sd_bus *bus, const char *name) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *n = NULL, *path = NULL, *load_state = NULL;
+ _cleanup_free_ char *n = NULL, *dbus_path = NULL, *load_state = NULL;
_cleanup_strv_free_ char **triggered_by = NULL;
bool print_warning_label = true;
UnitActiveState active_state;
if (streq(load_state, "masked"))
return 0;
- path = unit_dbus_path_from_name(n);
- if (!path)
+ dbus_path = unit_dbus_path_from_name(n);
+ if (!dbus_path)
return log_oom();
r = sd_bus_get_property_strv(
bus,
"org.freedesktop.systemd1",
- path,
+ dbus_path,
"org.freedesktop.systemd1.Unit",
"TriggeredBy",
&error,
return "start";
}
-typedef struct {
- sd_bus_slot *match;
- sd_event *event;
- Set *unit_paths;
- bool any_failed;
-} WaitContext;
-
-static void wait_context_free(WaitContext *c) {
- c->match = sd_bus_slot_unref(c->match);
- c->event = sd_event_unref(c->event);
- c->unit_paths = set_free_free(c->unit_paths);
-}
-
-static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char *path, *interface, *active_state = NULL, *job_path = NULL;
- WaitContext *c = userdata;
- bool is_failed;
- int r;
-
- /* Called whenever we get a PropertiesChanged signal. Checks if ActiveState changed to inactive/failed.
- *
- * Signal parameters: (s interface, a{sv} changed_properties, as invalidated_properties) */
-
- path = sd_bus_message_get_path(m);
- if (!set_contains(c->unit_paths, path))
- return 0;
-
- r = sd_bus_message_read(m, "s", &interface);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!streq(interface, "org.freedesktop.systemd1.Unit")) /* ActiveState is on the Unit interface */
- return 0;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return bus_log_parse_error(r);
-
- for (;;) {
- const char *s;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
- if (r < 0)
- return bus_log_parse_error(r);
- if (r == 0) /* end of array */
- break;
-
- r = sd_bus_message_read(m, "s", &s); /* Property name */
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (streq(s, "ActiveState")) {
- r = sd_bus_message_read(m, "v", "s", &active_state);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (job_path) /* Found everything we need */
- break;
-
- } else if (streq(s, "Job")) {
- uint32_t job_id;
-
- r = sd_bus_message_read(m, "v", "(uo)", &job_id, &job_path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- /* There's still a job pending for this unit, let's ignore this for now, and return right-away. */
- if (job_id != 0)
- return 0;
-
- if (active_state) /* Found everything we need */
- break;
-
- } else {
- r = sd_bus_message_skip(m, "v"); /* Other property */
- 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 this didn't contain the ActiveState property we can't do anything */
- if (!active_state)
- return 0;
-
- is_failed = streq(active_state, "failed");
- if (streq(active_state, "inactive") || is_failed) {
- log_debug("%s became %s, dropping from --wait tracking", path, active_state);
- free(set_remove(c->unit_paths, path));
- c->any_failed = c->any_failed || is_failed;
- } else
- log_debug("ActiveState on %s changed to %s", path, active_state);
-
- if (set_isempty(c->unit_paths))
- sd_event_exit(c->event, EXIT_SUCCESS);
-
- return 0;
-}
-
-static int wait_context_watch(
- WaitContext *wait_context,
- sd_bus *bus,
- const char *name) {
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *unit_path = NULL;
- int r;
-
- assert(wait_context);
- assert(name);
-
- log_debug("Watching for property changes of %s", name);
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "RefUnit",
- &error,
- NULL,
- "s", name);
- if (r < 0)
- return log_error_errno(r, "Failed to add reference to unit %s: %s", name, bus_error_message(&error, r));
-
- unit_path = unit_dbus_path_from_name(name);
- if (!unit_path)
- return log_oom();
-
- r = set_ensure_allocated(&wait_context->unit_paths, &string_hash_ops);
- if (r < 0)
- return log_oom();
-
- r = set_put_strdup(wait_context->unit_paths, unit_path);
- if (r < 0)
- return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path);
-
- r = sd_bus_match_signal_async(bus,
- &wait_context->match,
- NULL,
- unit_path,
- "org.freedesktop.DBus.Properties",
- "PropertiesChanged",
- on_properties_changed, NULL, wait_context);
- if (r < 0)
- return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
-
- return 0;
-}
-
static int start_unit_one(
sd_bus *bus,
const char *method, /* When using classic per-job bus methods */
const char *mode,
sd_bus_error *error,
BusWaitForJobs *w,
- WaitContext *wait_context) {
+ BusWaitForUnits *wu) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *path;
assert(mode);
assert(error);
- if (wait_context) {
- r = wait_context_watch(wait_context, bus, name);
- if (r < 0)
- return r;
- }
-
log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)",
arg_dry_run ? "Would execute" : "Executing",
method, name, mode);
return log_error_errno(r, "Failed to watch job for %s: %m", name);
}
+ if (wu) {
+ r = bus_wait_for_units_add_unit(wu, name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch unit %s: %m", name);
+ }
+
return 0;
fail:
static const char** make_extra_args(const char *extra_args[static 4]) {
size_t n = 0;
+ assert(extra_args);
+
if (arg_scope != UNIT_FILE_SYSTEM)
extra_args[n++] = "--user";
}
static int start_unit(int argc, char *argv[], void *userdata) {
+ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *wu = NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
- _cleanup_(wait_context_free) WaitContext wait_context = {};
const char *method, *job_type, *mode, *one_name, *suffix = NULL;
_cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
_cleanup_strv_free_ char **names = NULL;
if (r < 0)
return log_error_errno(r, "Failed to enable subscription: %m");
- r = sd_event_default(&wait_context.event);
+ r = bus_wait_for_units_new(bus, &wu);
if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
-
- r = sd_bus_attach_event(bus, wait_context.event, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to attach bus to event loop: %m");
+ return log_error_errno(r, "Failed to allocate unit watch context: %m");
}
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- r = start_unit_one(bus, method, job_type, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
+ r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
if (ret == EXIT_SUCCESS && r < 0)
ret = translate_bus_error_to_exit_status(r, &error);
(void) check_triggering_units(bus, *name);
}
- if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) {
- r = sd_event_loop(wait_context.event);
+ if (arg_wait) {
+ r = bus_wait_for_units_run(wu);
if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
- if (wait_context.any_failed)
+ return log_error_errno(r, "Failed to wait for units: %m");
+ if (r == BUS_WAIT_FAILURE && ret == EXIT_SUCCESS)
ret = EXIT_FAILURE;
}
return r;
}
+static int clean_unit(int argc, char *argv[], void *userdata) {
+ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ int r, ret = EXIT_SUCCESS;
+ char **name;
+ sd_bus *bus;
+
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
+
+ polkit_agent_open_maybe();
+
+ if (!arg_clean_what) {
+ arg_clean_what = strv_new("cache", "runtime");
+ if (!arg_clean_what)
+ return log_oom();
+ }
+
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
+
+ if (!arg_no_block) {
+ r = bus_wait_for_units_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate unit waiter: %m");
+ }
+
+ STRV_FOREACH(name, names) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ if (w) {
+ /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "RefUnit",
+ &error,
+ NULL,
+ "s", *name);
+ if (r < 0) {
+ log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r));
+ if (ret == EXIT_SUCCESS)
+ ret = r;
+ continue;
+ }
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "CleanUnit");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *name);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_clean_what);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r));
+ if (ret == EXIT_SUCCESS) {
+ ret = r;
+ continue;
+ }
+ }
+
+ if (w) {
+ r = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_REFFED|BUS_WAIT_FOR_MAINTENANCE_END, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch unit %s: %m", *name);
+ }
+ }
+
+ r = bus_wait_for_units_run(w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for units: %m");
+ if (r == BUS_WAIT_FAILURE)
+ ret = EXIT_FAILURE;
+
+ return ret;
+}
+
typedef struct ExecStatusInfo {
char *name;
int code;
int status;
+ ExecCommandFlags flags;
+
LIST_FIELDS(struct ExecStatusInfo, exec);
} ExecStatusInfo;
free(i);
}
-static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *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;
assert(m);
assert(i);
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii");
+ 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)
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,
- "bttttuii",
- &ignore,
+ "ttttuii",
&start_timestamp, &start_timestamp_monotonic,
&exit_timestamp, &exit_timestamp_monotonic,
&pid,
if (r < 0)
return bus_log_parse_error(r);
- i->ignore = ignore;
+ 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;
if (i->status_text)
printf(" Status: \"%s\"\n", i->status_text);
if (i->status_errno > 0)
- printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno));
+ 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];
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));
+ 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)
if (!c->name || !c->param)
return -ENOMEM;
-
LIST_PREPEND(conditions, i->conditions, TAKE_PTR(c));
}
if (r < 0)
_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, "(sasbttttuii)");
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
if (r < 0)
return r;
LIST_FIND_TAIL(exec, i->exec, last);
- while ((r = exec_status_info_deserialize(m, info)) > 0) {
+ while ((r = exec_status_info_deserialize(m, info, is_ex_prop)) > 0) {
info->name = strdup(member);
if (!info->name)
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;
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(stt)", &base, &v, &next_elapse)) > 0) {
- char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
+ 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,
- format_timespan(timespan1, sizeof(timespan1), v, 0),
- 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);
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(sst)", &base, &spec, &next_elapse)) > 0) {
- char timestamp[FORMAT_TIMESTAMP_MAX];
+ char timestamp[FORMAT_TIMESTAMP_MAX] = "n/a";
- bus_print_property_valuef(name, expected_value, value, "{ %s=%s ; next_elapse=%s }", base, spec,
- format_timestamp(timestamp, sizeof(timestamp), next_elapse));
+ (void) format_timestamp(timestamp, sizeof(timestamp), next_elapse);
+ 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);
} 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, "(sasbttttuii)");
+ 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)) > 0) {
+ while ((r = exec_status_info_deserialize(m, &info, is_ex_prop)) > 0) {
char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
- _cleanup_free_ char *tt;
+ _cleanup_strv_free_ char **optv = NULL;
+ _cleanup_free_ char *tt, *o = NULL;
tt = strv_join(info.argv, " ");
- 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(timestamp1, sizeof(timestamp1), info.start_timestamp)),
- strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
- info.pid,
- sigchld_code_to_string(info.code),
- info.status,
- info.code == CLD_EXITED ? "" : "/",
- strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+ 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(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ info.pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+ } 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(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ info.pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
free(info.path);
strv_free(info.argv);
bus_print_property_value(name, expected_value, value, strempty(fields));
return 1;
- } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "CPUAffinity")) {
+ } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "CPUAffinity", "NUMAMask")) {
_cleanup_free_ char *affinity = NULL;
_cleanup_(cpu_set_reset) CPUSet set = {};
const void *a;
r = cpu_set_from_dbus(a, n, &set);
if (r < 0)
- return log_error_errno(r, "Failed to deserialize CPUAffinity: %m");
+ return log_error_errno(r, "Failed to deserialize %s: %m", name);
affinity = cpu_set_to_range_string(&set);
if (!affinity)
bool *ellipsized) {
static const struct bus_properties_map property_map[] = {
- { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
- { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
- { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
+ { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
+ { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_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) },
- { "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) },
- { "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) },
- { "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) },
- { "ExecStartPre", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStart", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStartPost", "a(sasbttttuii)", map_exec, 0 },
- { "ExecReload", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStopPre", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStop", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStopPost", "a(sasbttttuii)", map_exec, 0 },
+ { "Id", "s", NULL, offsetof(UnitStatusInfo, id) },
+ { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
+ { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_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) },
+ { "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) },
+ { "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) },
+ { "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 },
+ { "ExecStopPre", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStop", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopPost", "a(sasbttttuii)", map_exec, 0 },
{}
};
bool first = true;
int r;
+ /* Include all units by default - i.e. continue as if the --all
+ * option was used */
+ if (strv_isempty(arg_states))
+ arg_all = true;
+
if (arg_transport != BUS_TRANSPORT_LOCAL)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot remotely cat units.");
if (init) {
const char *root_systemd_path = NULL, *root_init_path = NULL;
- root_systemd_path = strjoina(root, "/" SYSTEMD_BINARY_PATH);
- root_init_path = strjoina(root, "/", init);
+ root_systemd_path = prefix_roota(root, "/" SYSTEMD_BINARY_PATH);
+ root_init_path = prefix_roota(root, init);
/* If the passed init is actually the same as the
* systemd binary, then let's suppress it. */
}
if (!isempty(arg_root)) {
- q = strappend("--root=", arg_root);
+ q = strjoin("--root=", arg_root);
if (!q)
return log_oom();
assert(name);
assert(ret_path);
- path = strjoin(paths->persistent_config, "/", name);
+ path = path_join(paths->persistent_config, name);
if (!path)
return log_oom();
if (arg_runtime) {
- run = strjoin(paths->runtime_config, "/", name);
+ run = path_join(paths->runtime_config, name);
if (!run)
return log_oom();
}
" When shutting down or sleeping, ignore inhibitors\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
+ " --what=RESOURCES Which types of resources to remove\n"
" --now Start or stop unit in addition to enabling or disabling it\n"
" --dry-run Only print what would be done\n"
" -q --quiet Suppress output\n"
" if supported, otherwise restart\n"
" isolate UNIT Start one unit and stop all others\n"
" kill UNIT... Send signal to processes of a unit\n"
+ " clean UNIT... Clean runtime, cache, state, logs or\n"
+ " or configuration of unit\n"
" is-active PATTERN... Check whether units are active\n"
" is-failed PATTERN... Check whether units are failed\n"
" status [PATTERN...|PID...] Show runtime status of one or more units\n"
ARG_NOW,
ARG_MESSAGE,
ARG_WAIT,
+ ARG_WHAT,
};
static const struct option options[] = {
{ "now", no_argument, NULL, ARG_NOW },
{ "message", required_argument, NULL, ARG_MESSAGE },
{ "show-transaction", no_argument, NULL, 'T' },
+ { "what", required_argument, NULL, ARG_WHAT },
{}
};
arg_show_transaction = true;
break;
+ case ARG_WHAT: {
+ const char *p;
+
+ if (isempty(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--what= requires arguments.");
+
+ for (p = optarg;;) {
+ _cleanup_free_ char *k = NULL;
+
+ r = extract_first_word(&p, &k, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse directory type: %s", optarg);
+ if (r == 0)
+ break;
+
+ if (streq(k, "help")) {
+ puts("runtime\n"
+ "state\n"
+ "cache\n"
+ "logs\n"
+ "configuration");
+ return 0;
+ }
+
+ r = strv_consume(&arg_clean_what, TAKE_PTR(k));
+ if (r < 0)
+ return log_oom();
+ }
+
+ break;
+ }
+
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);
{ "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_sockets },
{ "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_timers },
{ "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_jobs },
- { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY|VERB_MUST_BE_ROOT, list_machines },
+ { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_machines },
{ "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, trivial_method },
{ "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, cancel_job },
{ "start", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */
{ "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit },
{ "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit },
+ { "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_unit },
{ "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active },
{ "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */
{ "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed },