ryantimwilson / rpms / systemd

Forked from rpms/systemd 6 days ago
Clone
8d419f
From 35bcb96c1ac2db777db1649026f931ce77c9c7ba Mon Sep 17 00:00:00 2001
8d419f
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
8d419f
Date: Thu, 10 Mar 2022 11:03:41 +0100
8d419f
Subject: [PATCH] shared/install: propagate errors about invalid aliases and
8d419f
 such too
8d419f
8d419f
If an invalid arg appears in [Install] Alias=, WantedBy=, RequiredBy=,
8d419f
we'd warn in the logs, but not propagate this information to the caller,
8d419f
and in particular not over dbus. But if we call "systemctl enable" on a
8d419f
unit, and the config if invalid, this information is quite important.
8d419f
8d419f
(cherry picked from commit cbfdbffb618f1d75e668c59887a27c7a60950546)
8d419f
8d419f
Related: #2082131
8d419f
---
8d419f
 src/basic/unit-file.c        | 44 +++++++++++++-------------
8d419f
 src/basic/unit-file.h        |  2 +-
8d419f
 src/shared/install.c         | 61 ++++++++++++++++++++++++++----------
8d419f
 src/shared/install.h         |  7 ++++-
8d419f
 src/test/test-install-root.c |  2 +-
8d419f
 src/test/test-unit-file.c    | 28 ++++++++---------
8d419f
 6 files changed, 88 insertions(+), 56 deletions(-)
8d419f
8d419f
diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c
8d419f
index f7a10b22c6..105dacc1b2 100644
8d419f
--- a/src/basic/unit-file.c
8d419f
+++ b/src/basic/unit-file.c
8d419f
@@ -69,7 +69,7 @@ int unit_symlink_name_compatible(const char *symlink, const char *target, bool i
8d419f
         return 0;
8d419f
 }
8d419f
 
8d419f
-int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) {
8d419f
+int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target) {
8d419f
         const char *src, *dst;
8d419f
         _cleanup_free_ char *src_instance = NULL, *dst_instance = NULL;
8d419f
         UnitType src_unit_type, dst_unit_type;
8d419f
@@ -92,51 +92,51 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe
8d419f
 
8d419f
         src_name_type = unit_name_to_instance(src, &src_instance);
8d419f
         if (src_name_type < 0)
8d419f
-                return log_notice_errno(src_name_type,
8d419f
-                                        "%s: not a valid unit name \"%s\": %m", filename, src);
8d419f
+                return log_full_errno(log_level, src_name_type,
8d419f
+                                      "%s: not a valid unit name \"%s\": %m", filename, src);
8d419f
 
8d419f
         src_unit_type = unit_name_to_type(src);
8d419f
         assert(src_unit_type >= 0); /* unit_name_to_instance() checked the suffix already */
8d419f
 
8d419f
         if (!unit_type_may_alias(src_unit_type))
8d419f
-                return log_notice_errno(SYNTHETIC_ERRNO(EINVAL),
8d419f
-                                        "%s: symlinks are not allowed for units of this type, rejecting.",
8d419f
-                                        filename);
8d419f
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL),
8d419f
+                                      "%s: symlinks are not allowed for units of this type, rejecting.",
8d419f
+                                      filename);
8d419f
 
8d419f
         if (src_name_type != UNIT_NAME_PLAIN &&
8d419f
             !unit_type_may_template(src_unit_type))
8d419f
-                return log_notice_errno(SYNTHETIC_ERRNO(EINVAL),
8d419f
-                                        "%s: templates not allowed for %s units, rejecting.",
8d419f
-                                        filename, unit_type_to_string(src_unit_type));
8d419f
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL),
8d419f
+                                      "%s: templates not allowed for %s units, rejecting.",
8d419f
+                                      filename, unit_type_to_string(src_unit_type));
8d419f
 
8d419f
         /* dst checks */
8d419f
 
8d419f
         dst_name_type = unit_name_to_instance(dst, &dst_instance);
8d419f
         if (dst_name_type < 0)
