]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysupdate: split out component validation/enumeration into sysupdate-util.[ch]
authorLennart Poettering <lennart@amutable.com>
Tue, 16 Jun 2026 16:14:17 +0000 (18:14 +0200)
committerLennart Poettering <lennart@amutable.com>
Mon, 22 Jun 2026 12:42:08 +0000 (14:42 +0200)
Just some refactoring.

The code is slightly updated, for example it now uses string_is_safe().
But mostly this just splits out code from one large file to a smaller
one.

src/sysupdate/meson.build
src/sysupdate/sysupdate-util.c
src/sysupdate/sysupdate-util.h
src/sysupdate/sysupdate.c
src/sysupdate/test-sysupdate-util.c [new file with mode: 0644]

index da554ddcc3d99af95eebe6dccd458270db60b239..44d8d59c5308536c3ce6183f7cfd592e0b3acc83 100644 (file)
@@ -45,6 +45,11 @@ executables += [
                 'objects' : ['systemd-sysupdate'],
                 'conditions' : ['ENABLE_SYSUPDATED'],
         },
+        test_template + {
+                'sources' : files('test-sysupdate-util.c'),
+                'objects' : ['systemd-sysupdate'],
+                'conditions' : ['ENABLE_SYSUPDATE'],
+        },
 ]
 
 if conf.get('ENABLE_SYSUPDATED') == 1
index 1aba63cf7828c99cf387f9e24f7a87b6a351a936..b3ed72577e911a5a5a1f71bf8c7adfcd3f5b2d74 100644 (file)
@@ -4,8 +4,14 @@
 
 #include "bus-error.h"
 #include "bus-locator.h"
