#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
-#include <locale.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
-#include <string.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/reboot.h>
-#include <sys/socket.h>
#include <unistd.h>
#include "sd-bus.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 "terminal-util.h"
#include "tmpfile-util.h"
#include "unit-def.h"
+#include "unit-file.h"
#include "unit-name.h"
#include "user-util.h"
#include "utf8.h"
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);
return c;
}
+static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) {
+ _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
+ char **name;
+ int r, i;
+
+ assert(bus);
+ assert(ret);
+
+ STRV_FOREACH(name, names) {
+ char *t;
+ UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN);
+
+ r = unit_name_mangle_with_suffix(*name, NULL, options, suffix ?: ".service", &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle name: %m");
+
+ if (string_is_glob(t))
+ r = strv_consume(&globs, t);
+ else
+ r = strv_consume(&mangled, t);
+ if (r < 0)
+ return log_oom();
+ }
+
+ /* Query the manager only if any of the names are a glob, since
+ * this is fairly expensive */
+ bool expanded = !strv_isempty(globs);
+ if (expanded) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ UnitInfo *unit_infos = NULL;
+ size_t allocated, n;
+
+ r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
+ if (r < 0)
+ return r;
+
+ n = strv_length(mangled);
+ allocated = n + 1;
+
+ for (i = 0; i < r; i++) {
+ if (!GREEDY_REALLOC(mangled, allocated, n+2))
+ return log_oom();
+
+ mangled[n] = strdup(unit_infos[i].id);
+ if (!mangled[n])
+ return log_oom();
+
+ mangled[++n] = NULL;
+ }
+ }
+
+ if (ret_expanded)
+ *ret_expanded = expanded;
+
+ *ret = TAKE_PTR(mangled);
+ return 0;
+}
+
static int list_units(int argc, char *argv[], void *userdata) {
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_(message_set_freep) Set *replies = NULL;
static int list_sockets(int argc, char *argv[], void *userdata) {
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
+ _cleanup_strv_free_ char **sockets_with_suffix = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_free_ struct socket_info *socket_infos = NULL;
const UnitInfo *u;
(void) pager_open(arg_pager_flags);
- n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
- if (n < 0)
- return n;
+ r = expand_names(bus, strv_skip(argv, 1), ".socket", &sockets_with_suffix, NULL);
+ if (r < 0)
+ return r;
+
+ if (argc == 1 || sockets_with_suffix) {
+ n = get_unit_list_recursive(bus, sockets_with_suffix, &unit_infos, &replies, &machines);
+ if (n < 0)
+ return n;
- for (u = unit_infos; u < unit_infos + n; u++) {
- _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
- int i, c;
+ for (u = unit_infos; u < unit_infos + n; u++) {
+ _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
+ int i, c;
- if (!endswith(u->id, ".socket"))
- continue;
+ if (!endswith(u->id, ".socket"))
+ continue;
- r = get_triggered_units(bus, u->unit_path, &triggered);
- if (r < 0)
- goto cleanup;
+ r = get_triggered_units(bus, u->unit_path, &triggered);
+ if (r < 0)
+ goto cleanup;
- c = get_listening(bus, u->unit_path, &listening);
- if (c < 0) {
- r = c;
- goto cleanup;
- }
+ c = get_listening(bus, u->unit_path, &listening);
+ if (c < 0) {
+ r = c;
+ goto cleanup;
+ }
- if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
- r = log_oom();
- goto cleanup;
- }
+ if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
+ r = log_oom();
+ goto cleanup;
+ }
- for (i = 0; i < c; i++)
- socket_infos[cs + i] = (struct socket_info) {
- .machine = u->machine,
- .id = u->id,
- .type = listening[i*2],
- .path = listening[i*2 + 1],
- .triggered = triggered,
- .own_triggered = i==0,
- };
+ for (i = 0; i < c; i++)
+ socket_infos[cs + i] = (struct socket_info) {
+ .machine = u->machine,
+ .id = u->id,
+ .type = listening[i*2],
+ .path = listening[i*2 + 1],
+ .triggered = triggered,
+ .own_triggered = i==0,
+ };
+
+ /* from this point on we will cleanup those socket_infos */
+ cs += c;
+ free(listening);
+ listening = triggered = NULL; /* avoid cleanup */
+ }
- /* from this point on we will cleanup those socket_infos */
- cs += c;
- free(listening);
- listening = triggered = NULL; /* avoid cleanup */
+ typesafe_qsort(socket_infos, cs, socket_info_compare);
}
- typesafe_qsort(socket_infos, cs, socket_info_compare);
-
output_sockets_list(socket_infos, cs);
cleanup:
static int list_timers(int argc, char *argv[], void *userdata) {
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
+ _cleanup_strv_free_ char **timers_with_suffix = NULL;
_cleanup_free_ struct timer_info *timer_infos = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
struct timer_info *t;
(void) pager_open(arg_pager_flags);
- n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
- if (n < 0)
- return n;
+ r = expand_names(bus, strv_skip(argv, 1), ".timer", &timers_with_suffix, NULL);
+ if (r < 0)
+ return r;
- dual_timestamp_get(&nw);
+ if (argc == 1 || timers_with_suffix) {
+ n = get_unit_list_recursive(bus, timers_with_suffix, &unit_infos, &replies, &machines);
+ if (n < 0)
+ return n;
- for (u = unit_infos; u < unit_infos + n; u++) {
- _cleanup_strv_free_ char **triggered = NULL;
- dual_timestamp next = DUAL_TIMESTAMP_NULL;
- usec_t m, last = 0;
+ dual_timestamp_get(&nw);
- if (!endswith(u->id, ".timer"))
- continue;
+ for (u = unit_infos; u < unit_infos + n; u++) {
+ _cleanup_strv_free_ char **triggered = NULL;
+ dual_timestamp next = DUAL_TIMESTAMP_NULL;
+ usec_t m, last = 0;
- r = get_triggered_units(bus, u->unit_path, &triggered);
- if (r < 0)
- goto cleanup;
+ if (!endswith(u->id, ".timer"))
+ continue;
- r = get_next_elapse(bus, u->unit_path, &next);
- if (r < 0)
- goto cleanup;
+ r = get_triggered_units(bus, u->unit_path, &triggered);
+ if (r < 0)
+ goto cleanup;
- get_last_trigger(bus, u->unit_path, &last);
+ r = get_next_elapse(bus, u->unit_path, &next);
+ if (r < 0)
+ goto cleanup;
- if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
- r = log_oom();
- goto cleanup;
- }
+ get_last_trigger(bus, u->unit_path, &last);
- m = calc_next_elapse(&nw, &next);
+ if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
+ r = log_oom();
+ goto cleanup;
+ }
- timer_infos[c++] = (struct timer_info) {
- .machine = u->machine,
- .id = u->id,
- .next_elapse = m,
- .last_trigger = last,
- .triggered = TAKE_PTR(triggered),
- };
- }
+ m = calc_next_elapse(&nw, &next);
- typesafe_qsort(timer_infos, c, timer_info_compare);
+ timer_infos[c++] = (struct timer_info) {
+ .machine = u->machine,
+ .id = u->id,
+ .next_elapse = m,
+ .last_trigger = last,
+ .triggered = TAKE_PTR(triggered),
+ };
+ }
+
+ typesafe_qsort(timer_infos, c, timer_info_compare);
+ }
output_timers_list(timer_infos, c);
assert(argc >= 2);
assert(argv);
- r = unit_name_mangle_with_suffix(argv[1], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".target", &unit);
+ r = unit_name_mangle_with_suffix(argv[1], "set-default",
+ arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ ".target", &unit);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
if (!path)
return log_oom();
- r = chase_symlinks(path, arg_root, 0, &lpath);
+ r = chase_symlinks(path, arg_root, 0, &lpath, NULL);
if (r == -ENOENT)
continue;
if (r == -ENOMEM)
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, NULL, &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_paths(arg_root, lp->search_path, NULL,
return r;
}
-static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
- _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
- char **name;
- int r, i;
-
- assert(bus);
- assert(ret);
-
- STRV_FOREACH(name, names) {
- char *t;
- UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN);
-
- if (suffix)
- r = unit_name_mangle_with_suffix(*name, options, suffix, &t);
- else
- r = unit_name_mangle(*name, options, &t);
- if (r < 0)
- return log_error_errno(r, "Failed to mangle name: %m");
-
- if (string_is_glob(t))
- r = strv_consume(&globs, t);
- else
- r = strv_consume(&mangled, t);
- if (r < 0)
- return log_oom();
- }
-
- /* Query the manager only if any of the names are a glob, since
- * this is fairly expensive */
- if (!strv_isempty(globs)) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ UnitInfo *unit_infos = NULL;
- size_t allocated, n;
-
- r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
- if (r < 0)
- return r;
-
- n = strv_length(mangled);
- allocated = n + 1;
-
- for (i = 0; i < r; i++) {
- if (!GREEDY_REALLOC(mangled, allocated, n+2))
- return log_oom();
-
- mangled[n] = strdup(unit_infos[i].id);
- if (!mangled[n])
- return log_oom();
-
- mangled[++n] = NULL;
- }
- }
-
- *ret = TAKE_PTR(mangled);
- return 0;
-}
-
static const struct {
const char *target;
const char *verb;
if (!names)
return log_oom();
} else {
- r = expand_names(bus, strv_skip(argv, 1), suffix, &names);
+ bool expanded;
+
+ r = expand_names(bus, strv_skip(argv, 1), suffix, &names, &expanded);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
+
+ if (!arg_all && expanded && streq(job_type, "start") && !arg_quiet) {
+ log_warning("Warning: %ssystemctl start called with a glob pattern.%s",
+ ansi_highlight_red(),
+ ansi_normal());
+ log_notice("Hint: unit globs expand to loaded units, so start will usually have no effect.\n"
+ " Passing --all will also load units which are pulled in by other units.\n"
+ " See systemctl(1) for more details.");
+ }
}
if (!arg_no_block) {
if (r < 0)
return r;
- r = expand_names(bus, args, NULL, &names);
+ r = expand_names(bus, args, NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
if (streq(arg_job_mode, "fail"))
kill_who = strjoina(arg_kill_who, "-fail");
- r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
return log_oom();
}
- r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
char **dropin_paths;
+ char **triggered_by;
+ char **triggers;
+
const char *load_error;
const char *result;
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)) {
}
}
+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,
/* This shows pretty information about a unit. See
* print_property() for a low-level property printer */
- if (streq_ptr(i->active_state, "failed")) {
- active_on = ansi_highlight_red();
- active_off = ansi_normal();
- } else if (STRPTR_IN_SET(i->active_state, "active", "reloading")) {
- active_on = ansi_highlight_green();
- active_off = ansi_normal();
- } else
- active_on = active_off = "";
+ 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));
path = formatted_path;
if (!isempty(i->load_error))
- printf(" Loaded: %s%s%s (Reason: %s)\n",
+ printf(" Loaded: %s%s%s (Reason: %s)\n",
on, strna(i->load_state), off, i->load_error);
else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset) &&
!STR_IN_SET(i->unit_file_state, "generated", "transient"))
- printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n",
+ printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n",
on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset);
else if (path && !isempty(i->unit_file_state))
- printf(" Loaded: %s%s%s (%s; %s)\n",
+ printf(" Loaded: %s%s%s (%s; %s)\n",
on, strna(i->load_state), off, path, i->unit_file_state);
else if (path)
- printf(" Loaded: %s%s%s (%s)\n",
+ printf(" Loaded: %s%s%s (%s)\n",
on, strna(i->load_state), off, path);
else
- printf(" Loaded: %s%s%s\n",
+ printf(" Loaded: %s%s%s\n",
on, strna(i->load_state), off);
if (i->transient)
- printf("Transient: yes\n");
+ printf(" Transient: yes\n");
if (!strv_isempty(i->dropin_paths)) {
_cleanup_free_ char *dir = NULL;
const char *df;
if (!dir || last) {
- printf(dir ? " " :
- " Drop-In: ");
+ printf(dir ? " " :
+ " Drop-In: ");
dir = mfree(dir);
}
printf("%s\n"
- " %s", dir,
+ " %s", dir,
special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
}
ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
if (ss)
- printf(" Active: %s%s (%s)%s",
+ printf(" Active: %s%s (%s)%s",
active_on, strna(i->active_state), ss, active_off);
else
- printf(" Active: %s%s%s",
+ printf(" Active: %s%s%s",
active_on, strna(i->active_state), active_off);
if (!isempty(i->result) && !streq(i->result, "success"))
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];
i->next_elapse_monotonic};
usec_t next_elapse;
- printf(" Trigger: ");
+ printf(" Trigger: ");
dual_timestamp_get(&nw);
next_elapse = calc_next_elapse(&nw, &next);
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(since2, sizeof(since2), i->condition_timestamp);
- printf("Condition: start %scondition failed%s at %s%s%s\n",
+ 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)
- printf(" %s %s=%s%s%s was not met\n",
+ 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 ? "|" : "",
s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp);
- printf(" Assert: start %sassertion failed%s at %s%s%s\n",
+ 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");
+ printf(" none of the trigger assertions were met\n");
else if (i->failed_assert)
- printf(" %s=%s%s was not met\n",
+ 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);
+ printf(" Device: %s\n", i->sysfs_path);
if (i->where)
- printf(" Where: %s\n", i->where);
+ printf(" Where: %s\n", i->where);
if (i->what)
- printf(" What: %s\n", i->what);
+ printf(" What: %s\n", i->what);
STRV_FOREACH(t, i->documentation) {
_cleanup_free_ char *formatted = NULL;
else
q = *t;
- printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", q);
+ 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);
+ 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);
+ printf(" Accepted: %u; Connected: %u;", i->n_accepted, i->n_connections);
if (i->n_refused)
- printf(" Refused: %u", i->n_refused);
+ printf(" Refused: %u", i->n_refused);
printf("\n");
}
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));
+ 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) {
printf("status=%i", p->status);
- c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
+ c = exit_status_to_string(p->status, EXIT_STATUS_LIBC | EXIT_STATUS_SYSTEMD);
if (c)
printf("/%s", c);
if (i->main_pid > 0 || i->control_pid > 0) {
if (i->main_pid > 0) {
- printf(" Main PID: "PID_FMT, i->main_pid);
+ printf(" Main PID: "PID_FMT, i->main_pid);
if (i->running) {
printf("status=%i", i->exit_status);
- c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
+ c = exit_status_to_string(i->exit_status,
+ EXIT_STATUS_LIBC | EXIT_STATUS_SYSTEMD);
if (c)
printf("/%s", c);
}
if (i->status_text)
- printf(" Status: \"%s\"\n", 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));
+ 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",
+ 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",
+ 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);
+ printf(" Tasks: %" PRIu64, i->tasks_current);
if (i->tasks_max != (uint64_t) -1)
printf(" (limit: %" PRIu64 ")\n", i->tasks_max);
if (i->memory_current != (uint64_t) -1) {
char buf[FORMAT_BYTES_MAX];
- printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current));
+ 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 ||
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));
+ printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC));
}
if (i->control_group) {
static const char prefix[] = " ";
unsigned c;
- printf(" CGroup: %s\n", i->control_group);
+ printf(" CGroup: %s\n", i->control_group);
c = columns();
if (c > sizeof(prefix) - 1)
} else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) {
const int32_t *status, *signal;
- size_t sz_status, sz_signal, i;
+ 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, &sz_status);
+ 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, &sz_signal);
+ r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &n_signal);
if (r < 0)
return bus_log_parse_error(r);
if (r < 0)
return bus_log_parse_error(r);
- sz_status /= sizeof(int32_t);
- sz_signal /= sizeof(int32_t);
+ n_status /= sizeof(int32_t);
+ n_signal /= sizeof(int32_t);
- if (all || sz_status > 0 || sz_signal > 0) {
+ if (all || n_status > 0 || n_signal > 0) {
bool first = true;
if (!value) {
fputc('=', stdout);
}
- for (i = 0; i < sz_status; i++) {
- if (status[i] < 0 || status[i] > 255)
- continue;
-
+ for (i = 0; i < n_status; i++) {
if (first)
first = false;
else
printf("%"PRIi32, status[i]);
}
- for (i = 0; i < sz_signal; i++) {
+ for (i = 0; i < n_signal; i++) {
const char *str;
str = signal_to_string((int) signal[i]);
- if (!str)
- continue;
if (first)
first = false;
else
fputc(' ', stdout);
- fputs(str, stdout);
+ if (str)
+ fputs(str, stdout);
+ else
+ printf("%"PRIi32, status[i]);
}
fputc('\n', stdout);
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")) {
+ } 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;
{ "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) },
{ "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 },
{ "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 },
{}
};
if (!strv_isempty(patterns)) {
_cleanup_strv_free_ char **names = NULL;
- r = expand_names(bus, patterns, NULL, &names);
+ r = expand_names(bus, patterns, NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
bool first = true;
int r;
- /* Include all units by default - i.e. continue as if the --all
+ /* Include all units by default — i.e. continue as if the --all
* option was used */
if (strv_isempty(arg_states))
arg_all = true;
if (r < 0)
return r;
- r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
polkit_agent_open_maybe();
- r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
return r;
}
-static int mangle_names(char **original_names, char ***mangled_names) {
+static int mangle_names(const char *operation, char **original_names, char ***mangled_names) {
char **i, **l, **name;
int r;
return log_oom();
}
} else {
- r = unit_name_mangle(*name, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, i);
+ r = unit_name_mangle_with_suffix(*name, operation,
+ arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ ".service", i);
if (r < 0) {
*i = NULL;
strv_free(l);
if (!argv[1])
return 0;
- r = mangle_names(strv_skip(argv, 1), &names);
+ r = mangle_names("to enable", strv_skip(argv, 1), &names);
if (r < 0)
return r;
if (!argv[1])
return 0;
- r = unit_name_mangle_with_suffix(argv[1], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".target", &target);
+ r = unit_name_mangle_with_suffix(argv[1], "as target",
+ arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+ ".target", &target);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
- r = mangle_names(strv_skip(argv, 2), &names);
+ r = mangle_names("as dependency", strv_skip(argv, 2), &names);
if (r < 0)
return r;
char **name;
int r;
- r = mangle_names(strv_skip(argv, 1), &names);
+ r = mangle_names("to check", strv_skip(argv, 1), &names);
if (r < 0)
return r;
if (r < 0)
return r;
- r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
if (r < 0)
return log_oom();
- printf("%1$s [OPTIONS...] {COMMAND} ...\n\n"
- "Query or send control commands to the systemd manager.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --system Connect to system manager\n"
- " --user Connect to user service manager\n"
- " -H --host=[USER@]HOST\n"
- " Operate on remote host\n"
- " -M --machine=CONTAINER\n"
- " Operate on local container\n"
- " -t --type=TYPE List units of a particular type\n"
- " --state=STATE List units with particular LOAD or SUB or ACTIVE state\n"
- " --failed Shorcut for --state=failed\n"
- " -p --property=NAME Show only properties by this name\n"
- " -a --all Show all properties/all units currently in memory,\n"
- " including dead/empty ones. To list all units installed on\n"
- " the system, use the 'list-unit-files' command instead.\n"
- " -l --full Don't ellipsize unit names on output\n"
- " -r --recursive Show unit list of host and local containers\n"
- " --reverse Show reverse dependencies with 'list-dependencies'\n"
- " --job-mode=MODE Specify how to deal with already queued jobs, when\n"
- " queueing a new job\n"
- " -T --show-transaction\n"
- " When enqueuing a unit job, show full transaction\n"
- " --show-types When showing sockets, explicitly show their type\n"
- " --value When showing properties, only print the value\n"
- " -i --ignore-inhibitors\n"
- " 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"
- " --wait For (re)start, wait until service stopped again\n"
- " For is-system-running, wait until startup is completed\n"
- " --no-block Do not wait until operation finished\n"
- " --no-wall Don't send wall message before halt/power-off/reboot\n"
- " --no-reload Don't reload daemon after en-/dis-abling unit files\n"
- " --no-legend Do not print a legend (column headers and hints)\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-ask-password\n"
- " Do not ask for system passwords\n"
- " --global Enable/disable/mask unit files globally\n"
- " --runtime Enable/disable/mask unit files temporarily until next\n"
- " reboot\n"
- " -f --force When enabling unit files, override existing symlinks\n"
- " When shutting down, execute action immediately\n"
- " --preset-mode= Apply only enable, only disable, or all presets\n"
- " --root=PATH Enable/disable/mask unit files in the specified root\n"
- " directory\n"
- " -n --lines=INTEGER Number of journal entries to show\n"
- " -o --output=STRING Change journal output mode (short, short-precise,\n"
- " short-iso, short-iso-precise, short-full,\n"
- " short-monotonic, short-unix,\n"
- " verbose, export, json, json-pretty, json-sse, cat)\n"
- " --firmware-setup Tell the firmware to show the setup menu on next boot\n"
- " --boot-loader-menu=TIME\n"
- " Boot into boot loader menu on next boot\n"
- " --boot-loader-entry=NAME\n"
- " Boot into a specific boot loader entry on next boot\n"
- " --plain Print unit dependencies as a list instead of a tree\n\n"
+ printf("%5$s%1$s [OPTIONS...] {COMMAND} ...\n\n"
+ "Query or send control commands to the systemd manager.%6$s\n\n"
"%3$sUnit Commands:%4$s\n"
" list-units [PATTERN...] List units currently in memory\n"
" list-sockets [PATTERN...] List socket units currently in memory,\n"
" hybrid-sleep Hibernate and suspend the system\n"
" suspend-then-hibernate Suspend the system, wake after a period of\n"
" time and put it into hibernate\n"
+ "\nOptions\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --system Connect to system manager\n"
+ " --user Connect to user service manager\n"
+ " -H --host=[USER@]HOST\n"
+ " Operate on remote host\n"
+ " -M --machine=CONTAINER\n"
+ " Operate on local container\n"
+ " -t --type=TYPE List units of a particular type\n"
+ " --state=STATE List units with particular LOAD or SUB or ACTIVE state\n"
+ " --failed Shorcut for --state=failed\n"
+ " -p --property=NAME Show only properties by this name\n"
+ " -a --all Show all properties/all units currently in memory,\n"
+ " including dead/empty ones. To list all units installed on\n"
+ " the system, use the 'list-unit-files' command instead.\n"
+ " -l --full Don't ellipsize unit names on output\n"
+ " -r --recursive Show unit list of host and local containers\n"
+ " --reverse Show reverse dependencies with 'list-dependencies'\n"
+ " --job-mode=MODE Specify how to deal with already queued jobs, when\n"
+ " queueing a new job\n"
+ " -T --show-transaction\n"
+ " When enqueuing a unit job, show full transaction\n"
+ " --show-types When showing sockets, explicitly show their type\n"
+ " --value When showing properties, only print the value\n"
+ " -i --ignore-inhibitors\n"
+ " 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"
+ " --wait For (re)start, wait until service stopped again\n"
+ " For is-system-running, wait until startup is completed\n"
+ " --no-block Do not wait until operation finished\n"
+ " --no-wall Don't send wall message before halt/power-off/reboot\n"
+ " --no-reload Don't reload daemon after en-/dis-abling unit files\n"
+ " --no-legend Do not print a legend (column headers and hints)\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-ask-password\n"
+ " Do not ask for system passwords\n"
+ " --global Enable/disable/mask unit files globally\n"
+ " --runtime Enable/disable/mask unit files temporarily until next\n"
+ " reboot\n"
+ " -f --force When enabling unit files, override existing symlinks\n"
+ " When shutting down, execute action immediately\n"
+ " --preset-mode= Apply only enable, only disable, or all presets\n"
+ " --root=PATH Enable/disable/mask unit files in the specified root\n"
+ " directory\n"
+ " -n --lines=INTEGER Number of journal entries to show\n"
+ " -o --output=STRING Change journal output mode (short, short-precise,\n"
+ " short-iso, short-iso-precise, short-full,\n"
+ " short-monotonic, short-unix,\n"
+ " verbose, export, json, json-pretty, json-sse, cat)\n"
+ " --firmware-setup Tell the firmware to show the setup menu on next boot\n"
+ " --boot-loader-menu=TIME\n"
+ " Boot into boot loader menu on next boot\n"
+ " --boot-loader-entry=NAME\n"
+ " Boot into a specific boot loader entry on next boot\n"
+ " --plain Print unit dependencies as a list instead of a tree\n"
"\nSee the %2$s for details.\n"
, program_invocation_short_name
, link
, ansi_underline(), ansi_normal()
+ , ansi_highlight(), ansi_normal()
);
return 0;
puts("\nAvailable unit active states:");
DUMP_STRING_TABLE(unit_active_state, UnitActiveState, _UNIT_ACTIVE_STATE_MAX);
+ if (!arg_no_legend)
+ puts("\nAvailable unit file states:");
+ DUMP_STRING_TABLE(unit_file_state, UnitFileState, _UNIT_FILE_STATE_MAX);
+
if (!arg_no_legend)
puts("\nAvailable automount unit substates:");
DUMP_STRING_TABLE(automount_state, AutomountState, _AUTOMOUNT_STATE_MAX);
static int help_boot_loader_entry(void) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char **l = NULL;
+ _cleanup_strv_free_ char **l = NULL;
sd_bus *bus;
char **i;
int r;