8d419f
-                return log_notice_errno(dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type,
8d419f
-                                        "%s points to \"%s\" which is not a valid unit name: %m",
8d419f
-                                        filename, dst);
8d419f
+                return log_full_errno(log_level, dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type,
8d419f
+                                      "%s points to \"%s\" which is not a valid unit name: %m",
8d419f
+                                      filename, dst);
8d419f
 
8d419f
         if (!(dst_name_type == src_name_type ||
8d419f
               (src_name_type == UNIT_NAME_INSTANCE && dst_name_type == UNIT_NAME_TEMPLATE)))
8d419f
-                return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
-                                        "%s: symlink target name type \"%s\" does not match source, rejecting.",
8d419f
-                                        filename, dst);
8d419f
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
8d419f
+                                      "%s: symlink target name type \"%s\" does not match source, rejecting.",
8d419f
+                                      filename, dst);
8d419f
 
8d419f
         if (dst_name_type == UNIT_NAME_INSTANCE) {
8d419f
                 assert(src_instance);
8d419f
                 assert(dst_instance);
8d419f
                 if (!streq(src_instance, dst_instance))
8d419f
-                        return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
-                                                "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
8d419f
-                                                filename, dst);
8d419f
+                        return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
8d419f
+                                              "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
8d419f
+                                              filename, dst);
8d419f
         }
8d419f
 
8d419f
         dst_unit_type = unit_name_to_type(dst);
8d419f
         if (dst_unit_type != src_unit_type)
8d419f
-                return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
-                                        "%s: symlink target \"%s\" has incompatible suffix, rejecting.",
8d419f
-                                        filename, dst);
8d419f
+                return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
8d419f
+                                      "%s: symlink target \"%s\" has incompatible suffix, rejecting.",
8d419f
+                                      filename, dst);
8d419f
 
8d419f
         return 0;
8d419f
 }
8d419f
@@ -355,7 +355,7 @@ int unit_file_resolve_symlink(
8d419f
                                  "Suspicious symlink %s/%s→%s, treating as alias.",
8d419f
                                  dir, filename, simplified);
8d419f
 
8d419f
-                r = unit_validate_alias_symlink_and_warn(filename, simplified);
8d419f
+                r = unit_validate_alias_symlink_or_warn(LOG_NOTICE, filename, simplified);
8d419f
                 if (r < 0)
8d419f
                         return r;
8d419f
 
8d419f
diff --git a/src/basic/unit-file.h b/src/basic/unit-file.h
8d419f
index e29e878cfd..b7c03e9c2c 100644
8d419f
--- a/src/basic/unit-file.h
8d419f
+++ b/src/basic/unit-file.h
8d419f
@@ -41,7 +41,7 @@ bool unit_type_may_alias(UnitType type) _const_;
8d419f
 bool unit_type_may_template(UnitType type) _const_;
8d419f
 
8d419f
 int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
8d419f
-int unit_validate_alias_symlink_and_warn(const char *filename, const char *target);
8d419f
+int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target);
8d419f
 
8d419f
 bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new);
8d419f
 
8d419f
diff --git a/src/shared/install.c b/src/shared/install.c
8d419f
index 80863b448b..6da9ba6b0c 100644
8d419f
--- a/src/shared/install.c
8d419f
+++ b/src/shared/install.c
8d419f
@@ -394,6 +394,14 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
8d419f
                         err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, refusing to operate on linked unit file %s.",
8d419f
                                               verb, changes[i].path);
8d419f
                         break;
8d419f
+                case -EXDEV:
8d419f
+                        if (changes[i].source)
8d419f
+                                err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, cannot alias %s as %s.",
8d419f
+                                                      verb, changes[i].source, changes[i].path);
8d419f
+                        else
8d419f
+                                err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, invalid unit reference \"%s\".",
8d419f
+                                                      verb, changes[i].path);
8d419f
+                        break;
8d419f
                 case -ENOENT:
8d419f
                         err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s does not exist.",
8d419f
                                               verb, changes[i].path);
8d419f
@@ -1678,7 +1686,13 @@ static int install_info_discover_and_check(
8d419f
         return install_info_may_process(ret ? *ret : NULL, lp, changes, n_changes);
8d419f
 }
8d419f
 
