From 32d9493e593fed7fe5b4dd1e92fe4fd419042fe5 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 11 Aug 2016 21:53:32 -0400 Subject: systemctl: fix preset-all with missing /etc/systemd/system If the directory is missing, we can assume that those pesky symlinks are gone too. --- src/shared/install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/shared') diff --git a/src/shared/install.c b/src/shared/install.c index e740ef3910..ccb1a70096 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -620,7 +620,7 @@ static int remove_marked_symlinks( fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (fd < 0) - return -errno; + return errno == ENOENT ? 0 : -errno; do { int q, cfd; -- cgit v1.2.3-54-g00ecf From ff56349d5a83f2202ed331f232f5d73467db482c Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Fri, 12 Aug 2016 23:50:58 -0400 Subject: shared/install: remove unused paramater and add more comments --- src/shared/install.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/shared') diff --git a/src/shared/install.c b/src/shared/install.c index ccb1a70096..0cf6a29403 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -903,6 +903,10 @@ static int install_info_may_process( return 0; } +/** + * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process + * hashmap, or retrieves the existing one if already present. + */ static int install_info_add( InstallContext *c, const char *name, @@ -1334,9 +1338,8 @@ static int install_info_follow( } /** - * Search for the unit file. If the unit name is a symlink, - * follow the symlink to the target, maybe more than once. - * Propagate the instance name if present. + * Search for the unit file. If the unit name is a symlink, follow the symlink to the + * target, maybe more than once. Propagate the instance name if present. */ static int install_info_traverse( UnitFileScope scope, @@ -1421,6 +1424,10 @@ static int install_info_traverse( return 0; } +/** + * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/") + * or the name (otherwise). root_dir is prepended to the path. + */ static int install_info_add_auto( InstallContext *c, const LookupPaths *paths, @@ -2685,7 +2692,6 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - UnitFilePresetMode mode, const char *name, Presets presets, UnitFileChange **changes, @@ -2748,7 +2754,7 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes); + r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes); if (r < 0) return r; } @@ -2809,7 +2815,7 @@ int unit_file_preset_all( continue; /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0); + r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0); if (r == -ERFKILL) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, de->d_name, NULL); -- cgit v1.2.3-54-g00ecf From 11e11fd57a837ea1cb142009c3048882392f3ed3 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 13 Aug 2016 01:20:29 -0400 Subject: shared/install: ignore unit symlinks when doing preset-all Before, when interating over unit files during preset-all, behaviour was the following: - if we hit the real unit name first, presets were queried for that name, and that unit was enabled or disabled accordingly, - if we hit an alias first (one of the symlinks chaining to the real unit), we checked the presets using the symlink name, and then proceeded to enable or disable the real unit. E.g. for systemd-networkd.service we have the alias dbus-org.freedesktop.network1.service (/usr/lib/systemd/system/dbus-org.freedesktop.network1.service), but the preset is only for the systemd-networkd.service name. The service would be enabled or disabled pseudorandomly depending on the order of iteration. For "preset", behaviour was analogous: preset on the alias name disabled the service (following the default disable policy), preset on the "real" name applied the presets. With the patch, for "preset" and "preset-all" we silently skip symlinks. This gives mostly the right behaviour, with the limitation that presets on aliases are ignored. I think that presets on aliases are not that common (at least my preset files on Fedora don't exhibit any such usage), and should not be necessary, since whoever installs the preset can just refer to the real unit file. It would be possible to overcome this limitation by gathering a list of names of a unit first, and then checking whether *any* of the names matches the presets list. That would require a significant redesign of the code, and be a lot slower (since we would have to fully read all unit directories to preset one unit) to so I'm not doing that for now. With this patch, two properties are satisfied: - preset-all and preset are idempotent, and the second and subsequent invocations do not produce any changes, - preset-all and preset for a specific name produce the same state for that unit. Fixes #3616. --- src/shared/install.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/shared') diff --git a/src/shared/install.c b/src/shared/install.c index 0cf6a29403..8e40c171da 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1974,7 +1974,6 @@ int unit_file_revert( unsigned *n_changes) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - /* _cleanup_(install_context_done) InstallContext c = {}; */ _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_strv_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; @@ -2697,13 +2696,21 @@ static int preset_prepare_one( UnitFileChange **changes, unsigned *n_changes) { + _cleanup_(install_context_done) InstallContext tmp = {}; UnitFileInstallInfo *i; int r; - if (install_info_find(plus, name) || - install_info_find(minus, name)) + if (install_info_find(plus, name) || install_info_find(minus, name)) return 0; + r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + if (!streq(name, i->name)) { + log_debug("Skipping %s because is an alias for %s", name, i->name); + return 0; + } + r = query_presets(name, presets); if (r < 0) return r; -- cgit v1.2.3-54-g00ecf From 60bec8e4031367869520280350fa1523625d682b Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 13 Aug 2016 01:21:57 -0400 Subject: shared/install: move root skipping into create_symlink() No functional change intended. --- src/shared/install.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) (limited to 'src/shared') diff --git a/src/shared/install.c b/src/shared/install.c index 8e40c171da..ad2e619237 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -394,6 +394,7 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang } static int create_symlink( + const LookupPaths *paths, const char *old_path, const char *new_path, bool force, @@ -401,11 +402,16 @@ static int create_symlink( unsigned *n_changes) { _cleanup_free_ char *dest = NULL; + const char *rp; int r; assert(old_path); assert(new_path); + rp = skip_root(paths, old_path); + if (rp) + old_path = rp; + /* Actually create a symlink, and remember that we did. Is * smart enough to check if there's already a valid symlink in * place. @@ -1486,7 +1492,6 @@ static int install_info_symlink_alias( STRV_FOREACH(s, i->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL; - const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1496,9 +1501,7 @@ static int install_info_symlink_alias( if (!alias_path) return -ENOMEM; - rp = skip_root(paths, i->path); - - q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); + q = create_symlink(paths, i->path, alias_path, force, changes, n_changes); if (r == 0) r = q; } @@ -1542,7 +1545,6 @@ static int install_info_symlink_wants( STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; - const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1557,9 +1559,7 @@ static int install_info_symlink_wants( if (!path) return -ENOMEM; - rp = skip_root(paths, i->path); - - q = create_symlink(rp ?: i->path, path, true, changes, n_changes); + q = create_symlink(paths, i->path, path, true, changes, n_changes); if (r == 0) r = q; } @@ -1576,7 +1576,6 @@ static int install_info_symlink_link( unsigned *n_changes) { _cleanup_free_ char *path = NULL; - const char *rp; int r; assert(i); @@ -1594,9 +1593,7 @@ static int install_info_symlink_link( if (!path) return -ENOMEM; - rp = skip_root(paths, i->path); - - return create_symlink(rp ?: i->path, path, force, changes, n_changes); + return create_symlink(paths, i->path, path, force, changes, n_changes); } static int install_info_apply( @@ -1772,7 +1769,7 @@ int unit_file_mask( if (!path) return -ENOMEM; - q = create_symlink("/dev/null", path, force, changes, n_changes); + q = create_symlink(&paths, "/dev/null", path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -1932,14 +1929,12 @@ int unit_file_link( r = 0; STRV_FOREACH(i, todo) { _cleanup_free_ char *new_path = NULL; - const char *old_path; - old_path = skip_root(&paths, *i); new_path = path_make_absolute(basename(*i), config_path); if (!new_path) return -ENOMEM; - q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); + q = create_symlink(&paths, *i, new_path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -2318,7 +2313,7 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; UnitFileInstallInfo *i; - const char *new_path, *old_path; + const char *new_path; int r; assert(scope >= 0); @@ -2341,10 +2336,8 @@ int unit_file_set_default( if (r < 0) return r; - old_path = skip_root(&paths, i->path); new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - - return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); + return create_symlink(&paths, i->path, new_path, force, changes, n_changes); } int unit_file_get_default( -- cgit v1.2.3-54-g00ecf From 25ea92778d5f4339e07c152a99d16223f43ad681 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 13 Aug 2016 01:27:21 -0400 Subject: shared/install: when creating symlinks, keep existing relative symlinks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running preset-all on a system installed from rpms or even created using make install would remove and recreate a lot of symlinks, changing relative to absolute symlinks. In general relative symlinks are nicer, so there is no reason to change them, and those spurious changes were obscuring more interesting stuff. $ make install DESTDIR=/var/tmp/inst1 $ systemctl preset-all --root=/var/tmp/inst1 (before) Removed /var/tmp/inst1/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service. Created symlink /var/tmp/inst1/etc/systemd/system/ctrl-alt-del.target → /usr/lib/systemd/system/exit.target. Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/remote-fs.target. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/remote-fs.target → /usr/lib/systemd/system/remote-fs.target. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.target. Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-journal-remote.socket → /usr/lib/systemd/system/systemd-journal-remote.socket. Removed /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-networkd.socket. Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-networkd.socket → /usr/lib/systemd/system/systemd-networkd.socket. Removed /var/tmp/inst1/etc/systemd/system/getty.target.wants/getty@tty1.service. Created symlink /var/tmp/inst1/etc/systemd/system/getty.target.wants/getty@tty1.service → /usr/lib/systemd/system/getty@.service. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-journal-upload.service → /usr/lib/systemd/system/systemd-journal-upload.service. Removed /var/tmp/inst1/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service. Created symlink /var/tmp/inst1/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service → /usr/lib/systemd/system/systemd-timesyncd.service. Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-resolved.service. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-resolved.service → /usr/lib/systemd/system/systemd-resolved.service. Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-networkd.service. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-networkd.service → /usr/lib/systemd/system/systemd-networkd.service. (after) Removed /var/tmp/inst1/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service. Created symlink /var/tmp/inst1/etc/systemd/system/ctrl-alt-del.target → /usr/lib/systemd/system/exit.target. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.target. Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-journal-remote.socket → /usr/lib/systemd/system/systemd-journal-remote.socket. Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-journal-upload.service → /usr/lib/systemd/system/systemd-journal-upload.service. --- src/shared/install.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/shared') diff --git a/src/shared/install.c b/src/shared/install.c index ad2e619237..6c7eb9b2ef 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -393,6 +393,21 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang log_error_errno(r, "Failed to %s: %m.", verb); } +/** + * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem. + * wc should be the full path in the host file system. + */ +static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) { + assert(path_is_absolute(wd)); + + /* This will give incorrect results if the paths are relative and go outside + * of the chroot. False negatives are possible. */ + + a = strjoina(path_is_absolute(a) ? root : wd, "/", a); + b = strjoina(path_is_absolute(b) ? root : wd, "/", b); + return path_equal_or_files_same(a, b); +} + static int create_symlink( const LookupPaths *paths, const char *old_path, @@ -401,7 +416,7 @@ static int create_symlink( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_free_ char *dest = NULL; + _cleanup_free_ char *dest = NULL, *dirname = NULL; const char *rp; int r; @@ -442,7 +457,11 @@ static int create_symlink( return r; } - if (path_equal(dest, old_path)) + dirname = dirname_malloc(new_path); + if (!dirname) + return -ENOMEM; + + if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) return 1; if (!force) { -- cgit v1.2.3-54-g00ecf From f16517151310b88591f3501a59e23ae2a79e7f02 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Wed, 17 Aug 2016 22:15:54 -0400 Subject: shared/install: properly report masked units listed in Also= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A masked unit is listed in Also=: $ systemctl cat test1 test2 →# /etc/systemd/system/test1.service [Unit] Description=test service 1 [Service] Type=oneshot ExecStart=/usr/bin/true [Install] WantedBy=multi-user.target Also=test2.service Alias=alias1.service →# /dev/null $ systemctl --root=/ enable test1 (before) Created symlink /etc/systemd/system/alias1.service → /etc/systemd/system/test1.service. Created symlink /etc/systemd/system/multi-user.target.wants/test1.service → /etc/systemd/system/test1.service. The unit files have no installation config (WantedBy, RequiredBy, Also, Alias settings in the [Install] section, and DefaultInstance for template units). This means they are not meant to be enabled using systemctl. Possible reasons for having this kind of units are: 1) A unit may be statically enabled by being symlinked from another unit's .wants/ or .requires/ directory. 2) A unit's purpose may be to act as a helper for some other unit which has a requirement dependency on it. 3) A unit may be started when needed via activation (socket, path, timer, D-Bus, udev, scripted systemctl call, ...). 4) In case of template units, the unit is meant to be enabled with some instance name specified. (after) Created symlink /etc/systemd/system/alias1.service → /etc/systemd/system/test1.service. Created symlink /etc/systemd/system/multi-user.target.wants/test1.service → /etc/systemd/system/test1.service. Unit /etc/systemd/system/test2.service is masked, ignoring. --- src/shared/install.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/shared') diff --git a/src/shared/install.c b/src/shared/install.c index 6c7eb9b2ef..6a16f8985b 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1686,6 +1686,17 @@ static int install_context_apply( if (r < 0) return r; + /* We can attempt to process a masked unit when a different unit + * that we were processing specifies it in DefaultInstance= or Also=. */ + if (i->type == UNIT_FILE_TYPE_MASKED) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL); + if (r >= 0) + /* Assume that some *could* have been enabled here, avoid + * "empty [Install] section" warning. */ + r += 1; + continue; + } + if (i->type != UNIT_FILE_TYPE_REGULAR) continue; -- cgit v1.2.3-54-g00ecf