diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 03:50:40 +0000 |
commit | fc53809803cd2bc2434e312b19a18fa36776da12 (patch) | |
tree | b4b43bd6538f51965ce32856e9c053d0f90919c8 /src/systemctl | |
parent | Adding upstream version 255.5. (diff) | |
download | systemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip |
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/systemctl')
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; |