8d419f
-int unit_file_verify_alias(const UnitFileInstallInfo *info, const char *dst, char **ret_dst) {
8d419f
+int unit_file_verify_alias(
8d419f
+                const UnitFileInstallInfo *info,
8d419f
+                const char *dst,
8d419f
+                char **ret_dst,
8d419f
+                UnitFileChange **changes,
8d419f
+                size_t *n_changes) {
8d419f
+
8d419f
         _cleanup_free_ char *dst_updated = NULL;
8d419f
         int r;
8d419f
 
8d419f
@@ -1705,15 +1719,19 @@ int unit_file_verify_alias(const UnitFileInstallInfo *info, const char *dst, cha
8d419f
                 p = endswith(dir, ".wants");
8d419f
                 if (!p)
8d419f
                         p = endswith(dir, ".requires");
8d419f
-                if (!p)
8d419f
-                        return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
-                                                 "Invalid path \"%s\" in alias.", dir);
8d419f
+                if (!p) {
8d419f
+                        unit_file_changes_add(changes, n_changes, -EXDEV, dst, NULL);
8d419f
+                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), "Invalid path \"%s\" in alias.", dir);
8d419f
+                }
8d419f
+
8d419f
                 *p = '\0'; /* dir should now be a unit name */
8d419f
 
8d419f
                 UnitNameFlags type = unit_name_classify(dir);
8d419f
-                if (type < 0)
8d419f
-                        return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
-                                                 "Invalid unit name component \"%s\" in alias.", dir);
8d419f
+                if (type < 0) {
8d419f
+                        unit_file_changes_add(changes, n_changes, -EXDEV, dst, NULL);
8d419f
+                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
+                                               "Invalid unit name component \"%s\" in alias.", dir);
8d419f
+                }
8d419f
 
8d419f
                 const bool instance_propagation = type == UNIT_NAME_TEMPLATE;
8d419f
 
8d419f
@@ -1721,10 +1739,12 @@ int unit_file_verify_alias(const UnitFileInstallInfo *info, const char *dst, cha
8d419f
                 r = unit_symlink_name_compatible(path_alias, info->name, instance_propagation);
8d419f
                 if (r < 0)
8d419f
                         return log_error_errno(r, "Failed to verify alias validity: %m");
8d419f
-                if (r == 0)
8d419f
-                        return log_warning_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
-                                                 "Invalid unit \"%s\" symlink \"%s\".",
8d419f
-                                                 info->name, dst);
8d419f
+                if (r == 0) {
8d419f
+                        unit_file_changes_add(changes, n_changes, -EXDEV, dst, info->name);
8d419f
+                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
8d419f
+                                               "Invalid unit \"%s\" symlink \"%s\".",
8d419f
+                                               info->name, dst);
8d419f
+                }
8d419f
 
8d419f
         } else {
8d419f
                 /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
8d419f
@@ -1733,8 +1753,10 @@ int unit_file_verify_alias(const UnitFileInstallInfo *info, const char *dst, cha
8d419f
                         _cleanup_free_ char *inst = NULL;
8d419f
 
8d419f
                         UnitNameFlags type = unit_name_to_instance(info->name, &inst);
8d419f
-                        if (type < 0)
8d419f
-                                return log_error_errno(type, "Failed to extract instance name from \"%s\": %m", info->name);
8d419f
+                        if (type < 0) {
8d419f
+                                unit_file_changes_add(changes, n_changes, -EUCLEAN, info->name, NULL);
8d419f
+                                return log_debug_errno(type, "Failed to extract instance name from \"%s\": %m", info->name);
8d419f
+                        }
8d419f
 
8d419f
                         if (type == UNIT_NAME_INSTANCE) {
8d419f
                                 r = unit_name_replace_instance(dst, inst, &dst_updated);
8d419f
@@ -1744,9 +1766,14 @@ int unit_file_verify_alias(const UnitFileInstallInfo *info, const char *dst, cha
8d419f
                         }
8d419f
                 }
8d419f
 
8d419f
-                r = unit_validate_alias_symlink_and_warn(dst_updated ?: dst, info->name);
8d419f
-                if (r < 0)
8d419f
+                r = unit_validate_alias_symlink_or_warn(LOG_DEBUG, dst_updated ?: dst, info->name);
8d419f
+                if (r < 0) {
8d419f
+                        unit_file_changes_add(changes, n_changes,
8d419f
+                                              r == -EINVAL ? -EXDEV : r,
8d419f
+                                              dst_updated ?: dst,
8d419f
+                                              info->name);
8d419f
                         return r;
8d419f
+                }
8d419f
         }
8d419f
 
8d419f
         *ret_dst = TAKE_PTR(dst_updated);
8d419f
@@ -1778,7 +1805,7 @@ static int install_info_symlink_alias(
8d419f
                         return q;
8d419f
                 }
8d419f
 
8d419f
-                q = unit_file_verify_alias(info, dst, &dst_updated);
8d419f
+                q = unit_file_verify_alias(info, dst, &dst_updated, changes, n_changes);
8d419f
                 if (q < 0)
8d419f
                         continue;
8d419f
 
8d419f
@@ -3332,7 +3359,7 @@ int unit_file_preset_all(
8d419f
 
8d419f
                         r = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes);
8d419f
                         if (r < 0 &&
8d419f
-                            !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EBADSLT, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT, -EUNATCH))
8d419f
+                            !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EBADSLT, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT, -EUNATCH, -EXDEV))
8d419f
                                 /* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors.
8d419f
                                  * Coordinate with unit_file_dump_changes() above. */
