]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homectl: split out parser parse_ssh_authorized_keys
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 8 Dec 2025 15:40:35 +0000 (16:40 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 12 Jan 2026 11:24:34 +0000 (12:24 +0100)
The idea is to split out helpers to make parse_argv() itself more
manageable. If possible, the helpers will be reused in many places. But
even if not, as in this case, it seems good to split out the code for a
specific option. Always, the sd_json_variant** pointer or pointers that
are operated on are selected in the caller. This way it is easier to see
which of the identity variables is used and if two different ones are
used.

src/home/homectl.c

index 8d8f2c0f121918c8d303f6c513e0ae9b5586e7b1..08a07665726b42891886d60939498fcdf51b8cb4 100644 (file)
@@ -3048,6 +3048,79 @@ static int drop_from_identity(const char *field) {
         return 0;
 }
 
+static int parse_ssh_authorized_keys(sd_json_variant **identity, const char *field, const char *arg) {
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        _cleanup_strv_free_ char **l = NULL, **add = NULL;
+        int r;
+
+        assert(identity);
+        assert(field);
+
+        if (isempty(arg))
+                return drop_from_identity(field);
+
+        if (arg[0] == '@') {
+                /* If prefixed with '@', read from a file */
+
+                _cleanup_fclose_ FILE *f = fopen(arg + 1, "re");
+                if (!f)
+                        return log_error_errno(errno, "Failed to open '%s': %m", arg + 1);
+
+                for (;;) {
+                        _cleanup_free_ char *line = NULL;
+
+                        r = read_line(f, LONG_LINE_MAX, &line);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to read from '%s': %m", arg + 1);
+                        if (r == 0)
+                                break;
+
+                        if (isempty(line) || line[0] == '#')
+                                continue;
+
+                        r = strv_consume(&add, TAKE_PTR(line));
+                        if (r < 0)
+                                return log_oom();
+                }
+        } else {
+                /* Otherwise, assume it's a literal key. Let's do some superficial checks
+                 * before accepting it though. */
+
+                if (string_has_cc(arg, NULL))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Authorized key contains control characters, refusing.");
+                if (arg[0] == '#')
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
+
+                add = strv_new(arg);
+                if (!add)
+                        return log_oom();
+        }
+
+        v = sd_json_variant_ref(sd_json_variant_by_key(*identity, field));
+        if (v) {
+                r = sd_json_variant_strv(v, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse %s list: %m", field);
+        }
+
+        r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ true);
+        if (r < 0)
+                return log_oom();
+
+        v = sd_json_variant_unref(v);
+
+        r = sd_json_variant_new_array_strv(&v, l);
+        if (r < 0)
+                return log_oom();
+
+        r = sd_json_variant_set_field(identity, field, v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+        return 0;
+}
+
 static int help(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -3484,7 +3557,8 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        /* Eventually we should probably turn this into a proper --dry-run option, but as long as it is not hooked up everywhere let's make it an environment variable only. */
+        /* Eventually we should probably turn this into a proper --dry-run option, but as long as it is not
+         * hooked up everywhere let's make it an environment variable only. */
         r = getenv_bool("SYSTEMD_HOME_DRY_RUN");
         if (r >= 0)
                 arg_dry_run = r;
@@ -4212,83 +4286,12 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
-                case ARG_SSH_AUTHORIZED_KEYS: {
-                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
-                        _cleanup_strv_free_ char **l = NULL, **add = NULL;
-
-                        if (isempty(optarg)) {
-                                r = drop_from_identity("sshAuthorizedKeys");
-                                if (r < 0)
-                                        return r;
-
-                                break;
-                        }
-
-                        if (optarg[0] == '@') {
-                                _cleanup_fclose_ FILE *f = NULL;
-
-                                /* If prefixed with '@' read from a file */
-
-                                f = fopen(optarg+1, "re");
-                                if (!f)
-                                        return log_error_errno(errno, "Failed to open '%s': %m", optarg+1);
-
-                                for (;;) {
-                                        _cleanup_free_ char *line = NULL;
-
-                                        r = read_line(f, LONG_LINE_MAX, &line);
-                                        if (r < 0)
-                                                return log_error_errno(r, "Failed to read from '%s': %m", optarg+1);
-                                        if (r == 0)
-                                                break;
-
-                                        if (isempty(line))
-                                                continue;
-
-                                        if (line[0] == '#')
-                                                continue;
-
-                                        r = strv_consume(&add, TAKE_PTR(line));
-                                        if (r < 0)
-                                                return log_oom();
-                                }
-                        } else {
-                                /* Otherwise, assume it's a literal key. Let's do some superficial checks
-                                 * before accept it though. */
-
-                                if (string_has_cc(optarg, NULL))
-                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing.");
-                                if (optarg[0] == '#')
-                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
-
-                                add = strv_new(optarg);
-                                if (!add)
-                                        return log_oom();
-                        }
-
-                        v = sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys"));
-                        if (v) {
-                                r = sd_json_variant_strv(v, &l);
-                                if (r < 0)
-                                        return log_error_errno(r, "Failed to parse SSH authorized keys list: %m");
-                        }
-
-                        r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ true);
+                case ARG_SSH_AUTHORIZED_KEYS:
+                        r = parse_ssh_authorized_keys(&arg_identity_extra_privileged, "sshAuthorizedKeys", optarg);
                         if (r < 0)
-                                return log_oom();
-
-                        v = sd_json_variant_unref(v);
-
-                        r = sd_json_variant_new_array_strv(&v, l);
-                        if (r < 0)
-                                return log_oom();
-
-                        r = sd_json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to set authorized keys: %m");
+                                return r;
 
                         break;
-                }
 
                 case ARG_NOT_BEFORE:
                 case ARG_NOT_AFTER: