diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-08-26 12:02:43 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2022-10-13 19:44:47 +0200 |
commit | bf3b0d5f29d769fc248808d323ee95b23eae86df (patch) | |
tree | 6ceac07c732455b4e262a0a0d08fdb69a32c0f21 /src/shared/install.c | |
parent | acb5b834381c7938c371b0b2a912bd8c420c4a4d (diff) | |
download | systemd-bf3b0d5f29d769fc248808d323ee95b23eae86df.tar.gz |
shared/install: print warning when unmasking unit with cmdline mask
'systemctl unmask foo' will try to remove the symlink to /dev/null under /etc/.
But the unit may also be masked by a symlink under /run/generator, in particular
the one created by systemd-debug-generator based on systemd.mask=foo on the
kernel commandline. The unmask call cannot anything about this: even if it removed
the symlink from /run/generator, it'll be recreated on the next daemon-reload.
Thus, we can only warn about it.
Initially, I wanted to check if 'systemctl.mask' is defined on the kernel
command-line, but that's not effective, because such mask symlinks can be
created by other generators based on other conditions. Checking for runtime
mask is "dumber", but is more robust because it doesn't assume who created the
mask and why.
The handling of InstallInfo is the copied from install_info_symlink_wants().
It's pretty ugly, this whole code should be rewritten from scratch.
The message is printed, but the whole operation is still "successful". This
keep backwards compatibility: people might call unmask to remove filesystem
masks even if there's still a cmdline param in place. We allow 'systemctl
mask' to create such a mask, so 'unmask' should be able to remove it.
Fixes #22689.
Diffstat (limited to 'src/shared/install.c')
-rw-r--r-- | src/shared/install.c | 32 |
1 files changed, 29 insertions, 3 deletions
diff --git a/src/shared/install.c b/src/shared/install.c index 7cf73fe0aa..e45ad01092 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -352,6 +352,11 @@ void install_changes_dump(int r, const char *verb, const InstallChange *changes, if (!quiet) log_info("Unit %s is masked, ignoring.", changes[i].path); break; + case INSTALL_CHANGE_IS_MASKED_GENERATOR: + if (!quiet) + log_info("Unit %s is masked via a generator and cannot be unmasked.", + changes[i].path); + break; case INSTALL_CHANGE_IS_DANGLING: if (!quiet) log_info("Unit %s is an alias to a unit that is not present, ignoring.", @@ -2287,12 +2292,32 @@ int unit_file_unmask( bool dry_run = flags & UNIT_FILE_DRY_RUN; STRV_FOREACH(name, names) { - _cleanup_free_ char *path = NULL; - if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) return -EINVAL; - path = path_make_absolute(*name, config_path); + /* If root_dir is set, we don't care about kernel commandline or generators. + * But if it is not set, we need to check for interference. */ + if (!root_dir) { + _cleanup_(install_info_clear) InstallInfo info = { + .name = *name, /* We borrow *name temporarily… */ + .install_mode = _INSTALL_MODE_INVALID, + }; + + r = unit_file_search(NULL, &info, &lp, 0); + if (r < 0) { + if (r != -ENOENT) + log_debug_errno(r, "Failed to look up unit %s, ignoring: %m", info.name); + } else { + if (info.install_mode == INSTALL_MODE_MASKED && + path_is_generator(&lp, info.path)) + install_changes_add(changes, n_changes, + INSTALL_CHANGE_IS_MASKED_GENERATOR, info.name, info.path); + } + + TAKE_PTR(info.name); /* … and give it back here */ + } + + _cleanup_free_ char *path = path_make_absolute(*name, config_path); if (!path) return -ENOMEM; @@ -3645,6 +3670,7 @@ static const char* const install_change_table[_INSTALL_CHANGE_MAX] = { [INSTALL_CHANGE_SYMLINK] = "symlink", [INSTALL_CHANGE_UNLINK] = "unlink", [INSTALL_CHANGE_IS_MASKED] = "masked", + [INSTALL_CHANGE_IS_MASKED_GENERATOR] = "masked by generator", [INSTALL_CHANGE_IS_DANGLING] = "dangling", [INSTALL_CHANGE_DESTINATION_NOT_PRESENT] = "destination not present", [INSTALL_CHANGE_AUXILIARY_FAILED] = "auxiliary unit failed", |