summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuca Boccassi <bluca@debian.org>2024-02-09 15:29:50 +0100
committerGitHub <noreply@github.com>2024-02-09 15:29:50 +0100
commitc5052754761c78b89e20d1f9c3396529dbab26de (patch)
treee66a8102d9c1ad7cebe860ec7cd9f7e479b68631
parentMerge pull request #31224 from mrc0mmand/packit-bpftool-workaround (diff)
parentsystemctl: allow --now only if not install_client_side() (diff)
downloadsystemd-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.c41
-rw-r--r--src/basic/unit-name.h11
-rw-r--r--src/systemctl/systemctl-enable.c56
-rw-r--r--src/systemctl/systemctl-util.c52
-rw-r--r--src/systemctl/systemctl-util.h4
-rwxr-xr-xtest/units/testsuite-26.sh18
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"