+#include "conf-files.h"
+#include "constants.h"
 #include "log.h"
 #include "login-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
 #include "sysupdate-util.h"
 
 int reboot_now(void) {
@@ -24,3 +30,72 @@ int reboot_now(void) {
 
         return 0;
 }
+
+bool component_name_valid(const char *c) {
+        /* See if the specified string enclosed in the directory prefix+suffix would be a valid file name */
+
+        if (!string_is_safe(c, STRING_FILENAME_PART))
+                return false;
+
+        /* Stack allocation is safe, since STRING_FILENAME_PART includes a length check */
+        const char *j = strjoina("sysupdate.", c, ".d");
+
+        return filename_is_valid(j);
+}
+
+int get_component_list(const char *root, char ***ret) {
+        int r;
+
+        assert(ret);
+
+        ConfFile **directories = NULL;
+        size_t n_directories = 0;
+        CLEANUP_ARRAY(directories, n_directories, conf_file_free_array);
+
+        r = conf_files_list_strv_full(
+                        ".d",
+                        root,
+                        CONF_FILES_DIRECTORY|CONF_FILES_WARN,
+                        (const char * const *) CONF_PATHS_STRV(""),
+                        &directories,
+                        &n_directories);
+        if (r < 0)
+                return r;
+
+        _cleanup_set_free_ Set *names = NULL;
+
+        FOREACH_ARRAY(i, directories, n_directories) {
+                ConfFile *e = *i;
+
+                const char *s = startswith(e->filename, "sysupdate.");
+                if (!s)
+                        continue;
+
+                const char *a = endswith(s, ".d");
+                if (!a)
+                        continue;
+
+                if (a == s)
+                        continue;
+
+                _cleanup_free_ char *n = strndup(s, a - s);
+                if (!n)
+                        return log_oom();
+
+                if (!component_name_valid(n))
+                        continue;
+
+                r = set_ensure_consume(&names, &string_hash_ops_free, TAKE_PTR(n));
+                if (r < 0 && r != -EEXIST)
+                        return r;
+        }
+
+        _cleanup_strv_free_ char **z = set_to_strv(&names);
+        if (!z)
+                return -ENOMEM;
+
+        strv_sort(z);
+
+        *ret = TAKE_PTR(z);
+        return 0;
+}
index 8504fd24b7d274676f005cbb09dfa24e60a8ba82..07d9def70872e58fe7dc97809a56e9f00343b49b 100644 (file)
@@ -1,8 +1,12 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
-
 #pragma once
 
+#include <stdbool.h>
+
 int reboot_now(void);
 
-#define SD_SYSUPDATE_OFFLINE  (UINT64_C(1) << 0)
+#define SD_SYSUPDATE_OFFLINE   (UINT64_C(1) << 0)
 #define SD_SYSUPDATE_FLAGS_ALL (SD_SYSUPDATE_OFFLINE)
+
+bool component_name_valid(const char *c);
+int get_component_list(const char *root, char ***ret);
index cd5850bd38dcc2a061ef698534ef33a552dd5117..a17fbdec83cd0eb0767ca8b715468197fa030b49 100644 (file)
@@ -1711,34 +1711,12 @@ static int verb_pending_or_reboot(int argc, char *argv[], uintptr_t _data, void
         return EXIT_SUCCESS;
 }
 
-static int component_name_valid(const char *c) {
-        _cleanup_free_ char *j = NULL;
-
-        /* See if the specified string enclosed in the directory prefix+suffix would be a valid file name */
-
-        if (isempty(c))
-                return false;
-
-        if (string_has_cc(c, NULL))
-                return false;
-
-        if (!utf8_is_valid(c))
-                return false;
-
-        j = strjoin("sysupdate.", c, ".d");
-        if (!j)
-                return -ENOMEM;
-
-        return filename_is_valid(j);
-}
-
 VERB_NOARG(verb_components, "components",
            "Show list of components");
 static int verb_components(int argc, char *argv[], uintptr_t _data, void *userdata) {
         _cleanup_(context_freep) Context* context = NULL;
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
-        _cleanup_set_free_ Set *names = NULL;
         bool has_default_component = false;
         int r;
 
@@ -1752,46 +1730,10 @@ static int verb_components(int argc, char *argv[], uintptr_t _data, void *userda
         if (r < 0)
                 return r;
 
-        ConfFile **directories = NULL;
-        size_t n_directories = 0;
-
-        CLEANUP_ARRAY(directories, n_directories, conf_file_free_array);
-
-        r = conf_files_list_strv_full(".d", arg_root, CONF_FILES_DIRECTORY|CONF_FILES_WARN,
-                                      (const char * const *) CONF_PATHS_STRV(""), &directories, &n_directories);
+        _cleanup_strv_free_ char **z = NULL;
+        r = get_component_list(arg_root, &z);
         if (r < 0)
-                return log_error_errno(r, "Failed to enumerate directories: %m");
-
-        FOREACH_ARRAY(i, directories, n_directories) {
-                ConfFile *e = *i;
-
-                if (streq(e->filename, "sysupdate.d")) {
-                        continue;
-                }
-
-                const char *s = startswith(e->filename, "sysupdate.");
-                if (!s)
-                        continue;
-
-                const char *a = endswith(s, ".d");
-                if (!a)
-                        continue;
-
-                _cleanup_free_ char *n = strndup(s, a - s);
-                if (!n)
-                        return log_oom();
-
-                r = component_name_valid(n);
-                if (r < 0)
-                        return log_error_errno(r, "Unable to validate component name '%s': %m", n);
-                if (r == 0)
-                        continue;
-
-                r = set_ensure_put(&names, &string_hash_ops_free, n);
-                if (r < 0 && r != -EEXIST)
-                        return log_error_errno(r, "Failed to add component '%s' to set: %m", n);
-                TAKE_PTR(n);
-        }
+                return log_error_errno(r, "Failed to enumerate components: %m");
 
         /* Does the system have at least one transfer file in /etc/sysupdate.d, which can be considered a
          * TARGET_HOST? See target_get_argument() in sysupdated.c */
@@ -1801,15 +1743,8 @@ static int verb_components(int argc, char *argv[], uintptr_t _data, void *userda
                                  !arg_image &&
                                  context->n_transfers > 0);
 
-        /* We use simple free() rather than strv_free() here, since set_free() will free the strings for us */
-        _cleanup_free_ char **z = set_get_strv(names);
-        if (!z)
-                return log_oom();
-
-        strv_sort(z);
-
         if (!sd_json_format_enabled(arg_json_format_flags)) {
-                if (!has_default_component && set_isempty(names)) {
+                if (!has_default_component && strv_isempty(z)) {
                         log_info("No components defined.");
                         return 0;
                 }
@@ -1912,10 +1847,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
                                 break;
                         }
 
-                        r = component_name_valid(opts.arg);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to determine if component name is valid: %m");
-                        if (r == 0)
+                        if (!component_name_valid(opts.arg))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Component name invalid: %s", opts.arg);
 
                         r = free_and_strdup_warn(&arg_component, opts.arg);
diff --git a/src/sysupdate/test-sysupdate-util.c b/src/sysupdate/test-sysupdate-util.c
new file mode 100644 (file)
index 0000000..b9a5123
--- /dev/null
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sysupdate-util.h"
+#include "tests.h"
+
+TEST(component_name_valid) {
+        /* Valid component names: anything that turns "sysupdate.<name>.d" into a valid filename. */
+        ASSERT_TRUE(component_name_valid("foo"));
+        ASSERT_TRUE(component_name_valid("foo-bar"));
+        ASSERT_TRUE(component_name_valid("foo.bar"));
+        ASSERT_TRUE(component_name_valid("foo_bar_baz"));
+        ASSERT_TRUE(component_name_valid("0815"));
+        ASSERT_TRUE(component_name_valid("über"));            /* valid UTF-8 is fine */
+
+        /* Invalid: empty, slashes, control characters, invalid UTF-8. */
+        ASSERT_FALSE(component_name_valid(""));
+        ASSERT_FALSE(component_name_valid("foo/bar"));
+        ASSERT_FALSE(component_name_valid("/foo"));
+        ASSERT_FALSE(component_name_valid("foo/"));
+        ASSERT_FALSE(component_name_valid("foo\tbar"));
+        ASSERT_FALSE(component_name_valid("foo\nbar"));
+        ASSERT_FALSE(component_name_valid("foo\x7f"));
+        ASSERT_FALSE(component_name_valid("\xff"));           /* not valid UTF-8 */
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);