summaryrefslogtreecommitdiffstats
path: root/src/systemctl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/systemctl/fuzz-systemctl-parse-argv.c4
-rw-r--r--src/systemctl/meson.build9
-rw-r--r--src/systemctl/systemctl-compat-shutdown.c2
-rw-r--r--src/systemctl/systemctl-compat-telinit.c8
-rw-r--r--src/systemctl/systemctl-compat-telinit.h1
-rw-r--r--src/systemctl/systemctl-edit.c34
-rw-r--r--src/systemctl/systemctl-enable.c61
-rw-r--r--src/systemctl/systemctl-kill.c27
-rw-r--r--src/systemctl/systemctl-list-jobs.c4
-rw-r--r--src/systemctl/systemctl-list-unit-files.c2
-rw-r--r--src/systemctl/systemctl-list-units.c68
-rw-r--r--src/systemctl/systemctl-logind.c11
-rw-r--r--src/systemctl/systemctl-mount.c2
-rw-r--r--src/systemctl/systemctl-show.c143
-rw-r--r--src/systemctl/systemctl-start-special.c24
-rw-r--r--src/systemctl/systemctl-start-unit.c63
-rw-r--r--src/systemctl/systemctl-sysv-compat.c2
-rw-r--r--src/systemctl/systemctl-util.c215
-rw-r--r--src/systemctl/systemctl-util.h7
-rw-r--r--src/systemctl/systemctl-whoami.c76
-rw-r--r--src/systemctl/systemctl.c57
-rw-r--r--src/systemctl/systemctl.h3
22 files changed, 496 insertions, 327 deletions
diff --git a/src/systemctl/fuzz-systemctl-parse-argv.c b/src/systemctl/fuzz-systemctl-parse-argv.c
index 9ea8f7a..99cf6c2 100644
--- a/src/systemctl/fuzz-systemctl-parse-argv.c
+++ b/src/systemctl/fuzz-systemctl-parse-argv.c
@@ -49,7 +49,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
opterr = 0; /* do not print errors */
}
+ /* We need to reset some global state manually here since libfuzzer feeds a single process with
+ * multiple inputs, so we might carry over state from previous invocations that can trigger
+ * certain asserts. */
optind = 0; /* this tells the getopt machinery to reinitialize */
+ arg_transport = BUS_TRANSPORT_LOCAL;
r = systemctl_dispatch_parse_argv(strv_length(argv), argv);
if (r < 0)
diff --git a/src/systemctl/meson.build b/src/systemctl/meson.build
index 255c639..88f73bf 100644
--- a/src/systemctl/meson.build
+++ b/src/systemctl/meson.build
@@ -44,8 +44,7 @@ if get_option('link-systemctl-shared')
systemctl_link_with = [libshared]
else
systemctl_link_with = [libsystemd_static,
- libshared_static,
- libbasic_gcrypt]
+ libshared_static]
endif
executables += [
@@ -56,10 +55,10 @@ executables += [
'link_with' : systemctl_link_with,
'dependencies' : [
libcap,
- liblz4,
+ liblz4_cflags,
libselinux,
- libxz,
- libzstd,
+ libxz_cflags,
+ libzstd_cflags,
threads,
],
},
diff --git a/src/systemctl/systemctl-compat-shutdown.c b/src/systemctl/systemctl-compat-shutdown.c
index 881d00e..c5b4cb4 100644
--- a/src/systemctl/systemctl-compat-shutdown.c
+++ b/src/systemctl/systemctl-compat-shutdown.c
@@ -35,7 +35,7 @@ static int shutdown_help(void) {
" --no-wall Don't send wall message before halt/power-off/reboot\n"
" -c Cancel a pending shutdown\n"
" --show Show pending shutdown\n"
- "\n%sThis is a compatibility interface, please use the more powerful 'systemctl reboot',\n"
+ "\n%sThis is a compatibility interface, please use the more powerful 'systemctl halt',\n"
"'systemctl poweroff', 'systemctl reboot' commands instead.%s\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
diff --git a/src/systemctl/systemctl-compat-telinit.c b/src/systemctl/systemctl-compat-telinit.c
index 20325e5..210d0a1 100644
--- a/src/systemctl/systemctl-compat-telinit.c
+++ b/src/systemctl/systemctl-compat-telinit.c
@@ -155,11 +155,3 @@ int reload_with_fallback(void) {
return 0;
}
-
-int exec_telinit(char *argv[]) {
- (void) rlimit_nofile_safe();
- (void) execv(TELINIT, argv);
-
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Couldn't find an alternative telinit implementation to spawn.");
-}
diff --git a/src/systemctl/systemctl-compat-telinit.h b/src/systemctl/systemctl-compat-telinit.h
index 783c387..1a2bcd4 100644
--- a/src/systemctl/systemctl-compat-telinit.h
+++ b/src/systemctl/systemctl-compat-telinit.h
@@ -4,4 +4,3 @@
int telinit_parse_argv(int argc, char *argv[]);
int start_with_fallback(void);
int reload_with_fallback(void);
-int exec_telinit(char *argv[]);
diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
index 367afa2..15398f8 100644
--- a/src/systemctl/systemctl-edit.c
+++ b/src/systemctl/systemctl-edit.c
@@ -14,8 +14,8 @@
#include "terminal-util.h"
int verb_cat(int argc, char *argv[], void *userdata) {
- _cleanup_hashmap_free_ Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
- _cleanup_(lookup_paths_free) LookupPaths lp = {};
+ _cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
+ _cleanup_(lookup_paths_done) LookupPaths lp = {};
_cleanup_strv_free_ char **names = NULL;
sd_bus *bus;
bool first = true;
@@ -50,7 +50,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *fragment_path = NULL;
_cleanup_strv_free_ char **dropin_paths = NULL;
- r = unit_find_paths(bus, *name, &lp, false, &cached_name_map, &cached_id_map, &fragment_path, &dropin_paths);
+ r = unit_find_paths(bus, *name, &lp, false, &cached_id_map, &cached_name_map, &fragment_path, &dropin_paths);
if (r == -ERFKILL) {
printf("%s# Unit %s is masked%s.\n",
ansi_highlight_magenta(),
@@ -197,8 +197,8 @@ static int find_paths_to_edit(
EditFileContext *context,
char **names) {
- _cleanup_hashmap_free_ Hashmap *cached_name_map = NULL, *cached_id_map = NULL;
- _cleanup_(lookup_paths_free) LookupPaths lp = {};
+ _cleanup_hashmap_free_ Hashmap *cached_id_map = NULL, *cached_name_map = NULL;
+ _cleanup_(lookup_paths_done) LookupPaths lp = {};
_cleanup_free_ char *drop_in_alloc = NULL, *suffix = NULL;
const char *drop_in;
int r;
@@ -233,13 +233,13 @@ static int find_paths_to_edit(
_cleanup_free_ char *path = NULL;
_cleanup_strv_free_ char **unit_paths = NULL;
- r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ false, &cached_name_map, &cached_id_map, &path, &unit_paths);
+ r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ false, &cached_id_map, &cached_name_map, &path, &unit_paths);
if (r == -EKEYREJECTED) {
/* If loading of the unit failed server side complete, then the server won't tell us
* the unit file path. In that case, find the file client side. */
log_debug_errno(r, "Unit '%s' was not loaded correctly, retrying client-side.", *name);
- r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ true, &cached_name_map, &cached_id_map, &path, &unit_paths);
+ r = unit_find_paths(bus, *name, &lp, /* force_client_side= */ true, &cached_id_map, &cached_name_map, &path, &unit_paths);
}
if (r == -ERFKILL)
return log_error_errno(r, "Unit '%s' masked, cannot edit.", *name);
@@ -247,13 +247,12 @@ static int find_paths_to_edit(
return r; /* Already logged by unit_find_paths() */
if (!path) {
- if (!arg_force) {
- log_info("Run 'systemctl edit%s --force --full %s' to create a new unit.",
- arg_runtime_scope == RUNTIME_SCOPE_GLOBAL ? " --global" :
- arg_runtime_scope == RUNTIME_SCOPE_USER ? " --user" : "",
- *name);
- return -ENOENT;
- }
+ if (!arg_force)
+ return log_info_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Run 'systemctl edit%s --force --full %s' to create a new unit.",
+ arg_runtime_scope == RUNTIME_SCOPE_GLOBAL ? " --global" :
+ arg_runtime_scope == RUNTIME_SCOPE_USER ? " --user" : "",
+ *name);
/* Create a new unit from scratch */
r = unit_file_create_new(
@@ -317,12 +316,13 @@ int verb_edit(int argc, char *argv[], void *userdata) {
.marker_end = DROPIN_MARKER_END,
.remove_parent = !arg_full,
.overwrite_with_origin = true,
+ .stdin = arg_stdin,
};
_cleanup_strv_free_ char **names = NULL;
sd_bus *bus;
int r;
- if (!on_tty())
+ if (!on_tty() && !arg_stdin)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty.");
if (arg_transport != BUS_TRANSPORT_LOCAL)
@@ -342,6 +342,10 @@ int verb_edit(int argc, char *argv[], void *userdata) {
if (strv_isempty(names))
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No units matched the specified patterns.");
+ if (arg_stdin && arg_full && strv_length(names) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "With 'edit --stdin --full', exactly one unit for editing must be specified.");
+
STRV_FOREACH(tmp, names) {
r = unit_is_masked(bus, *tmp);
if (r < 0)
diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c
index 7d9b7c7..496a817 100644
--- a/src/systemctl/systemctl-enable.c
+++ b/src/systemctl/systemctl-enable.c
@@ -66,12 +66,14 @@ int verb_enable(int argc, char *argv[], void *userdata) {
const char *verb = argv[0];
int carries_install_info = -1;
bool ignore_carries_install_info = arg_quiet || arg_no_warn;
+ sd_bus *bus = NULL;
int r;
if (!argv[1])
return 0;
- r = mangle_names("to enable", strv_skip(argv, 1), &names);
+ const char *operation = strjoina("to ", verb);
+ r = mangle_names(operation, strv_skip(argv, 1), &names);
if (r < 0)
return r;
@@ -140,10 +142,9 @@ int verb_enable(int argc, char *argv[], void *userdata) {
bool send_runtime = true, send_force = true, send_preset_mode = false;
const char *method, *warn_trigger_operation = NULL;
bool warn_trigger_ignore_masked = true; /* suppress "used uninitialized" warning */
- sd_bus *bus;
if (STR_IN_SET(verb, "mask", "unmask")) {
- _cleanup_(lookup_paths_free) LookupPaths lp = {};
+ _cleanup_(lookup_paths_done) LookupPaths lp = {};
r = lookup_paths_init_or_warn(&lp, arg_runtime_scope, 0, arg_root);
if (r < 0)
@@ -312,25 +313,51 @@ int verb_enable(int argc, char *argv[], void *userdata) {
}
}
- if (arg_now && STR_IN_SET(argv[0], "enable", "disable", "mask")) {
- sd_bus *bus;
- size_t len, i;
+ if (arg_now) {
+ _cleanup_strv_free_ char **new_args = NULL;
- r = acquire_bus(BUS_MANAGER, &bus);
- if (r < 0)
- return r;
+ if (!STR_IN_SET(verb, "enable", "disable", "mask"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--now can only be used with verb enable, disable, or mask.");
+
+ if (install_client_side())
+ return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
+ "--now cannot be used when systemd is not running or in conjunction with --root=/--global, refusing.");
+
+ assert(bus);
+
+ if (strv_extend(&new_args, streq(verb, "enable") ? "start" : "stop") < 0)
+ return log_oom();
+
+ STRV_FOREACH(name, names) {
+ if (streq(verb, "enable")) {
+ char *fn;
- len = strv_length(names);
- {
- char *new_args[len + 2];
+ /* 'enable' accept path to unit files, so extract it first. Don't try to
+ * glob them though, as starting globbed unit seldom makes sense and
+ * actually changes the semantic (we're operating on DefaultInstance=
+ * when enabling). */
- new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop");
- for (i = 0; i < len; i++)
- new_args[i + 1] = basename(names[i]);
- new_args[i + 1] = NULL;
+ r = path_extract_filename(*name, &fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename of '%s': %m", *name);
+
+ r = strv_consume(&new_args, fn);
+ } else if (unit_name_is_valid(*name, UNIT_NAME_TEMPLATE)) {
+ char *globbed;
+
+ r = unit_name_replace_instance_full(*name, "*", /* accept_glob = */ true, &globbed);
+ if (r < 0)
+ return log_error_errno(r, "Failed to glob unit name '%s': %m", *name);
- r = verb_start(len + 1, new_args, userdata);
+ r = strv_consume(&new_args, globbed);
+ } else
+ r = strv_extend(&new_args, *name);
+ if (r < 0)
+ return log_oom();
}
+
+ return verb_start(strv_length(new_args), new_args, userdata);
}
return 0;
diff --git a/src/systemctl/systemctl-kill.c b/src/systemctl/systemctl-kill.c
index c4c6096..4c1829e 100644
--- a/src/systemctl/systemctl-kill.c
+++ b/src/systemctl/systemctl-kill.c
@@ -2,11 +2,13 @@
#include "bus-error.h"
#include "bus-locator.h"
+#include "bus-wait-for-units.h"
#include "systemctl-kill.h"
#include "systemctl-util.h"
#include "systemctl.h"
int verb_kill(int argc, char *argv[], void *userdata) {
+ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
_cleanup_strv_free_ char **names = NULL;
const char *kill_whom;
sd_bus *bus;
@@ -16,6 +18,12 @@ int verb_kill(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
+ if (arg_wait) {
+ r = bus_wait_for_units_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate unit watch context: %m");
+ }
+
polkit_agent_open_maybe();
kill_whom = arg_kill_whom ?: "all";
@@ -48,11 +56,24 @@ int verb_kill(int argc, char *argv[], void *userdata) {
NULL,
"ssi", *name, kill_whom, arg_signal);
if (q < 0) {
- log_error_errno(q, "Failed to kill unit %s: %s", *name, bus_error_message(&error, q));
- if (r == 0)
- r = q;
+ RET_GATHER(r, log_error_errno(q, "Failed to kill unit %s: %s", *name, bus_error_message(&error, q)));
+ continue;
+ }
+
+ if (w) {
+ q = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL);
+ if (q < 0)
+ RET_GATHER(r, log_error_errno(q, "Failed to watch unit %s: %m", *name));
}
}
+ if (w) {
+ q = bus_wait_for_units_run(w);
+ if (q < 0)
+ return log_error_errno(q, "Failed to wait for units: %m");
+ if (q == BUS_WAIT_FAILURE)
+ RET_GATHER(r, -EIO);
+ }
+
return r;
}
diff --git a/src/systemctl/systemctl-list-jobs.c b/src/systemctl/systemctl-list-jobs.c
index a752173..fcfe2ac 100644
--- a/src/systemctl/systemctl-list-jobs.c
+++ b/src/systemctl/systemctl-list-jobs.c
@@ -102,9 +102,9 @@ static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n
return table_log_add_error(r);
if (arg_jobs_after)
- output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\twaiting for job");
+ output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\tblocking job");
if (arg_jobs_before)
- output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\tblocking job");
+ output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\twaiting for job");
}
r = table_print(table, NULL);
diff --git a/src/systemctl/systemctl-list-unit-files.c b/src/systemctl/systemctl-list-unit-files.c
index fc1ad98..b8b1531 100644
--- a/src/systemctl/systemctl-list-unit-files.c
+++ b/src/systemctl/systemctl-list-unit-files.c
@@ -79,7 +79,7 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) {
table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
- for (const UnitFileList *u = units; u < units + c; u++) {
+ FOREACH_ARRAY(u, units, c) {
const char *on_underline = NULL, *on_unit_color = NULL, *id;
bool underline;
diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c
index fbc04b7..184468e 100644
--- a/src/systemctl/systemctl-list-units.c
+++ b/src/systemctl/systemctl-list-units.c
@@ -127,49 +127,69 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
FOREACH_ARRAY(u, unit_infos, c) {
+ const char *on_loaded = NULL, *on_active = NULL, *on_sub = NULL, *on_circle = NULL;
_cleanup_free_ char *id = NULL;
- const char *on_underline = "", *on_loaded = "", *on_active = "", *on_circle = "";
- bool circle = false, underline = false;
+ bool circle = false, underline;
- if (u + 1 < unit_infos + c &&
- !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) {
- on_underline = ansi_underline();
- underline = true;
- }
+ underline = u + 1 < unit_infos + c && !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id));
- if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) {
- on_circle = underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow();
+ if (streq(u->load_state, "not-found")) {
+ on_circle = on_loaded = ansi_highlight_yellow();
circle = true;
- on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
- } else if (streq(u->active_state, "failed") && !arg_plain) {
- on_circle = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
+ } else if (STR_IN_SET(u->load_state, "bad-setting", "error", "masked")) {
+ on_loaded = ansi_highlight_red();
+ on_circle = ansi_highlight_yellow();
circle = true;
- on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
- } else {
- on_circle = on_underline;
- on_active = on_underline;
- on_loaded = on_underline;
}
+ if (streq(u->active_state, "failed")) {
+ on_sub = on_active = ansi_highlight_red();
+
+ /* Here override any load_state highlighting */
+ on_circle = ansi_highlight_red();
+ circle = true;
+ } else if (STR_IN_SET(u->active_state, "reloading", "activating", "maintenance", "deactivating")) {
+ on_sub = on_active = ansi_highlight();
+
+ if (!circle) { /* Here we let load_state highlighting win */
+ on_circle = ansi_highlight();
+ circle = true;
+ }
+ } else if (streq(u->active_state, "inactive"))
+ on_sub = on_active = ansi_grey();
+
+ /* As a special case, when this is a service which has not process running, let's grey out
+ * its state, to highlight that a bit */
+ if (!on_sub && endswith(u->id, ".service") && streq(u->sub_state, "exited"))
+ on_sub = ansi_grey();
+
+ if (arg_plain)
+ circle = false;
+
id = format_unit_id(u->id, u->machine);
if (!id)
return log_oom();
r = table_add_many(table,
TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
- TABLE_SET_BOTH_COLORS, on_circle,
+ TABLE_SET_COLOR, on_circle,
+ TABLE_SET_BOTH_UNDERLINES, underline,
TABLE_STRING, id,
- TABLE_SET_BOTH_COLORS, on_active,
+ TABLE_SET_COLOR, on_active,
+ TABLE_SET_BOTH_UNDERLINES, underline,
TABLE_STRING, u->load_state,
- TABLE_SET_BOTH_COLORS, on_loaded,
+ TABLE_SET_COLOR, on_loaded,
+ TABLE_SET_BOTH_UNDERLINES, underline,
TABLE_STRING, u->active_state,
- TABLE_SET_BOTH_COLORS, on_active,
+ TABLE_SET_COLOR, on_active,
+ TABLE_SET_BOTH_UNDERLINES, underline,
TABLE_STRING, u->sub_state,
- TABLE_SET_BOTH_COLORS, on_active,
+ TABLE_SET_COLOR, on_sub,
+ TABLE_SET_BOTH_UNDERLINES, underline,
TABLE_STRING, u->job_id ? u->job_type: "",
- TABLE_SET_BOTH_COLORS, on_underline,
+ TABLE_SET_BOTH_UNDERLINES, underline,
TABLE_STRING, u->description,
- TABLE_SET_BOTH_COLORS, on_underline);
+ TABLE_SET_BOTH_UNDERLINES, underline);
if (r < 0)
return table_log_add_error(r);
diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
index 7f97325..d6cdd97 100644
--- a/src/systemctl/systemctl-logind.c
+++ b/src/systemctl/systemctl-logind.c
@@ -7,6 +7,7 @@
#include "bus-error.h"
#include "bus-locator.h"
#include "login-util.h"
+#include "mountpoint-util.h"
#include "process-util.h"
#include "systemctl-logind.h"
#include "systemctl-start-unit.h"
@@ -51,6 +52,7 @@ int logind_reboot(enum action a) {
[ACTION_HIBERNATE] = "Hibernate",
[ACTION_HYBRID_SLEEP] = "HybridSleep",
[ACTION_SUSPEND_THEN_HIBERNATE] = "SuspendThenHibernate",
+ [ACTION_SLEEP] = "Sleep",
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -71,7 +73,7 @@ int logind_reboot(enum action a) {
polkit_agent_open_maybe();
(void) logind_set_wall_message(bus);
- const char *method_with_flags = strjoina(actions[a], "WithFlags");
+ const char *method_with_flags = a == ACTION_SLEEP ? actions[a] : strjoina(actions[a], "WithFlags");
log_debug("%s org.freedesktop.login1.Manager %s dbus call.",
arg_dry_run ? "Would execute" : "Executing", method_with_flags);
@@ -83,9 +85,12 @@ int logind_reboot(enum action a) {
SET_FLAG(flags,
SD_LOGIND_REBOOT_VIA_KEXEC,
a == ACTION_KEXEC || (a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_KEXEC") <= 0));
+ /* Try to soft-reboot if /run/nextroot/ is a valid OS tree, but only if it's also a mount point.
+ * Otherwise, if people store new rootfs directly on /run/ tmpfs, 'systemctl reboot' would always
+ * soft-reboot, as /run/nextroot/ can never go away. */
SET_FLAG(flags,
SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP,
- a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0);
+ a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0 && path_is_mount_point("/run/nextroot") > 0);
SET_FLAG(flags, SD_LOGIND_SOFT_REBOOT, a == ACTION_SOFT_REBOOT);
r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
@@ -103,7 +108,7 @@ int logind_reboot(enum action a) {
}
if (r >= 0)
return 0;
- if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
+ if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || a == ACTION_SLEEP)
return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r));
/* Fall back to original methods in case there is an older version of systemd-logind */
diff --git a/src/systemctl/systemctl-mount.c b/src/systemctl/systemctl-mount.c
index d9ad332..61af218 100644
--- a/src/systemctl/systemctl-mount.c
+++ b/src/systemctl/systemctl-mount.c
@@ -86,7 +86,7 @@ int verb_mount_image(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *partition = NULL, *mount_options = NULL;
const char *options = argv[4];
- r = extract_many_words(&options, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
+ r = extract_many_words(&options, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
if (r < 0)
return r;
/* Single set of options, applying to the root partition/single filesystem */
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
index 5d1eb49..2fdf321 100644
--- a/src/systemctl/systemctl-show.c
+++ b/src/systemctl/systemctl-show.c
@@ -196,6 +196,8 @@ typedef struct UnitStatusInfo {
uint64_t runtime_max_sec;
+ sd_id128_t invocation_id;
+
bool need_daemon_reload;
bool transient;
@@ -204,7 +206,7 @@ typedef struct UnitStatusInfo {
pid_t control_pid;
const char *status_text;
const char *pid_file;
- bool running:1;
+ bool running;
int status_errno;
uint32_t fd_store_max;
@@ -468,6 +470,9 @@ static void print_status_info(
} else
printf("\n");
+ if (!sd_id128_is_null(i->invocation_id))
+ printf(" Invocation: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(i->invocation_id));
+
STRV_FOREACH(t, i->triggered_by) {
UnitActiveState state = _UNIT_ACTIVE_STATE_INVALID;
@@ -703,17 +708,18 @@ static void print_status_info(
if (i->n_fd_store > 0 || i->fd_store_max > 0)
printf(" FD Store: %u%s (limit: %u)%s\n", i->n_fd_store, ansi_grey(), i->fd_store_max, ansi_normal());
+ bool show_memory_peak = i->memory_peak != CGROUP_LIMIT_MAX,
+ show_memory_swap_peak = !IN_SET(i->memory_swap_peak, 0, CGROUP_LIMIT_MAX);
+
if (i->memory_current != UINT64_MAX) {
printf(" Memory: %s", FORMAT_BYTES(i->memory_current));
- /* Only show current swap if it ever was non-zero or is currently non-zero. In both cases
- memory_swap_peak will be non-zero (and not CGROUP_LIMIT_MAX).
- Only show the available memory if it was artificially limited. */
- bool show_memory_swap = !IN_SET(i->memory_swap_peak, 0, CGROUP_LIMIT_MAX),
- show_memory_zswap_current = !IN_SET(i->memory_zswap_current, 0, CGROUP_LIMIT_MAX),
- show_memory_available = i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX;
- if (i->memory_peak != CGROUP_LIMIT_MAX ||
- show_memory_swap ||
+ bool show_memory_zswap_current = !IN_SET(i->memory_zswap_current, 0, CGROUP_LIMIT_MAX),
+ /* Only show the available memory if it was artificially limited. */
+ show_memory_available = i->memory_available != CGROUP_LIMIT_MAX &&
+ (i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX);
+ if (show_memory_peak ||
+ show_memory_swap_peak || /* We don't need to check memory_swap_current, as if peak is 0 that must also be 0 */
show_memory_zswap_current ||
show_memory_available ||
i->memory_min > 0 ||
@@ -722,7 +728,6 @@ static void print_status_info(
i->memory_max != CGROUP_LIMIT_MAX || i->startup_memory_max != CGROUP_LIMIT_MAX ||
i->memory_swap_max != CGROUP_LIMIT_MAX || i->startup_memory_swap_max != CGROUP_LIMIT_MAX ||
i->memory_zswap_max != CGROUP_LIMIT_MAX || i->startup_memory_zswap_max != CGROUP_LIMIT_MAX ||
- i->memory_available != CGROUP_LIMIT_MAX ||
i->memory_limit != CGROUP_LIMIT_MAX) {
const char *prefix = "";
@@ -779,11 +784,11 @@ static void print_status_info(
printf("%savailable: %s", prefix, FORMAT_BYTES(i->memory_available));
prefix = " ";
}
- if (i->memory_peak != CGROUP_LIMIT_MAX) {
+ if (show_memory_peak) {
printf("%speak: %s", prefix, FORMAT_BYTES(i->memory_peak));
prefix = " ";
}
- if (show_memory_swap) {
+ if (show_memory_swap_peak) {
printf("%sswap: %s swap peak: %s", prefix,
FORMAT_BYTES(i->memory_swap_current), FORMAT_BYTES(i->memory_swap_peak));
prefix = " ";
@@ -795,6 +800,14 @@ static void print_status_info(
printf(")");
}
printf("\n");
+
+ } else if (show_memory_peak) {
+ printf(" Mem peak: %s", FORMAT_BYTES(i->memory_peak));
+
+ if (show_memory_swap_peak)
+ printf(" (swap: %s)", FORMAT_BYTES(i->memory_swap_peak));
+
+ putchar('\n');
}
if (i->cpu_usage_nsec != UINT64_MAX)
@@ -834,10 +847,9 @@ static void print_status_info(
i->id,
i->log_namespace,
arg_output,
- 0,
+ /* n_columns = */ 0,
i->inactive_exit_timestamp_monotonic,
arg_lines,
- getuid(),
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
arg_runtime_scope == RUNTIME_SCOPE_SYSTEM,
@@ -922,11 +934,7 @@ static int map_listen(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus
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);
+ r = strv_extend_many(p, type, path);
if (r < 0)
return r;
}
@@ -2025,8 +2033,9 @@ static int show_one(
{ "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) },
- { "RuntimeMaxUSec", "t", NULL, offsetof(UnitStatusInfo, runtime_max_sec) },
{ "InactiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_enter_timestamp) },
+ { "RuntimeMaxUSec", "t", NULL, offsetof(UnitStatusInfo, runtime_max_sec) },
+ { "InvocationID", "s", bus_map_id128, offsetof(UnitStatusInfo, invocation_id) },
{ "NeedDaemonReload", "b", NULL, offsetof(UnitStatusInfo, need_daemon_reload) },
{ "Transient", "b", NULL, offsetof(UnitStatusInfo, transient) },
{ "ExecMainPID", "u", NULL, offsetof(UnitStatusInfo, main_pid) },
@@ -2194,96 +2203,6 @@ static int show_one(
return 0;
}
-static int get_unit_dbus_path_by_pid_fallback(
- sd_bus *bus,
- uint32_t pid,
- char **ret_path,
- char **ret_unit) {
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *path = NULL, *unit = NULL;
- char *p;
- int r;
-
- assert(bus);
- assert(ret_path);
- assert(ret_unit);
-
- 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", &p);
- if (r < 0)
- return bus_log_parse_error(r);
-
- path = strdup(p);
- if (!path)
- return log_oom();
-
- r = unit_name_from_dbus_path(path, &unit);
- if (r < 0)
- return log_oom();
-
- *ret_unit = TAKE_PTR(unit);
- *ret_path = TAKE_PTR(path);
-
- return 0;
-}
-
-static int get_unit_dbus_path_by_pid(
- sd_bus *bus,
- uint32_t pid,
- char **ret_path,
- char **ret_unit) {
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *path = NULL, *unit = NULL;
- _cleanup_close_ int pidfd = -EBADF;
- char *p, *u;
- int r;
-
- assert(bus);
- assert(ret_path);
- assert(ret_unit);
-
- /* First, try to send a PIDFD across the wire, so that we can pin the process and there's no race
- * condition possible while we wait for the D-Bus reply. If we either don't have PIDFD support in
- * the kernel or the new D-Bus method is not available, then fallback to the older method that
- * sends the numeric PID. */
-
- pidfd = pidfd_open(pid, 0);
- if (pidfd < 0 && (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)))
- return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
- if (pidfd < 0)
- return log_error_errno(errno, "Failed to open PID %"PRIu32": %m", pid);
-
- r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pidfd);
- if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
- return get_unit_dbus_path_by_pid_fallback(bus, pid, ret_path, ret_unit);
- 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, "os", &p, &u);
- if (r < 0)
- return bus_log_parse_error(r);
-
- path = strdup(p);
- if (!path)
- return log_oom();
-
- unit = strdup(u);
- if (!unit)
- return log_oom();
-
- *ret_unit = TAKE_PTR(unit);
- *ret_path = TAKE_PTR(path);
-
- return 0;
-}
-
static int show_all(
sd_bus *bus,
SystemctlShowMode show_mode,
@@ -2455,9 +2374,9 @@ int verb_show(int argc, char *argv[], void *userdata) {
} else {
/* Interpret as PID */
- r = get_unit_dbus_path_by_pid(bus, id, &path, &unit);
+ r = lookup_unit_by_pidref(bus, (pid_t) id, &unit, &path);
if (r < 0) {
- ret = r;
+ RET_GATHER(ret, r);
continue;
}
}
diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c
index d23ce36..95cf00f 100644
--- a/src/systemctl/systemctl-start-special.c
+++ b/src/systemctl/systemctl-start-special.c
@@ -155,14 +155,16 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
return r;
}
- if (a == ACTION_REBOOT) {
- if (arg_reboot_argument) {
- r = update_reboot_parameter_and_warn(arg_reboot_argument, false);
- if (r < 0)
- return r;
- }
+ if (arg_reboot_argument && IN_SET(a, ACTION_HALT, ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC)) {
+ /* If we are going through an action that involves systemd-shutdown, let's set the reboot
+ * parameter, even if it's not a regular reboot. After all we nowadays send the string to
+ * our supervisor via sd_notify() too. */
+ r = update_reboot_parameter_and_warn(arg_reboot_argument, /* keep= */ false);
+ if (r < 0)
+ return r;
+ }
- } else if (a == ACTION_KEXEC) {
+ if (a == ACTION_KEXEC) {
r = load_kexec_kernel();
if (r < 0 && arg_force >= 1)
log_notice("Failed to load kexec kernel, continuing without.");
@@ -221,13 +223,19 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
case ACTION_HYBRID_SLEEP:
case ACTION_SUSPEND_THEN_HIBERNATE:
+ /* For sleep operations, do not automatically fall back to low-level operation for
+ * errors other than logind not available. There's a high chance that logind did
+ * some extra sanity check and that didn't pass. */
r = logind_reboot(a);
- if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ if (r >= 0 || (r != -ENOSYS && arg_force == 0))
return r;
arg_no_block = true;
break;
+ case ACTION_SLEEP:
+ return logind_reboot(a);
+
case ACTION_EXIT:
/* Since exit is so close in behaviour to power-off/reboot, let's also make
* it asynchronous, in order to not confuse the user needlessly with unexpected
diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c
index 6927e97..8068d77 100644
--- a/src/systemctl/systemctl-start-unit.c
+++ b/src/systemctl/systemctl-start-unit.c
@@ -35,20 +35,24 @@ static const struct {
{ "force-reload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */
};
-static const char *verb_to_method(const char *verb) {
- for (size_t i = 0; i < ELEMENTSOF(unit_actions); i++)
- if (streq_ptr(unit_actions[i].verb, verb))
- return unit_actions[i].method;
+static const char* verb_to_method(const char *verb) {
+ assert(verb);
- return "StartUnit";
+ FOREACH_ELEMENT(i, unit_actions)
+ if (streq(i->verb, verb))
+ return i->method;
+
+ return "StartUnit";
}
-static const char *verb_to_job_type(const char *verb) {
- for (size_t i = 0; i < ELEMENTSOF(unit_actions); i++)
- if (streq_ptr(unit_actions[i].verb, verb))
- return unit_actions[i].job_type;
+static const char* verb_to_job_type(const char *verb) {
+ assert(verb);
+
+ FOREACH_ELEMENT(i, unit_actions)
+ if (streq(i->verb, verb))
+ return i->job_type;
- return "start";
+ return "start";
}
static int start_unit_one(
@@ -236,9 +240,12 @@ const struct action_metadata action_table[_ACTION_MAX] = {
[ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
[ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
[ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" },
+ [ACTION_SLEEP] = { NULL, /* handled only by logind */ "sleep", NULL },
};
enum action verb_to_action(const char *verb) {
+ assert(verb);
+
for (enum action i = 0; i < _ACTION_MAX; i++)
if (streq_ptr(action_table[i].verb, verb))
return i;
@@ -254,14 +261,29 @@ static const char** make_extra_args(const char *extra_args[static 4]) {
if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
extra_args[n++] = "--user";
- if (arg_transport == BUS_TRANSPORT_REMOTE) {
+ switch (arg_transport) {
+
+ case BUS_TRANSPORT_REMOTE:
extra_args[n++] = "-H";
extra_args[n++] = arg_host;
- } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
+ break;
+
+ case BUS_TRANSPORT_MACHINE:
extra_args[n++] = "-M";
extra_args[n++] = arg_host;
- } else
- assert(arg_transport == BUS_TRANSPORT_LOCAL);
+ break;
+
+ case BUS_TRANSPORT_CAPSULE:
+ extra_args[n++] = "-C";
+ extra_args[n++] = arg_host;
+ break;
+
+ case BUS_TRANSPORT_LOCAL:
+ break;
+
+ default:
+ assert_not_reached();
+ }
extra_args[n] = NULL;
return extra_args;
@@ -294,6 +316,8 @@ int verb_start(int argc, char *argv[], void *userdata) {
action = verb_to_action(argv[0]);
+ assert(action != ACTION_SLEEP);
+
if (action != _ACTION_INVALID) {
/* A command in style "systemctl reboot", "systemctl poweroff", … */
method = "StartUnit";
@@ -357,10 +381,6 @@ int verb_start(int argc, char *argv[], void *userdata) {
}
if (arg_wait) {
- r = bus_call_method_async(bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to enable subscription: %m");
-
r = bus_wait_for_units_new(bus, &wu);
if (r < 0)
return log_error_errno(r, "Failed to allocate unit watch context: %m");
@@ -384,9 +404,12 @@ int verb_start(int argc, char *argv[], void *userdata) {
}
if (!arg_no_block) {
- const char* extra_args[4];
+ const char *extra_args[4];
+ WaitJobsFlags flags = 0;
- r = bus_wait_for_jobs(w, arg_quiet, make_extra_args(extra_args));
+ SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet);
+ SET_FLAG(flags, BUS_WAIT_JOBS_LOG_SUCCESS, arg_show_transaction);
+ r = bus_wait_for_jobs(w, flags, make_extra_args(extra_args));
if (r < 0)
return r;
diff --git a/src/systemctl/systemctl-sysv-compat.c b/src/systemctl/systemctl-sysv-compat.c
index 2aa1ec6..8ee16eb 100644
--- a/src/systemctl/systemctl-sysv-compat.c
+++ b/src/systemctl/systemctl-sysv-compat.c
@@ -111,7 +111,7 @@ int enable_sysv_units(const char *verb, char **args) {
int r = 0;
#if HAVE_SYSV_COMPAT
- _cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_(lookup_paths_done) LookupPaths paths = {};
unsigned f = 0;
SysVUnitEnableState enable_state = SYSV_UNIT_NOT_FOUND;
diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c
index 2498725..2482b7c 100644
--- a/src/systemctl/systemctl-util.c
+++ b/src/systemctl/systemctl-util.c
@@ -18,6 +18,8 @@
#include "glob-util.h"
#include "macro.h"
#include "path-util.h"
+#include "pidref.h"
+#include "process-util.h"
#include "reboot-util.h"
#include "set.h"
#include "spawn-ask-password-agent.h"
@@ -40,7 +42,7 @@ int acquire_bus(BusFocus focus, sd_bus **ret) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--global is not supported for this operation.");
/* We only go directly to the manager, if we are using a local transport */
- if (arg_transport != BUS_TRANSPORT_LOCAL)
+ if (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE))
focus = BUS_FULL;
if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0)
@@ -62,8 +64,8 @@ int acquire_bus(BusFocus focus, sd_bus **ret) {
}
void release_busses(void) {
- for (BusFocus w = 0; w < _BUS_FOCUS_MAX; w++)
- buses[w] = sd_bus_flush_close_unref(buses[w]);
+ FOREACH_ARRAY(w, buses, _BUS_FOCUS_MAX)
+ *w = sd_bus_flush_close_unref(*w);
}
void ask_password_agent_open_maybe(void) {
@@ -260,7 +262,13 @@ int get_unit_list(
return c;
}
-int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) {
+int expand_unit_names(
+ sd_bus *bus,
+ char * const *names,
+ const char *suffix,
+ char ***ret,
+ bool *ret_expanded) {
+
_cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
int r;
@@ -288,30 +296,20 @@ int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret
if (expanded) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
- size_t n;
r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
if (r < 0)
return r;
- n = strv_length(mangled);
-
- for (int i = 0; i < r; i++) {
- if (!GREEDY_REALLOC(mangled, n+2))
- return log_oom();
-
- mangled[n] = strdup(unit_infos[i].id);
- if (!mangled[n])
+ FOREACH_ARRAY(info, unit_infos, r)
+ if (strv_extend(&mangled, info->id) < 0)
return log_oom();
-
- mangled[++n] = NULL;
- }
}
+ *ret = TAKE_PTR(mangled);
if (ret_expanded)
*ret_expanded = expanded;
- *ret = TAKE_PTR(mangled);
return 0;
}
@@ -438,6 +436,9 @@ int need_daemon_reload(sd_bus *bus, const char *unit) {
void warn_unit_file_changed(const char *unit) {
assert(unit);
+ if (arg_no_warn)
+ return;
+
log_warning("Warning: The unit file, source configuration file or drop-ins of %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.",
unit,
arg_runtime_scope == RUNTIME_SCOPE_SYSTEM ? "" : " --user");
@@ -480,8 +481,8 @@ int unit_find_paths(
const char *unit_name,
LookupPaths *lp,
bool force_client_side,
- Hashmap **cached_name_map,
Hashmap **cached_id_map,
+ Hashmap **cached_name_map,
char **ret_fragment_path,
char ***ret_dropin_paths) {
@@ -763,9 +764,7 @@ int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) {
if (r < 0)
return log_error_errno(r, "Failed to append unit dependencies: %m");
- strv_free(*list);
- *list = TAKE_PTR(list_with_deps);
- return 0;
+ return strv_free_and_replace(*list, list_with_deps);
}
int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) {
@@ -921,37 +920,31 @@ UnitFileFlags unit_file_flags_from_args(void) {
(arg_force ? UNIT_FILE_FORCE : 0);
}
-int mangle_names(const char *operation, char **original_names, char ***ret_mangled_names) {
+int mangle_names(const char *operation, char * const *original_names, char ***ret) {
_cleanup_strv_free_ char **l = NULL;
- char **i;
int r;
- assert(ret_mangled_names);
-
- l = i = new(char*, strv_length(original_names) + 1);
- if (!l)
- return log_oom();
+ assert(operation);
+ assert(ret);
STRV_FOREACH(name, original_names) {
-
- /* When enabling units qualified path names are OK, too, hence allow them explicitly. */
+ char *mangled;
if (is_path(*name))
- r = path_make_absolute_cwd(*name, i);
+ /* When enabling units qualified path names are OK, too, hence allow them explicitly. */
+ r = path_make_absolute_cwd(*name, &mangled);
else
r = unit_name_mangle_with_suffix(*name, operation,
arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
- ".service", i);
- if (r < 0) {
- *i = NULL;
+ ".service", &mangled);
+ if (r < 0)
return log_error_errno(r, "Failed to mangle unit name or path '%s': %m", *name);
- }
- i++;
+ if (strv_consume(&l, mangled) < 0)
+ return log_oom();
}
- *i = NULL;
- *ret_mangled_names = TAKE_PTR(l);
+ *ret = TAKE_PTR(l);
return 0;
}
@@ -994,3 +987,149 @@ int halt_now(enum action a) {
assert_not_reached();
}
}
+
+int get_unit_by_pid(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int r;
+
+ assert(bus);
+ assert(pid >= 0); /* 0 is accepted by GetUnitByPID for querying our own process. */
+
+ r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", (uint32_t) pid);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_UNIT_FOR_PID))
+ return log_error_errno(r, "%s", bus_error_message(&error, r));
+
+ return log_error_errno(r,
+ "Failed to get unit that PID " PID_FMT " belongs to: %s",
+ pid > 0 ? pid : getpid_cached(),
+ bus_error_message(&error, r));
+ }
+
+ _cleanup_free_ char *u = NULL, *p = NULL;
+ const char *path;
+
+ r = sd_bus_message_read_basic(reply, 'o', &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (ret_unit) {
+ r = unit_name_from_dbus_path(path, &u);
+ if (r < 0)
+ return log_error_errno(r,
+ "Failed to extract unit name from D-Bus object path '%s': %m",
+ path);
+ }
+
+ if (ret_path) {
+ p = strdup(path);
+ if (!p)
+ return log_oom();
+ }
+
+ if (ret_unit)
+ *ret_unit = TAKE_PTR(u);
+ if (ret_path)
+ *ret_path = TAKE_PTR(p);
+
+ return 0;
+}
+
+static int get_unit_by_pidfd(sd_bus *bus, const PidRef *pid, char **ret_unit, char **ret_path) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int r;
+
+ assert(bus);
+ assert(pidref_is_set(pid));
+
+ if (pid->fd < 0)
+ return -EOPNOTSUPP;
+
+ r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPIDFD", &error, &reply, "h", pid->fd);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
+ return -EOPNOTSUPP;
+
+ if (sd_bus_error_has_names(&error, BUS_ERROR_NO_UNIT_FOR_PID, BUS_ERROR_NO_SUCH_PROCESS))
+ return log_error_errno(r, "%s", bus_error_message(&error, r));
+
+ return log_error_errno(r,
+ "Failed to get unit that PID " PID_FMT " belongs to: %s",
+ pid->pid, bus_error_message(&error, r));
+ }
+
+ _cleanup_free_ char *u = NULL, *p = NULL;
+ const char *path, *unit;
+
+ r = sd_bus_message_read(reply, "os", &path, &unit);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (ret_unit) {
+ u = strdup(unit);
+ if (!u)
+ return log_oom();
+ }
+
+ if (ret_path) {
+ p = strdup(path);
+ if (!p)
+ return log_oom();
+ }
+
+ if (ret_unit)
+ *ret_unit = TAKE_PTR(u);
+ if (ret_path)
+ *ret_path = TAKE_PTR(p);
+
+ return 0;
+}
+
+int lookup_unit_by_pidref(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path) {
+ int r;
+
+ assert(bus);
+ assert(pid >= 0); /* 0 means our own process */
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
+ return get_unit_by_pid(bus, pid, ret_unit, ret_path);
+
+ static bool use_pidfd = true;
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+
+ r = pidref_set_pid(&pidref, pid);
+ if (r < 0)
+ return log_error_errno(r,
+ r == -ESRCH ?
+ "PID " PID_FMT " doesn't exist or is already gone." :
+ "Failed to create reference to PID " PID_FMT ": %m",
+ pid);
+
+ if (use_pidfd) {
+ r = get_unit_by_pidfd(bus, &pidref, ret_unit, ret_path);
+ if (r != -EOPNOTSUPP)
+ return r;
+
+ use_pidfd = false;
+ log_debug_errno(r, "Unable to look up process using pidfd, falling back to pid.");
+ }
+
+ _cleanup_free_ char *u = NULL, *p = NULL;
+
+ r = get_unit_by_pid(bus, pidref.pid, ret_unit ? &u : NULL, ret_path ? &p : NULL);
+ if (r < 0)
+ return r;
+
+ r = pidref_verify(&pidref);
+ if (r < 0)
+ return log_error_errno(r, "Failed to verify our reference to PID " PID_FMT ": %m", pidref.pid);
+
+ if (ret_unit)
+ *ret_unit = TAKE_PTR(u);
+ if (ret_path)
+ *ret_path = TAKE_PTR(p);
+
+ return 0;
+}
diff --git a/src/systemctl/systemctl-util.h b/src/systemctl/systemctl-util.h
index 7bddef0..a950dde 100644
--- a/src/systemctl/systemctl-util.h
+++ b/src/systemctl/systemctl-util.h
@@ -24,7 +24,7 @@ int translate_bus_error_to_exit_status(int r, const sd_bus_error *error);
int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_active_state);
int get_sub_state_one_unit(sd_bus *bus, const char *unit, char **ret_sub_state);
int get_unit_list(sd_bus *bus, const char *machine, char **patterns, UnitInfo **unit_infos, int c, sd_bus_message **ret_reply);
-int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded);
+int expand_unit_names(sd_bus *bus, char * const *names, const char* suffix, char ***ret, bool *ret_expanded);
int get_active_triggering_units(sd_bus *bus, const char *unit, bool ignore_masked, char ***ret);
void warn_triggering_units(sd_bus *bus, const char *unit, const char *operation, bool ignore_masked);
@@ -53,8 +53,11 @@ int output_table(Table *table);
bool show_preset_for_state(UnitFileState state);
-int mangle_names(const char *operation, char **original_names, char ***ret_mangled_names);
+int mangle_names(const char *operation, char * const *original_names, char ***ret);
UnitFileFlags unit_file_flags_from_args(void);
int halt_now(enum action a);
+
+int get_unit_by_pid(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path);
+int lookup_unit_by_pidref(sd_bus *bus, pid_t pid, char **ret_unit, char **ret_path);
diff --git a/src/systemctl/systemctl-whoami.c b/src/systemctl/systemctl-whoami.c
index 4ee6592..607f2db 100644
--- a/src/systemctl/systemctl-whoami.c
+++ b/src/systemctl/systemctl-whoami.c
@@ -1,70 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include "bus-error.h"
-#include "bus-locator.h"
+#include "format-util.h"
+#include "parse-util.h"
#include "systemctl.h"
#include "systemctl-util.h"
#include "systemctl-whoami.h"
-#include "parse-util.h"
-
-static int lookup_pid(sd_bus *bus, pid_t pid) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *unit = NULL;
- const char *path;
- int r;
-
- r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", (uint32_t) pid);
- if (r < 0)
- return log_error_errno(r, "Failed to get unit for ourselves: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = unit_name_from_dbus_path(path, &unit);
- if (r < 0)
- return log_error_errno(r, "Failed to extract unit name from D-Bus object path '%s': %m", path);
-
- printf("%s\n", unit);
- return 0;
-}
int verb_whoami(int argc, char *argv[], void *userdata) {
sd_bus *bus;
- int r;
+ int r, ret = 0;
r = acquire_bus(BUS_FULL, &bus);
if (r < 0)
return r;
- char **pids = strv_skip(argv, 1);
-
- if (strv_isempty(pids)) {
+ if (argc <= 1) {
+ _cleanup_free_ char *unit = NULL;
if (arg_transport != BUS_TRANSPORT_LOCAL)
- return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Refusing to look up local PID on remote host.");
+ return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
+ "Refusing to look up our local PID on remote host.");
- return lookup_pid(bus, 0);
- } else {
- int ret = 0;
+ /* Our own process can never go away while querying, hence no need to go through pidfd. */
- STRV_FOREACH(p, pids) {
- pid_t pid;
+ r = get_unit_by_pid(bus, 0, &unit, /* ret_path = */ NULL);
+ if (r < 0)
+ return r;
- r = parse_pid(*p, &pid);
- if (r < 0) {
- log_error_errno(r, "Failed to parse PID: %s", *p);
- if (ret >= 0)
- ret = r;
- continue;
- }
+ puts(unit);
+ return 0;
+ }
+
+ STRV_FOREACH(pidstr, strv_skip(argv, 1)) {
+ _cleanup_free_ char *unit = NULL;
+ pid_t pid;
- r = lookup_pid(bus, pid);
- if (r < 0 && ret >= 0)
- ret = r;
- }
+ r = parse_pid(*pidstr, &pid);
+ if (r < 0)
+ return log_error_errno(r, "Invalid PID specified: %s", *pidstr);
- return ret;
+ r = lookup_unit_by_pidref(bus, pid, &unit, /* ret_path = */ NULL);
+ if (r < 0)
+ RET_GATHER(ret, r);
+ else
+ puts(unit);
}
+
+ return ret;
}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index dd6f6c9..0ca76ac 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -8,6 +8,7 @@
#include "build.h"
#include "bus-util.h"
+#include "capsule-util.h"
#include "dissect-image.h"
#include "install.h"
#include "main-func.h"
@@ -63,6 +64,7 @@
#include "systemctl.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "user-util.h"
#include "verbs.h"
#include "virt.h"
@@ -103,6 +105,7 @@ bool arg_kill_value_set = false;
char *arg_root = NULL;
char *arg_image = NULL;
usec_t arg_when = 0;
+bool arg_stdin = false;
const char *arg_reboot_argument = NULL;
enum action arg_action = ACTION_SYSTEMCTL;
BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
@@ -249,6 +252,8 @@ static int systemctl_help(void) {
" soft-reboot Shut down and reboot userspace\n"
" exit [EXIT_CODE] Request user instance or container exit\n"
" switch-root [ROOT [INIT]] Change to a different root file system\n"
+ " sleep Put the system to sleep (through one of\n"
+ " the operations below)\n"
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
" hybrid-sleep Hibernate and suspend the system\n"
@@ -259,6 +264,7 @@ static int systemctl_help(void) {
" --version Show package version\n"
" --system Connect to system manager\n"
" --user Connect to user service manager\n"
+ " -C --capsule=NAME Connect to service manager of specified capsule\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on a local container\n"
" -t --type=TYPE List units of a particular type\n"
@@ -272,6 +278,8 @@ static int systemctl_help(void) {
" -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"
+ " --before Show units ordered before with 'list-dependencies'\n"
+ " --after Show units ordered after with 'list-dependencies'\n"
" --with-dependencies Show unit dependencies with 'status', 'cat',\n"
" 'list-units', and 'list-unit-files'.\n"
" --job-mode=MODE Specify how to deal with already queued jobs, when\n"
@@ -297,8 +305,10 @@ static int systemctl_help(void) {
" --no-warn Suppress several warnings shown by default\n"
" --wait For (re)start, wait until service stopped again\n"
" For is-system-running, wait until startup is completed\n"
+ " For kill, wait until service stopped\n"
" --no-block Do not wait until operation finished\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
+ " --message=MESSAGE Specify human readable reason for system shutdown\n"
" --no-reload Don't reload daemon after en-/dis-abling unit files\n"
" --legend=BOOL Enable/disable the legend (column headers and hints)\n"
" --no-pager Do not pipe output into a pager\n"
@@ -326,6 +336,8 @@ static int systemctl_help(void) {
" 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"
+ " --reboot-argument=ARG\n"
+ " Specify argument string to pass to reboot()\n"
" --plain Print unit dependencies as a list instead of a tree\n"
" --timestamp=FORMAT Change format of printed timestamps (pretty, unix,\n"
" us, utc, us+utc)\n"
@@ -335,6 +347,7 @@ static int systemctl_help(void) {
" --drop-in=NAME Edit unit files using the specified drop-in file name\n"
" --when=TIME Schedule halt/power-off/reboot/kexec action after\n"
" a certain timestamp\n"
+ " --stdin Read contents of edited file from stdin\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -461,6 +474,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_NO_WARN,
ARG_DROP_IN,
ARG_WHEN,
+ ARG_STDIN,
};
static const struct option options[] = {
@@ -485,6 +499,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL },
+ { "capsule", required_argument, NULL, 'C' },
{ "wait", no_argument, NULL, ARG_WAIT },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
{ "legend", required_argument, NULL, ARG_LEGEND },
@@ -527,6 +542,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "marked", no_argument, NULL, ARG_MARKED },
{ "drop-in", required_argument, NULL, ARG_DROP_IN },
{ "when", required_argument, NULL, ARG_WHEN },
+ { "stdin", no_argument, NULL, ARG_STDIN },
{}
};
@@ -538,7 +554,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
/* We default to allowing interactive authorization only in systemctl (not in the legacy commands) */
arg_ask_password = true;
- while ((c = getopt_long(argc, argv, "ht:p:P:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hC:t:p:P:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0)
switch (c) {
@@ -673,6 +689,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_runtime_scope = RUNTIME_SCOPE_GLOBAL;
break;
+ case 'C':
+ r = capsule_name_is_valid(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Unable to validate capsule name '%s': %m", optarg);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid capsule name: %s", optarg);
+
+ arg_host = optarg;
+ arg_transport = BUS_TRANSPORT_CAPSULE;
+ arg_runtime_scope = RUNTIME_SCOPE_USER;
+ break;
+
case ARG_WAIT:
arg_wait = true;
break;
@@ -1017,6 +1045,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
+ case ARG_STDIN:
+ arg_stdin = true;
+ break;
+
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);
@@ -1067,7 +1099,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
if (arg_image && arg_root)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Please specify either --root= or --image=, the combination of both is not supported.");
return 1;
}
@@ -1107,15 +1140,8 @@ int systemctl_dispatch_parse_argv(int argc, char *argv[]) {
*
* Also see redirect_telinit() in src/core/main.c. */
- if (sd_booted() > 0) {
- arg_action = _ACTION_INVALID;
- return telinit_parse_argv(argc, argv);
- } else {
- /* Hmm, so some other init system is running, we need to forward this request to it.
- */
- arg_action = ACTION_TELINIT;
- return 1;
- }
+ arg_action = _ACTION_INVALID; /* telinit_parse_argv() will figure out the actual action we'll execute */
+ return telinit_parse_argv(argc, argv);
} else if (invoked_as(argv, "runlevel")) {
arg_action = ACTION_RUNLEVEL;
@@ -1179,6 +1205,7 @@ static int systemctl_main(int argc, char *argv[]) {
{ "reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "soft-reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
+ { "sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
@@ -1265,7 +1292,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_VALIDATE_OS,
+ DISSECT_IMAGE_VALIDATE_OS |
+ DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);
@@ -1318,11 +1346,8 @@ static int run(int argc, char *argv[]) {
r = runlevel_main();
break;
- case ACTION_TELINIT:
- r = exec_telinit(argv);
- break;
-
case ACTION_EXIT:
+ case ACTION_SLEEP:
case ACTION_SUSPEND:
case ACTION_HIBERNATE:
case ACTION_HYBRID_SLEEP:
diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h
index e8ba8f7..cc2b8c2 100644
--- a/src/systemctl/systemctl.h
+++ b/src/systemctl/systemctl.h
@@ -18,6 +18,7 @@ enum action {
ACTION_KEXEC,
ACTION_SOFT_REBOOT,
ACTION_EXIT,
+ ACTION_SLEEP,
ACTION_SUSPEND,
ACTION_HIBERNATE,
ACTION_HYBRID_SLEEP,
@@ -32,7 +33,6 @@ enum action {
ACTION_RELOAD,
ACTION_REEXEC,
ACTION_RUNLEVEL,
- ACTION_TELINIT,
ACTION_CANCEL_SHUTDOWN,
ACTION_SHOW_SHUTDOWN,
_ACTION_MAX,
@@ -83,6 +83,7 @@ extern int arg_kill_value;
extern bool arg_kill_value_set;
extern char *arg_root;
extern usec_t arg_when;
+extern bool arg_stdin;
extern const char *arg_reboot_argument;
extern enum action arg_action;
extern BusTransport arg_transport;