8d419f
                                 return r;
8d419f
diff --git a/src/shared/install.h b/src/shared/install.h
8d419f
index cdc5435035..d21e2aaa45 100644
8d419f
--- a/src/shared/install.h
8d419f
+++ b/src/shared/install.h
8d419f
@@ -193,7 +193,12 @@ int unit_file_changes_add(UnitFileChange **changes, size_t *n_changes, int type,
8d419f
 void unit_file_changes_free(UnitFileChange *changes, size_t n_changes);
8d419f
 void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet);
8d419f
 
8d419f
-int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst);
8d419f
+int unit_file_verify_alias(
8d419f
+                const UnitFileInstallInfo *info,
8d419f
+                const char *dst,
8d419f
+                char **ret_dst,
8d419f
+                UnitFileChange **changes,
8d419f
+                size_t *n_changes);
8d419f
 
8d419f
 typedef struct UnitFilePresetRule UnitFilePresetRule;
8d419f
 
8d419f
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
8d419f
index f718689c3a..4f66c12655 100644
8d419f
--- a/src/test/test-install-root.c
8d419f
+++ b/src/test/test-install-root.c
8d419f
@@ -1091,7 +1091,7 @@ static void verify_one(
8d419f
         if (i != last_info)
8d419f
                 log_info("-- %s --", (last_info = i)->name);
8d419f
 
8d419f
-        r = unit_file_verify_alias(i, alias, &alias2);
8d419f
+        r = unit_file_verify_alias(i, alias, &alias2, NULL, NULL);
8d419f
         log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s",
8d419f
                        i->name, alias, r, expected,
8d419f
                        alias2 ? " [" : "", strempty(alias2),
8d419f
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
8d419f
index cc08a4ae4b..8ed56ad3b8 100644
8d419f
--- a/src/test/test-unit-file.c
8d419f
+++ b/src/test/test-unit-file.c
8d419f
@@ -8,20 +8,20 @@
8d419f
 #include "unit-file.h"
8d419f
 
8d419f
 TEST(unit_validate_alias_symlink_and_warn) {
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.service") == 0);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.socket") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.foobar") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@.service") == 0);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@.socket") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@YYY.service") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@XXX.service") == 0);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@.service") == 0);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b.service") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b@.service") == -EXDEV);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a@.slice", "/other/b.slice") == -EINVAL);
8d419f
-        assert_se(unit_validate_alias_symlink_and_warn("/path/a.slice", "/other/b.slice") == -EINVAL);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.service") == 0);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.socket") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.foobar") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.service") == 0);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.socket") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@YYY.service") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@XXX.service") == 0);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@.service") == 0);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b.service") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b@.service") == -EXDEV);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.slice", "/other/b.slice") == -EINVAL);
8d419f
+        assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.slice", "/other/b.slice") == -EINVAL);
8d419f
 }
8d419f
 
8d419f
 TEST(unit_file_build_name_map) {