diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 5ebb093082..4f8936e866 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -209,6 +209,10 @@ node /org/freedesktop/systemd1 { DisableUnitFilesWithFlags(in as files, in t flags, out a(sss) changes); + DisableUnitFilesWithFlagsAndInstallInfo(in as files, + in t flags, + out b carries_install_info, + out a(sss) changes); ReenableUnitFiles(in as files, in b runtime, in b force, @@ -916,6 +920,8 @@ node /org/freedesktop/systemd1 { + + @@ -1417,7 +1423,7 @@ node /org/freedesktop/systemd1 { enabled for runtime only (true, /run/), or persistently (false, /etc/). The second one controls whether symlinks pointing to other units shall be replaced if necessary. This method returns one boolean and an array of the changes made. The boolean - signals whether the unit files contained any enablement information (i.e. an [Install]) section. The + signals whether the unit files contained any enablement information (i.e. an [Install] section). The changes array consists of structures with three strings: the type of the change (one of symlink or unlink), the file name of the symlink and the destination of the symlink. Note that most of the following calls return a changes list in the same @@ -1440,6 +1446,11 @@ node /org/freedesktop/systemd1 { replaced if necessary. SD_SYSTEMD_UNIT_PORTABLE will add or remove the symlinks in /etc/systemd/system.attached and /run/systemd/system.attached. + DisableUnitFilesWithFlagsAndInstallInfo() is similar to + DisableUnitFilesWithFlags() and takes the same arguments, but returns + a boolean to indicate whether the unit files contain any enablement information, like + EnableUnitFiles(). The changes made are still returned in an array. + Similarly, ReenableUnitFiles() applies the changes to one or more units that would result from disabling and enabling the unit quickly one after the other in an atomic fashion. This is useful to apply updated [Install] information contained in unit files. diff --git a/man/systemctl.xml b/man/systemctl.xml index 1d91c8a726..d6deb6d78a 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -771,6 +771,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err account. + When using this operation on units without install information, a warning about it is shown. + can be used to suppress the warning. + Enabling units should not be confused with starting (activating) units, as done by the start command. Enabling and starting units is orthogonal: units may be enabled without being started and started without being enabled. Enabling simply hooks the unit into various suggested @@ -814,8 +817,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err executed. This output may be suppressed by passing . - This command honors , , - and in a similar way as enable. + This command honors , , , + and in a similar way as enable. @@ -1997,6 +2000,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + + Don't generate the warning shown by default when using + enable or disable on units + without install information (i.e. don't have or have an empty + [Install] section). + + + diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index ab2617153a..d2e7372606 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2425,6 +2425,7 @@ static int method_disable_unit_files_generic( sd_bus_message *message, Manager *m, int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], InstallChange **changes, size_t *n_changes), + bool carries_install_info, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; @@ -2440,7 +2441,8 @@ static int method_disable_unit_files_generic( if (r < 0) return r; - if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags")) { + if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags") || + sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlagsAndInstallInfo")) { uint64_t raw_flags; r = sd_bus_message_read(message, "t", &raw_flags); @@ -2469,19 +2471,23 @@ static int method_disable_unit_files_generic( if (r < 0) return install_error(error, r, changes, n_changes); - return reply_install_changes_and_free(m, message, -1, changes, n_changes, error); + return reply_install_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes, error); } static int method_disable_unit_files_with_flags(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, unit_file_disable, error); + return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ false, error); +} + +static int method_disable_unit_files_with_flags_and_install_info(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ true, error); } static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, unit_file_disable, error); + return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ false, error); } static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error); + return method_disable_unit_files_generic(message, userdata, unit_file_unmask, /* carries_install_info = */ false, error); } static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -3194,6 +3200,11 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_RESULT("a(sss)", changes), method_disable_unit_files_with_flags, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("DisableUnitFilesWithFlagsAndInstallInfo", + SD_BUS_ARGS("as", files, "t", flags), + SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes), + method_disable_unit_files_with_flags_and_install_info, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("ReenableUnitFiles", SD_BUS_ARGS("as", files, "b", runtime, "b", force), SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes), diff --git a/src/rpm/systemd-update-helper.in b/src/rpm/systemd-update-helper.in index ab8cdc0ff9..b35d952fab 100755 --- a/src/rpm/systemd-update-helper.in +++ b/src/rpm/systemd-update-helper.in @@ -19,21 +19,21 @@ case "$command" in remove-system-units) if [ -d /run/systemd/system ]; then - systemctl --no-reload disable --now "$@" + systemctl --no-reload disable --now --no-warn "$@" else - systemctl --no-reload disable "$@" + systemctl --no-reload disable --no-warn "$@" fi ;; remove-user-units) - systemctl --global disable "$@" + systemctl --global disable --no-warn "$@" [ -d /run/systemd/system ] || exit 0 users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p') for user in $users; do SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \ - systemctl --user -M "$user@" disable --now "$@" & + systemctl --user -M "$user@" disable --now --no-warn "$@" & done wait ;; diff --git a/src/shared/install.c b/src/shared/install.c index 51aa60bb52..c38ba1bd7a 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -2790,25 +2790,38 @@ static int do_unit_file_disable( _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + InstallInfo *info; + bool has_install_info = false; int r; STRV_FOREACH(name, names) { if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) return install_changes_add(changes, n_changes, -EUCLEAN, *name, NULL); - r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, NULL); + r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, &info); + if (r >= 0) + r = install_info_traverse(&ctx, lp, info, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + if (r < 0) - return r; + return install_changes_add(changes, n_changes, r, *name, NULL); + + /* If we enable multiple units, some with install info and others without, + * the "empty [Install] section" warning is not shown. Let's make the behavior + * of disable align with that. */ + has_install_info = has_install_info || install_info_has_rules(info) || install_info_has_also(info); } r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes); + if (r >= 0) + r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes); + if (r < 0) return r; - return remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes); + /* The warning is shown only if it's a no-op */ + return install_changes_have_modification(*changes, *n_changes) || has_install_info; } - int unit_file_disable( LookupScope scope, UnitFileFlags flags, diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c index 5be4c0c725..86d9f602fa 100644 --- a/src/systemctl/systemctl-enable.c +++ b/src/systemctl/systemctl-enable.c @@ -67,7 +67,7 @@ int verb_enable(int argc, char *argv[], void *userdata) { InstallChange *changes = NULL; size_t n_changes = 0; int carries_install_info = -1; - bool ignore_carries_install_info = arg_quiet; + bool ignore_carries_install_info = arg_quiet || arg_no_warn; int r; if (!argv[1]) @@ -109,9 +109,10 @@ int verb_enable(int argc, char *argv[], void *userdata) { if (streq(verb, "enable")) { r = unit_file_enable(arg_scope, flags, arg_root, names, &changes, &n_changes); carries_install_info = r; - } else if (streq(verb, "disable")) + } else if (streq(verb, "disable")) { r = unit_file_disable(arg_scope, flags, arg_root, names, &changes, &n_changes); - else if (streq(verb, "reenable")) { + carries_install_info = r; + } else if (streq(verb, "reenable")) { r = unit_file_reenable(arg_scope, flags, arg_root, names, &changes, &n_changes); carries_install_info = r; } else if (streq(verb, "link")) @@ -165,7 +166,8 @@ int verb_enable(int argc, char *argv[], void *userdata) { method = "EnableUnitFiles"; expect_carries_install_info = true; } else if (streq(verb, "disable")) { - method = "DisableUnitFiles"; + method = "DisableUnitFilesWithFlagsAndInstallInfo"; + expect_carries_install_info = true; send_force = false; } else if (streq(verb, "reenable")) { method = "ReenableUnitFiles"; @@ -208,7 +210,10 @@ int verb_enable(int argc, char *argv[], void *userdata) { } if (send_runtime) { - r = sd_bus_message_append(m, "b", arg_runtime); + if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo")) + r = sd_bus_message_append(m, "t", arg_runtime ? UNIT_FILE_RUNTIME : 0); + else + r = sd_bus_message_append(m, "b", arg_runtime); if (r < 0) return bus_log_create_error(r); } @@ -245,7 +250,7 @@ int verb_enable(int argc, char *argv[], void *userdata) { if (carries_install_info == 0 && !ignore_carries_install_info) log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n" "Alias= settings in the [Install] section, and DefaultInstance= for template\n" - "units). This means they are not meant to be enabled using systemctl.\n" + "units). This means they are not meant to be enabled or disabled using systemctl.\n" " \n" /* trick: the space is needed so that the line does not get stripped from output */ "Possible reasons for having this kind of units are:\n" "%1$s A unit may be statically enabled by being symlinked from another unit's\n" diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4f2637f0f1..0a12bcebc3 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -84,6 +84,7 @@ bool arg_show_types = false; int arg_check_inhibitors = -1; bool arg_dry_run = false; bool arg_quiet = false; +bool arg_no_warn = false; bool arg_full = false; bool arg_recursive = false; bool arg_with_dependencies = false; @@ -277,6 +278,8 @@ static int systemctl_help(void) { " kexec, suspend, hibernate, suspend-then-hibernate,\n" " hybrid-sleep, default, rescue, emergency, and exit.\n" " -q --quiet Suppress output\n" + " --no-warn Don't generate warning when trying to enable/disable\n" + " units without install information\n" " --wait For (re)start, wait until service stopped again\n" " For is-system-running, wait until startup is completed\n" " --no-block Do not wait until operation finished\n" @@ -433,6 +436,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_READ_ONLY, ARG_MKDIR, ARG_MARKED, + ARG_NO_WARN, }; static const struct option options[] = { @@ -465,6 +469,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "no-wall", no_argument, NULL, ARG_NO_WALL }, { "dry-run", no_argument, NULL, ARG_DRY_RUN }, { "quiet", no_argument, NULL, 'q' }, + { "no-warn", no_argument, NULL, ARG_NO_WARN }, { "root", required_argument, NULL, ARG_ROOT }, { "image", required_argument, NULL, ARG_IMAGE }, { "force", no_argument, NULL, 'f' }, @@ -926,6 +931,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_marked = true; break; + case ARG_NO_WARN: + arg_no_warn = true; + break; + case '.': /* Output an error mimicking getopt, and print a hint afterwards */ log_error("%s: invalid option -- '.'", program_invocation_name); diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index 2454c4c714..1a7a6e28d3 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -65,6 +65,7 @@ extern bool arg_show_types; extern int arg_check_inhibitors; extern bool arg_dry_run; extern bool arg_quiet; +extern bool arg_no_warn; extern bool arg_full; extern bool arg_recursive; extern bool arg_with_dependencies;