systemctl-enable: support reenable --now

Also, instead of silently continuing when template units
are specified with enable --now, print a warning and skip them.

Closes #31541
This commit is contained in:
Mike Yuan
2024-07-09 16:46:13 +02:00
parent 5ed6cc7ca9
commit 98aedce914
2 changed files with 51 additions and 19 deletions
+8 -5
View File
@@ -973,6 +973,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
enabled with to the defaults configured in its [Install] section. This command expects
a unit name only, it does not accept paths to unit files.</para>
<para>This command implicitly reloads the system manager configuration after completing the operation.
Note that this command does not implicitly restart the units that are being disabled. If this is
desired, either combine this command with the <option>--now</option> switch, or invoke
the <command>try-restart</command> command with appropriate arguments later.</para>
<xi:include href="version-info.xml" xpointer="v238"/>
</listitem>
</varlistentry>
@@ -2583,11 +2588,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<term><option>--now</option></term>
<listitem>
<para>When used with <command>enable</command>, the units
will also be started. When used with <command>disable</command> or
<command>mask</command>, the units will also be stopped. The start
or stop operation is only carried out when the respective enable or
disable operation has been successful.</para>
<para>When used with <command>enable</command>, <command>disable</command>, <command>mask</command>,
or <command>reenable</command>, also start/stop/try-restart the units after the specified
unit file operations succeed.</para>
<xi:include href="version-info.xml" xpointer="v220"/>
</listitem>
+43 -14
View File
@@ -316,10 +316,26 @@ int verb_enable(int argc, char *argv[], void *userdata) {
if (arg_now) {
_cleanup_strv_free_ char **new_args = NULL;
const char *start_verb;
bool accept_path, prohibit_templates;
if (!STR_IN_SET(verb, "enable", "disable", "mask"))
if (streq(verb, "enable")) {
start_verb = "start";
accept_path = true;
prohibit_templates = true;
} else if (STR_IN_SET(verb, "disable", "mask")) {
start_verb = "stop";
accept_path = false;
prohibit_templates = false;
} else if (streq(verb, "reenable")) {
/* Note that we use try-restart here. This matches the semantics of reenable better,
* and allows us to glob template units. */
start_verb = "try-restart";
accept_path = true;
prohibit_templates = false;
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--now can only be used with verb enable, disable, or mask.");
"--now can only be used with verb enable, disable, reenable, or mask.");
if (install_client_side())
return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
@@ -327,33 +343,46 @@ int verb_enable(int argc, char *argv[], void *userdata) {
assert(bus);
if (strv_extend(&new_args, streq(verb, "enable") ? "start" : "stop") < 0)
if (strv_extend(&new_args, start_verb) < 0)
return log_oom();
STRV_FOREACH(name, names) {
if (streq(verb, "enable")) {
char *fn;
_cleanup_free_ char *fn = NULL;
const char *unit_name;
/* '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). */
if (accept_path) {
/* 'enable' and 'reenable' accept path to unit files, so extract it first. */
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)) {
unit_name = fn;
} else
unit_name = *name;
if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
char *globbed;
r = unit_name_replace_instance_full(*name, "*", /* accept_glob = */ true, &globbed);
if (prohibit_templates) {
/* Skip template units when enabling. Globbing doesn't make sense
* since the semantics would be altered (we're operating on
* DefaultInstance= when enabling), and starting template unit
* is not supported anyway. */
log_warning("Template unit is not supported by %s --now, skipping: %s",
verb, unit_name);
continue;
}
assert(!STR_IN_SET(start_verb, "start", "restart"));
r = unit_name_replace_instance_full(unit_name, "*", /* accept_glob = */ true, &globbed);
if (r < 0)
return log_error_errno(r, "Failed to glob unit name '%s': %m", *name);
return log_error_errno(r, "Failed to glob unit name '%s': %m", unit_name);
r = strv_consume(&new_args, globbed);
} else
r = strv_extend(&new_args, *name);
r = strv_extend(&new_args, unit_name);
if (r < 0)
return log_oom();
}