]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
home: Prompt for auxiliary groups in homectl firstboot
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 16 Sep 2024 11:43:38 +0000 (13:43 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 2 Oct 2024 13:07:59 +0000 (15:07 +0200)
src/home/homectl.c

index c193715d96ea9920eacdb5c88475ff27b9cbdf69..a2678c8140619709e30ceb8876b8b81d9070888d 100644 (file)
@@ -2424,6 +2424,58 @@ static int has_regular_user(void) {
         return false;
 }
 
+static int acquire_group_list(char ***ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_strv_free_ char **groups = NULL;
+        int r;
+
+        assert(ret);
+
+        r = groupdb_all(USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &iterator);
+        if (r == -ENOLINK)
+                log_debug_errno(r, "No groups found. (Didn't check via Varlink.)");
+        else if (r == -ESRCH)
+                log_debug_errno(r, "No groups found.");
+        else if (r < 0)
+                return log_debug_errno(r, "Failed to enumerate groups, ignoring: %m");
+        else {
+                for (;;) {
+                        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+                        r = groupdb_iterator_get(iterator, &gr);
+                        if (r == -ESRCH)
+                                break;
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed acquire next group: %m");
+
+                        if (!IN_SET(group_record_disposition(gr), USER_REGULAR, USER_SYSTEM))
+                                continue;
+
+                        if (group_record_disposition(gr) == USER_REGULAR) {
+                                _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+
+                                /* Filter groups here that belong to a specific user, and are named like them */
+
+                                r = userdb_by_name(gr->group_name, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &ur);
+                                if (r < 0 && r != -ESRCH)
+                                        return log_debug_errno(r, "Failed to check if matching user exists for group '%s': %m", gr->group_name);
+
+                                if (r >= 0 && ur->gid == gr->gid && user_record_disposition(ur) == USER_REGULAR)
+                                        continue;
+                        }
+
+                        r = strv_extend(&groups, gr->group_name);
+                        if (r < 0)
+                                return log_oom();
+                }
+        }
+
+        strv_sort(groups);
+
+        *ret = TAKE_PTR(groups);
+        return !!*ret;
+}
+
 static int create_interactively(void) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_free_ char *username = NULL;
@@ -2476,6 +2528,86 @@ static int create_interactively(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to set userName field: %m");
 
+        _cleanup_strv_free_ char **available = NULL, **groups = NULL;
+
+        for (;;) {
+                _cleanup_free_ char *s = NULL;
+                unsigned u;
+
+                r = ask_string(&s,
+                               "%s Please enter an auxiliary group for user %s (empty to continue, \"list\" to list available groups): ",
+                               special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), username);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to query user for auxiliary group: %m");
+
+                if (isempty(s))
+                        break;
+
+                if (streq(s, "list")) {
+                        if (!available) {
+                                r = acquire_group_list(&available);
+                                if (r < 0)
+                                        log_warning_errno(r, "Failed to enumerate available groups, ignoring: %m");
+                                if (r == 0)
+                                        log_notice("Did not find any available groups");
+                                if (r <= 0)
+                                        continue;
+                        }
+
+                        r = show_menu(available, /*n_columns=*/ 3, /*width=*/ 20, /*percentage=*/ 60);
+                        if (r < 0)
+                                return r;
+
+                        putchar('\n');
+                        continue;
+                };
+
+                if (available) {
+                        r = safe_atou(s, &u);
+                        if (r >= 0) {
+                                if (u <= 0 || u > strv_length(available)) {
+                                        log_error("Specified entry number out of range.");
+                                        continue;
+                                }
+
+                                log_info("Selected '%s'.", available[u-1]);
+
+                                r = strv_extend(&groups, available[u-1]);
+                                if (r < 0)
+                                        return log_oom();
+
+                                continue;
+                        }
+                }
+
+                if (!valid_user_group_name(s, /* flags= */ 0)) {
+                        log_notice("Specified group name is not a valid UNIX group name, try again: %s", s);
+                        continue;
+                }
+
+                r = groupdb_by_name(s, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, /*ret=*/ NULL);
+                if (r == -ESRCH) {
+                        log_notice("Specified auxiliary group does not exist, try again: %s", s);
+                        continue;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if specified group '%s' already exists: %m", s);
+
+                log_info("Selected '%s'.", s);
+
+                r = strv_extend(&groups, s);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        if (groups) {
+                strv_sort_uniq(groups);
+
+                r = sd_json_variant_set_field_strv(&arg_identity_extra, "memberOf", groups);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set memberOf field: %m");
+        }
+
         return create_home_common(/* input= */ NULL);
 }