diff options
author | Luca Boccassi <bluca@debian.org> | 2024-02-09 15:29:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-09 15:29:50 +0100 |
commit | c5052754761c78b89e20d1f9c3396529dbab26de (patch) | |
tree | e66a8102d9c1ad7cebe860ec7cd9f7e479b68631 | |
parent | Merge pull request #31224 from mrc0mmand/packit-bpftool-workaround (diff) | |
parent | systemctl: allow --now only if not install_client_side() (diff) | |
download | systemd-c5052754761c78b89e20d1f9c3396529dbab26de.tar.xz systemd-c5052754761c78b89e20d1f9c3396529dbab26de.zip |
Merge pull request #31243 from YHNdnzj/systemctl-disable-now-template
systemctl: support disable/mask --now with unit template
-rw-r--r-- | src/basic/unit-name.c | 41 | ||||
-rw-r--r-- | src/basic/unit-name.h | 11 | ||||
-rw-r--r-- | src/systemctl/systemctl-enable.c | 56 | ||||
-rw-r--r-- | src/systemctl/systemctl-util.c | 52 | ||||
-rw-r--r-- | src/systemctl/systemctl-util.h | 4 | ||||
-rwxr-xr-x | test/units/testsuite-26.sh | 18 |
6 files changed, 117 insertions, 65 deletions
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 8bf28ba749..06ed9054d0 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -454,34 +454,45 @@ int unit_name_path_unescape(const char *f, char **ret) { return 0; } -int unit_name_replace_instance(const char *f, const char *i, char **ret) { +int unit_name_replace_instance_full( + const char *original, + const char *instance, + bool accept_glob, + char **ret) { + _cleanup_free_ char *s = NULL; - const char *p, *e; - size_t a, b; + const char *prefix, *suffix; + size_t pl; - assert(f); - assert(i); + assert(original); + assert(instance); assert(ret); - if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + if (!unit_name_is_valid(original, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) return -EINVAL; - if (!unit_instance_is_valid(i)) + if (!unit_instance_is_valid(instance) && !(accept_glob && in_charset(instance, VALID_CHARS_GLOB))) return -EINVAL; - assert_se(p = strchr(f, '@')); - assert_se(e = strrchr(f, '.')); + prefix = ASSERT_PTR(strchr(original, '@')); + suffix = ASSERT_PTR(strrchr(original, '.')); + assert(prefix < suffix); - a = p - f; - b = strlen(i); + pl = prefix - original + 1; /* include '@' */ - s = new(char, a + 1 + b + strlen(e) + 1); + s = new(char, pl + strlen(instance) + strlen(suffix) + 1); if (!s) return -ENOMEM; - strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); +#if HAS_FEATURE_MEMORY_SANITIZER + /* MSan doesn't like stpncpy... See also https://github.com/google/sanitizers/issues/926 */ + memzero(s, pl + strlen(instance) + strlen(suffix) + 1); +#endif - /* Make sure the resulting name still is valid, i.e. didn't grow too large */ - if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE)) + strcpy(stpcpy(stpncpy(s, original, pl), instance), suffix); + + /* Make sure the resulting name still is valid, i.e. didn't grow too large. Globs will be expanded + * by clients when used, so the check is pointless. */ + if (!accept_glob && !unit_name_is_valid(s, UNIT_NAME_INSTANCE)) return -EINVAL; *ret = TAKE_PTR(s); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index eaa701e9f6..fa7295e250 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -33,14 +33,21 @@ UnitType unit_name_to_type(const char *n) _pure_; int unit_name_change_suffix(const char *n, const char *suffix, char **ret); int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret); -int unit_name_build_from_type(const char *prefix, const char *instance, UnitType, char **ret); +int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret); char *unit_name_escape(const char *f); int unit_name_unescape(const char *f, char **ret); int unit_name_path_escape(const char *f, char **ret); int unit_name_path_unescape(const char *f, char **ret); -int unit_name_replace_instance(const char *f, const char *i, char **ret); +int unit_name_replace_instance_full( + const char *original, + const char *instance, + bool accept_glob, + char **ret); +static inline int unit_name_replace_instance(const char *original, const char *instance, char **ret) { + return unit_name_replace_instance_full(original, instance, false, ret); +} int unit_name_template(const char *f, char **ret); diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c index 7d9b7c794a..1904ecf949 100644 --- a/src/systemctl/systemctl-enable.c +++ b/src/systemctl/systemctl-enable.c @@ -66,6 +66,7 @@ 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]) @@ -140,7 +141,6 @@ 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 = {}; @@ -312,25 +312,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 seldomly 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-util.c b/src/systemctl/systemctl-util.c index d3668dd12e..f37401d917 100644 --- a/src/systemctl/systemctl-util.c +++ b/src/systemctl/systemctl-util.c @@ -260,7 +260,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 +294,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; } @@ -921,37 +917,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; } diff --git a/src/systemctl/systemctl-util.h b/src/systemctl/systemctl-util.h index 7bddef07ef..17975e110d 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,7 +53,7 @@ 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); diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh index 9f672c738a..910e7531b1 100755 --- a/test/units/testsuite-26.sh +++ b/test/units/testsuite-26.sh @@ -207,6 +207,24 @@ test_mask_unmask_revert() { test_mask_unmask_revert test_mask_unmask_revert --root=/ +# disable --now with template unit +cat >/run/systemd/system/test-disable@.service <<EOF +[Service] +ExecStart=sleep infinity + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable --now test-disable@1.service test-disable@2.service +systemctl is-active test-disable@1.service +systemctl is-active test-disable@2.service +systemctl disable --now test-disable@.service +for u in test-disable@{1,2}.service; do + (! systemctl is-active "$u") + (! systemctl is-enabled "$u") +done +rm /run/systemd/system/test-disable@.service + # add-wants/add-requires (! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service") systemctl add-wants "$UNIT_NAME" "systemd-journald.service" |