]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysusers: move global variables into a Context object
authorLennart Poettering <lennart@poettering.net>
Tue, 8 Aug 2023 12:56:40 +0000 (14:56 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 9 Aug 2023 15:34:00 +0000 (17:34 +0200)
Our coding style says static variables suck except for very special
cases, i.e. things like the log level or very per-process stuff, such as
parsed version of cmdline args and such. sysusers departed from that as
one of the very few exceptions in our codebases: it keeps its
operational state in global variables.

Address that. Introduce a Context object that carries the fields that so
far have been global, and pass it around as needed.

This has the nice effect that state and configuration is clearly
separated in code, and we can very clearly see which functions mangle
state and which ones do not.

No actual codeflow changes, just refactoring.

src/sysusers/sysusers.c

index 09af6dcd933c9babe934c565780aab270bb50eca..d4ae9912c80d56b64b700829900dc1d1ebc0f773 100644 (file)
@@ -101,37 +101,46 @@ static bool arg_inline = false;
 static PagerFlags arg_pager_flags = 0;
 static ImagePolicy *arg_image_policy = NULL;
 
-static OrderedHashmap *users = NULL, *groups = NULL;
-static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
-static OrderedHashmap *members = NULL;
-
-static Hashmap *database_by_uid = NULL, *database_by_username = NULL;
-static Hashmap *database_by_gid = NULL, *database_by_groupname = NULL;
-
-/* A helper set to hold names that are used by database_by_{uid,gid,username,groupname} above. */
-static Set *names = NULL;
-
-static uid_t search_uid = UID_INVALID;
-static UidRange *uid_range = NULL;
-
-static UGIDAllocationRange login_defs = {};
-static bool login_defs_need_warning = false;
-
-STATIC_DESTRUCTOR_REGISTER(groups, ordered_hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(users, ordered_hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(members, ordered_hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(todo_uids, ordered_hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(todo_gids, ordered_hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(database_by_uid, hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(database_by_username, hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(database_by_gid, hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep);
-STATIC_DESTRUCTOR_REGISTER(names, set_free_freep);
-STATIC_DESTRUCTOR_REGISTER(uid_range, uid_range_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
 
+typedef struct Context {
+        OrderedHashmap *users, *groups;
+        OrderedHashmap *todo_uids, *todo_gids;
+        OrderedHashmap *members;
+
+        Hashmap *database_by_uid, *database_by_username;
+        Hashmap *database_by_gid, *database_by_groupname;
+
+        /* A helper set to hold names that are used by database_by_{uid,gid,username,groupname} above. */
+        Set *names;
+
+        uid_t search_uid;
+        UidRange *uid_range;
+
+        UGIDAllocationRange login_defs;
+        bool login_defs_need_warning;
+} Context;
+
+static void context_done(Context *c) {
+        assert(c);
+
+        ordered_hashmap_free(c->groups);
+        ordered_hashmap_free(c->users);
+        ordered_hashmap_free(c->members);
+        ordered_hashmap_free(c->todo_uids);
+        ordered_hashmap_free(c->todo_gids);
+
+        hashmap_free(c->database_by_uid);
+        hashmap_free(c->database_by_username);
+        hashmap_free(c->database_by_gid);
+        hashmap_free(c->database_by_groupname);
+
+        set_free_free(c->names);
+        uid_range_free(c->uid_range);
+}
+
 static int errno_is_not_exists(int code) {
         /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
          * not found. */
@@ -146,47 +155,51 @@ static int errno_is_not_exists(int code) {
 #define FORMAT_GID(is_set, gid) \
         ((is_set) ? snprintf_ok((char[DECIMAL_STR_MAX(gid_t)]){}, DECIMAL_STR_MAX(gid_t), GID_FMT, gid) : "(unset)")
 
-static void maybe_emit_login_defs_warning(void) {
-        if (!login_defs_need_warning)
+static void maybe_emit_login_defs_warning(Context *c) {
+        assert(c);
+
+        if (!c->login_defs_need_warning)
                 return;
 
-        if (login_defs.system_alloc_uid_min != SYSTEM_ALLOC_UID_MIN ||
-            login_defs.system_uid_max != SYSTEM_UID_MAX)
+        if (c->login_defs.system_alloc_uid_min != SYSTEM_ALLOC_UID_MIN ||
+            c->login_defs.system_uid_max != SYSTEM_UID_MAX)
                 log_warning("login.defs specifies UID allocation range "UID_FMT"–"UID_FMT
                             " that is different than the built-in defaults ("UID_FMT"–"UID_FMT")",
-                            login_defs.system_alloc_uid_min, login_defs.system_uid_max,
+                            c->login_defs.system_alloc_uid_min, c->login_defs.system_uid_max,
                             (uid_t) SYSTEM_ALLOC_UID_MIN, (uid_t) SYSTEM_UID_MAX);
-        if (login_defs.system_alloc_gid_min != SYSTEM_ALLOC_GID_MIN ||
-            login_defs.system_gid_max != SYSTEM_GID_MAX)
+        if (c->login_defs.system_alloc_gid_min != SYSTEM_ALLOC_GID_MIN ||
+            c->login_defs.system_gid_max != SYSTEM_GID_MAX)
                 log_warning("login.defs specifies GID allocation range "GID_FMT"–"GID_FMT
                             " that is different than the built-in defaults ("GID_FMT"–"GID_FMT")",
-                            login_defs.system_alloc_gid_min, login_defs.system_gid_max,
+                            c->login_defs.system_alloc_gid_min, c->login_defs.system_gid_max,
                             (gid_t) SYSTEM_ALLOC_GID_MIN, (gid_t) SYSTEM_GID_MAX);
 
-        login_defs_need_warning = false;
+        c->login_defs_need_warning = false;
 }
 
-static int load_user_database(void) {
+static int load_user_database(Context *c) {
         _cleanup_fclose_ FILE *f = NULL;
         const char *passwd_path;
         struct passwd *pw;
         int r;
 
+        assert(c);
+
         passwd_path = prefix_roota(arg_root, "/etc/passwd");
         f = fopen(passwd_path, "re");
         if (!f)
                 return errno == ENOENT ? 0 : -errno;
 
-        r = hashmap_ensure_allocated(&database_by_username, &string_hash_ops);
+        r = hashmap_ensure_allocated(&c->database_by_username, &string_hash_ops);
         if (r < 0)
                 return r;
 
-        r = hashmap_ensure_allocated(&database_by_uid, NULL);
+        r = hashmap_ensure_allocated(&c->database_by_uid, NULL);
         if (r < 0)
                 return r;
 
         /* Note that we use NULL, i.e. trivial_hash_ops here, so identical strings can exist in the set. */
-        r = set_ensure_allocated(&names, NULL);
+        r = set_ensure_allocated(&c->names, NULL);
         if (r < 0)
                 return r;
 
@@ -196,19 +209,19 @@ static int load_user_database(void) {
                 if (!n)
                         return -ENOMEM;
 
-                r = set_consume(names, n);
+                r = set_consume(c->names, n);
                 if (r < 0)
                         return r;
                 assert(r > 0);  /* The set uses pointer comparisons, so n must not be in the set. */
 
-                r = hashmap_put(database_by_username, n, UID_TO_PTR(pw->pw_uid));
+                r = hashmap_put(c->database_by_username, n, UID_TO_PTR(pw->pw_uid));
                 if (r == -EEXIST)
                         log_debug_errno(r, "%s: user '%s' is listed twice, ignoring duplicate uid.",
                                         passwd_path, n);
                 else if (r < 0)
                         return r;
 
-                r = hashmap_put(database_by_uid, UID_TO_PTR(pw->pw_uid), n);
+                r = hashmap_put(c->database_by_uid, UID_TO_PTR(pw->pw_uid), n);
                 if (r == -EEXIST)
                         log_debug_errno(r, "%s: uid "UID_FMT" is listed twice, ignoring duplicate name.",
                                         passwd_path, pw->pw_uid);
@@ -218,27 +231,29 @@ static int load_user_database(void) {
         return r;
 }
 
-static int load_group_database(void) {
+static int load_group_database(Context *c) {
         _cleanup_fclose_ FILE *f = NULL;
         const char *group_path;
         struct group *gr;
         int r;
 
+        assert(c);
+
         group_path = prefix_roota(arg_root, "/etc/group");
         f = fopen(group_path, "re");
         if (!f)
                 return errno == ENOENT ? 0 : -errno;
 
-        r = hashmap_ensure_allocated(&database_by_groupname, &string_hash_ops);
+        r = hashmap_ensure_allocated(&c->database_by_groupname, &string_hash_ops);
         if (r < 0)
                 return r;
 
-        r = hashmap_ensure_allocated(&database_by_gid, NULL);
+        r = hashmap_ensure_allocated(&c->database_by_gid, NULL);
         if (r < 0)
                 return r;
 
         /* Note that we use NULL, i.e. trivial_hash_ops here, so identical strings can exist in the set. */
-        r = set_ensure_allocated(&names, NULL);
+        r = set_ensure_allocated(&c->names, NULL);
         if (r < 0)
                 return r;
 
@@ -248,19 +263,19 @@ static int load_group_database(void) {
                 if (!n)
                         return -ENOMEM;
 
-                r = set_consume(names, n);
+                r = set_consume(c->names, n);
                 if (r < 0)
                         return r;
                 assert(r > 0);  /* The set uses pointer comparisons, so n must not be in the set. */
 
-                r = hashmap_put(database_by_groupname, n, GID_TO_PTR(gr->gr_gid));
+                r = hashmap_put(c->database_by_groupname, n, GID_TO_PTR(gr->gr_gid));
                 if (r == -EEXIST)
                         log_debug_errno(r, "%s: group '%s' is listed twice, ignoring duplicate gid.",
                                         group_path, n);
                 else if (r < 0)
                         return r;
 
-                r = hashmap_put(database_by_gid, GID_TO_PTR(gr->gr_gid), n);
+                r = hashmap_put(c->database_by_gid, GID_TO_PTR(gr->gr_gid), n);
                 if (r == -EEXIST)
                         log_debug_errno(r, "%s: gid "GID_FMT" is listed twice, ignoring duplicate name.",
                                         group_path, gr->gr_gid);
@@ -326,13 +341,18 @@ static int make_backup(const char *target, const char *x) {
         return 0;
 }
 
-static int putgrent_with_members(const struct group *gr, FILE *group) {
+static int putgrent_with_members(
+                Context *c,
+                const struct group *gr,
+                FILE *group) {
+
         char **a;
 
+        assert(c);
         assert(gr);
         assert(group);
 
-        a = ordered_hashmap_get(members, gr->gr_name);
+        a = ordered_hashmap_get(c->members, gr->gr_name);
         if (a) {
                 _cleanup_strv_free_ char **l = NULL;
                 bool added = false;
@@ -370,13 +390,17 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
 }
 
 #if ENABLE_GSHADOW
-static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
+static int putsgent_with_members(
+                Context *c,
+                const struct sgrp *sg,
+                FILE *gshadow) {
+
         char **a;
 
         assert(sg);
         assert(gshadow);
 
-        a = ordered_hashmap_get(members, sg->sg_namp);
+        a = ordered_hashmap_get(c->members, sg->sg_namp);
         if (a) {
                 _cleanup_strv_free_ char **l = NULL;
                 bool added = false;
@@ -424,14 +448,21 @@ static const char* pick_shell(const Item *i) {
         return NOLOGIN;
 }
 
-static int write_temporary_passwd(const char *passwd_path, FILE **ret_tmpfile, char **ret_tmpfile_path) {
+static int write_temporary_passwd(
+                Context *c,
+                const char *passwd_path,
+                FILE **ret_tmpfile,
+                char **ret_tmpfile_path) {
+
         _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
         _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
         struct passwd *pw = NULL;
         Item *i;
         int r;
 
-        if (ordered_hashmap_isempty(todo_uids))
+        assert(c);
+
+        if (ordered_hashmap_isempty(c->todo_uids))
                 return 0;
 
         if (arg_dry_run) {
@@ -456,13 +487,13 @@ static int write_temporary_passwd(const char *passwd_path, FILE **ret_tmpfile, c
                                                passwd_path, passwd_tmp);
 
                 while ((r = fgetpwent_sane(original, &pw)) > 0) {
-                        i = ordered_hashmap_get(users, pw->pw_name);
+                        i = ordered_hashmap_get(c->users, pw->pw_name);
                         if (i && i->todo_user)
                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
                                                        "%s: User \"%s\" already exists.",
                                                        passwd_path, pw->pw_name);
 
-                        if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid)))
+                        if (ordered_hashmap_contains(c->todo_uids, UID_TO_PTR(pw->pw_uid)))
                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
                                                        "%s: Detected collision for UID " UID_FMT ".",
                                                        passwd_path, pw->pw_uid);
@@ -486,7 +517,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **ret_tmpfile, c
                         return log_debug_errno(errno, "Failed to fchmod %s: %m", passwd_tmp);
         }
 
-        ORDERED_HASHMAP_FOREACH(i, todo_uids) {
+        ORDERED_HASHMAP_FOREACH(i, c->todo_uids) {
                 _cleanup_free_ char *creds_shell = NULL, *cn = NULL;
 
                 struct passwd n = {
@@ -559,7 +590,12 @@ static usec_t epoch_or_now(void) {
         return now(CLOCK_REALTIME);
 }
 
-static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, char **ret_tmpfile_path) {
+static int write_temporary_shadow(
+                Context *c,
+                const char *shadow_path,
+                FILE **ret_tmpfile,
+                char **ret_tmpfile_path) {
+
         _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
         _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
         struct spwd *sp = NULL;
@@ -567,7 +603,9 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c
         Item *i;
         int r;
 
-        if (ordered_hashmap_isempty(todo_uids))
+        assert(c);
+
+        if (ordered_hashmap_isempty(c->todo_uids))
                 return 0;
 
         if (arg_dry_run) {
@@ -590,7 +628,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c
                                                shadow_path, shadow_tmp);
 
                 while ((r = fgetspent_sane(original, &sp)) > 0) {
-                        i = ordered_hashmap_get(users, sp->sp_namp);
+                        i = ordered_hashmap_get(c->users, sp->sp_namp);
                         if (i && i->todo_user) {
                                 /* we will update the existing entry */
                                 sp->sp_lstchg = lstchg;
@@ -598,7 +636,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c
                                 /* only the /etc/shadow stage is left, so we can
                                  * safely remove the item from the todo set */
                                 i->todo_user = false;
-                                ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
+                                ordered_hashmap_remove(c->todo_uids, UID_TO_PTR(i->uid));
                         }
 
                         /* Make sure we keep the NIS entries (if any) at the end. */
@@ -621,7 +659,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c
                         return log_debug_errno(errno, "Failed to fchmod %s: %m", shadow_tmp);
         }
 
-        ORDERED_HASHMAP_FOREACH(i, todo_uids) {
+        ORDERED_HASHMAP_FOREACH(i, c->todo_uids) {
                 _cleanup_(erase_and_freep) char *creds_password = NULL;
                 bool is_hashed;
 
@@ -687,7 +725,12 @@ static int write_temporary_shadow(const char *shadow_path, FILE **ret_tmpfile, c
         return 0;
 }
 
-static int write_temporary_group(const char *group_path, FILE **ret_tmpfile, char **ret_tmpfile_path) {
+static int write_temporary_group(
+                Context *c,
+                const char *group_path,
+                FILE **ret_tmpfile,
+                char **ret_tmpfile_path) {
+
         _cleanup_fclose_ FILE *original = NULL, *group = NULL;
         _cleanup_(unlink_and_freep) char *group_tmp = NULL;
         bool group_changed = false;
@@ -695,7 +738,9 @@ static int write_temporary_group(const char *group_path, FILE **ret_tmpfile, cha
         Item *i;
         int r;
 
-        if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
+        assert(c);
+
+        if (ordered_hashmap_isempty(c->todo_gids) && ordered_hashmap_isempty(c->members))
                 return 0;
 
         if (arg_dry_run) {
@@ -721,13 +766,13 @@ static int write_temporary_group(const char *group_path, FILE **ret_tmpfile, cha
                          * entries anyway here, let's make an extra verification
                          * step that we don't generate duplicate entries. */
 
-                        i = ordered_hashmap_get(groups, gr->gr_name);
+                        i = ordered_hashmap_get(c->groups, gr->gr_name);
                         if (i && i->todo_group)
                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
                                                        "%s: Group \"%s\" already exists.",
                                                        group_path, gr->gr_name);
 
-                        if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid)))
+                        if (ordered_hashmap_contains(c->todo_gids, GID_TO_PTR(gr->gr_gid)))
                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
                                                        "%s: Detected collision for GID " GID_FMT ".",
                                                        group_path, gr->gr_gid);
@@ -736,7 +781,7 @@ static int write_temporary_group(const char *group_path, FILE **ret_tmpfile, cha
                         if (IN_SET(gr->gr_name[0], '+', '-'))
                                 break;
 
-                        r = putgrent_with_members(gr, group);
+                        r = putgrent_with_members(c, gr, group);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
                                                        gr->gr_name);
@@ -753,14 +798,14 @@ static int write_temporary_group(const char *group_path, FILE **ret_tmpfile, cha
                         return log_error_errno(errno, "Failed to fchmod %s: %m", group_tmp);
         }
 
-        ORDERED_HASHMAP_FOREACH(i, todo_gids) {
+        ORDERED_HASHMAP_FOREACH(i, c->todo_gids) {
                 struct group n = {
                         .gr_name = i->name,
                         .gr_gid = i->gid,
                         .gr_passwd = (char*) PASSWORD_SEE_SHADOW,
                 };
 
-                r = putgrent_with_members(&n, group);
+                r = putgrent_with_members(c, &n, group);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
                                                gr->gr_name);
@@ -793,7 +838,12 @@ static int write_temporary_group(const char *group_path, FILE **ret_tmpfile, cha
         return 0;
 }
 
-static int write_temporary_gshadow(const char * gshadow_path, FILE **ret_tmpfile, char **ret_tmpfile_path) {
+static int write_temporary_gshadow(
+                Context *c,
+                const char * gshadow_path,
+                FILE **ret_tmpfile,
+                char **ret_tmpfile_path) {
+
 #if ENABLE_GSHADOW
         _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
         _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL;
@@ -801,7 +851,9 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **ret_tmpfile
         Item *i;
         int r;
 
-        if (ordered_hashmap_isempty(todo_gids) && ordered_hashmap_isempty(members))
+        assert(c);
+
+        if (ordered_hashmap_isempty(c->todo_gids) && ordered_hashmap_isempty(c->members))
                 return 0;
 
         if (arg_dry_run) {
@@ -824,13 +876,13 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **ret_tmpfile
 
                 while ((r = fgetsgent_sane(original, &sg)) > 0) {
 
-                        i = ordered_hashmap_get(groups, sg->sg_namp);
+                        i = ordered_hashmap_get(c->groups, sg->sg_namp);
                         if (i && i->todo_group)
                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
                                                        "%s: Group \"%s\" already exists.",
                                                        gshadow_path, sg->sg_namp);
 
-                        r = putsgent_with_members(sg, gshadow);
+                        r = putsgent_with_members(c, sg, gshadow);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
                                                        sg->sg_namp);
@@ -847,13 +899,13 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **ret_tmpfile
                         return log_error_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
         }
 
-        ORDERED_HASHMAP_FOREACH(i, todo_gids) {
+        ORDERED_HASHMAP_FOREACH(i, c->todo_gids) {
                 struct sgrp n = {
                         .sg_namp = i->name,
                         .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID,
                 };
 
-                r = putsgent_with_members(&n, gshadow);
+                r = putsgent_with_members(c, &n, gshadow);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
                                                n.sg_namp);
@@ -873,7 +925,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **ret_tmpfile
         return 0;
 }
 
-static int write_files(void) {
+static int write_files(Context *c) {
         _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
         _cleanup_(unlink_and_freep) char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
         int r;
@@ -884,19 +936,21 @@ static int write_files(void) {
                 *group_path = prefix_roota(arg_root, "/etc/group"),
                 *gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
 
-        r = write_temporary_group(group_path, &group, &group_tmp);
+        assert(c);
+
+        r = write_temporary_group(c, group_path, &group, &group_tmp);
         if (r < 0)
                 return r;
 
-        r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
+        r = write_temporary_gshadow(c, gshadow_path, &gshadow, &gshadow_tmp);
         if (r < 0)
                 return r;
 
-        r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
+        r = write_temporary_passwd(c, passwd_path, &passwd, &passwd_tmp);
         if (r < 0)
                 return r;
 
-        r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
+        r = write_temporary_shadow(c, shadow_path, &shadow, &shadow_tmp);
         if (r < 0)
                 return r;
 
@@ -966,10 +1020,16 @@ static int write_files(void) {
         return 0;
 }
 
-static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) {
+static int uid_is_ok(
+                Context *c,
+                uid_t uid,
+                const char *name,
+                bool check_with_gid) {
+
+        assert(c);
 
         /* Let's see if we already have assigned the UID a second time */
-        if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid)))
+        if (ordered_hashmap_get(c->todo_uids, UID_TO_PTR(uid)))
                 return 0;
 
         /* Try to avoid using uids that are already used by a group
@@ -977,19 +1037,19 @@ static int uid_is_ok(uid_t uid, const char *name, bool check_with_gid) {
         if (check_with_gid) {
                 Item *i;
 
-                i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid));
+                i = ordered_hashmap_get(c->todo_gids, GID_TO_PTR(uid));
                 if (i && !streq(i->name, name))
                         return 0;
         }
 
         /* Let's check the files directly */
-        if (hashmap_contains(database_by_uid, UID_TO_PTR(uid)))
+        if (hashmap_contains(c->database_by_uid, UID_TO_PTR(uid)))
                 return 0;
 
         if (check_with_gid) {
                 const char *n;
 
-                n = hashmap_get(database_by_gid, GID_TO_PTR(uid));
+                n = hashmap_get(c->database_by_gid, GID_TO_PTR(uid));
                 if (n && !streq(n, name))
                         return 0;
         }
@@ -1085,14 +1145,15 @@ static int read_id_from_file(Item *i, uid_t *ret_uid, gid_t *ret_gid) {
         return 1;
 }
 
-static int add_user(Item *i) {
+static int add_user(Context *c, Item *i) {
         void *z;
         int r;
 
+        assert(c);
         assert(i);
 
         /* Check the database directly */
-        z = hashmap_get(database_by_username, i->name);
+        z = hashmap_get(c->database_by_username, i->name);
         if (z) {
                 log_debug("User %s already exists.", i->name);
                 i->uid = PTR_TO_UID(z);
@@ -1123,7 +1184,7 @@ static int add_user(Item *i) {
 
         /* Try to use the suggested numeric UID */
         if (i->uid_set) {
-                r = uid_is_ok(i->uid, i->name, !i->id_set_strict);
+                r = uid_is_ok(c, i->uid, i->name, !i->id_set_strict);
                 if (r < 0)
                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
                 if (r == 0) {
@@ -1134,28 +1195,28 @@ static int add_user(Item *i) {
 
         /* If that didn't work, try to read it from the specified path */
         if (!i->uid_set) {
-                uid_t c;
+                uid_t candidate;
 
-                if (read_id_from_file(i, &c, NULL) > 0) {
+                if (read_id_from_file(i, &candidate, NULL) > 0) {
 
-                        if (c <= 0 || !uid_range_contains(uid_range, c))
-                                log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
+                        if (candidate <= 0 || !uid_range_contains(c->uid_range, candidate))
+                                log_debug("User ID " UID_FMT " of file not suitable for %s.", candidate, i->name);
                         else {
-                                r = uid_is_ok(c, i->name, true);
+                                r = uid_is_ok(c, candidate, i->name, true);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
                                 else if (r > 0) {
-                                        i->uid = c;
+                                        i->uid = candidate;
                                         i->uid_set = true;
                                 } else
-                                        log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
+                                        log_debug("User ID " UID_FMT " of file for %s is already used.", candidate, i->name);
                         }
                 }
         }
 
         /* Otherwise, try to reuse the group ID */
         if (!i->uid_set && i->gid_set) {
-                r = uid_is_ok((uid_t) i->gid, i->name, true);
+                r = uid_is_ok(c, (uid_t) i->gid, i->name, true);
                 if (r < 0)
                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
                 if (r > 0) {
@@ -1166,14 +1227,14 @@ static int add_user(Item *i) {
 
         /* And if that didn't work either, let's try to find a free one */
         if (!i->uid_set) {
-                maybe_emit_login_defs_warning();
+                maybe_emit_login_defs_warning(c);
 
                 for (;;) {
-                        r = uid_range_next_lower(uid_range, &search_uid);
+                        r = uid_range_next_lower(c->uid_range, &c->search_uid);
                         if (r < 0)
                                 return log_error_errno(r, "No free user ID available for %s.", i->name);
 
-                        r = uid_is_ok(search_uid, i->name, true);
+                        r = uid_is_ok(c, c->search_uid, i->name, true);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
                         else if (r > 0)
@@ -1181,10 +1242,10 @@ static int add_user(Item *i) {
                 }
 
                 i->uid_set = true;
-                i->uid = search_uid;
+                i->uid = c->search_uid;
         }
 
-        r = ordered_hashmap_ensure_put(&todo_uids, NULL, UID_TO_PTR(i->uid), i);
+        r = ordered_hashmap_ensure_put(&c->todo_uids, NULL, UID_TO_PTR(i->uid), i);
         if (r == -EEXIST)
                 return log_error_errno(r, "Requested user %s with UID " UID_FMT " and gid" GID_FMT " to be created is duplicated "
                                        "or conflicts with another user.", i->name, i->uid, i->gid);
@@ -1201,29 +1262,35 @@ static int add_user(Item *i) {
         return 0;
 }
 
-static int gid_is_ok(gid_t gid, const char *groupname, bool check_with_uid) {
+static int gid_is_ok(
+                Context *c,
+                gid_t gid,
+                const char *groupname,
+                bool check_with_uid) {
+
         struct group *g;
         struct passwd *p;
         Item *user;
         char *username;
 
+        assert(c);
         assert(groupname);
 
-        if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid)))
+        if (ordered_hashmap_get(c->todo_gids, GID_TO_PTR(gid)))
                 return 0;
 
         /* Avoid reusing gids that are already used by a different user */
         if (check_with_uid) {
-                user = ordered_hashmap_get(todo_uids, UID_TO_PTR(gid));
+                user = ordered_hashmap_get(c->todo_uids, UID_TO_PTR(gid));
                 if (user && !streq(user->name, groupname))
                         return 0;
         }
 
-        if (hashmap_contains(database_by_gid, GID_TO_PTR(gid)))
+        if (hashmap_contains(c->database_by_gid, GID_TO_PTR(gid)))
                 return 0;
 
         if (check_with_uid) {
-                username = hashmap_get(database_by_uid, UID_TO_PTR(gid));
+                username = hashmap_get(c->database_by_uid, UID_TO_PTR(gid));
                 if (username && !streq(username, groupname))
                         return 0;
         }
@@ -1249,15 +1316,20 @@ static int gid_is_ok(gid_t gid, const char *groupname, bool check_with_uid) {
         return 1;
 }
 
-static int get_gid_by_name(const char *name, gid_t *gid) {
+static int get_gid_by_name(
+                Context *c,
+                const char *name,
+                gid_t *ret_gid) {
+
         void *z;
 
-        assert(gid);
+        assert(c);
+        assert(ret_gid);
 
         /* Check the database directly */
-        z = hashmap_get(database_by_groupname, name);
+        z = hashmap_get(c->database_by_groupname, name);
         if (z) {
-                *gid = PTR_TO_GID(z);
+                *ret_gid = PTR_TO_GID(z);
                 return 0;
         }
 
@@ -1268,7 +1340,7 @@ static int get_gid_by_name(const char *name, gid_t *gid) {
                 errno = 0;
                 g = getgrnam(name);
                 if (g) {
-                        *gid = g->gr_gid;
+                        *ret_gid = g->gr_gid;
                         return 0;
                 }
                 if (!errno_is_not_exists(errno))
@@ -1278,12 +1350,13 @@ static int get_gid_by_name(const char *name, gid_t *gid) {
         return -ENOENT;
 }
 
-static int add_group(Item *i) {
+static int add_group(Context *c, Item *i) {
         int r;
 
+        assert(c);
         assert(i);
 
-        r = get_gid_by_name(i->name, &i->gid);
+        r = get_gid_by_name(c, i->name, &i->gid);
         if (r != -ENOENT) {
                 if (r < 0)
                         return r;
@@ -1294,7 +1367,7 @@ static int add_group(Item *i) {
 
         /* Try to use the suggested numeric GID */
         if (i->gid_set) {
-                r = gid_is_ok(i->gid, i->name, false);
+                r = gid_is_ok(c, i->gid, i->name, false);
                 if (r < 0)
                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                 if (i->id_set_strict) {
@@ -1317,7 +1390,7 @@ static int add_group(Item *i) {
 
         /* Try to reuse the numeric uid, if there's one */
         if (!i->gid_set && i->uid_set) {
-                r = gid_is_ok((gid_t) i->uid, i->name, true);
+                r = gid_is_ok(c, (gid_t) i->uid, i->name, true);
                 if (r < 0)
                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                 if (r > 0) {
@@ -1328,36 +1401,36 @@ static int add_group(Item *i) {
 
         /* If that didn't work, try to read it from the specified path */
         if (!i->gid_set) {
-                gid_t c;
+                gid_t candidate;
 
-                if (read_id_from_file(i, NULL, &c) > 0) {
+                if (read_id_from_file(i, NULL, &candidate) > 0) {
 
-                        if (c <= 0 || !uid_range_contains(uid_range, c))
-                                log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
+                        if (candidate <= 0 || !uid_range_contains(c->uid_range, candidate))
+                                log_debug("Group ID " GID_FMT " of file not suitable for %s.", candidate, i->name);
                         else {
-                                r = gid_is_ok(c, i->name, true);
+                                r = gid_is_ok(c, candidate, i->name, true);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                                 else if (r > 0) {
-                                        i->gid = c;
+                                        i->gid = candidate;
                                         i->gid_set = true;
                                 } else
-                                        log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
+                                        log_debug("Group ID " GID_FMT " of file for %s already used.", candidate, i->name);
                         }
                 }
         }
 
         /* And if that didn't work either, let's try to find a free one */
         if (!i->gid_set) {
-                maybe_emit_login_defs_warning();
+                maybe_emit_login_defs_warning(c);
 
                 for (;;) {
                         /* We look for new GIDs in the UID pool! */
-                        r = uid_range_next_lower(uid_range, &search_uid);
+                        r = uid_range_next_lower(c->uid_range, &c->search_uid);
                         if (r < 0)
                                 return log_error_errno(r, "No free group ID available for %s.", i->name);
 
-                        r = gid_is_ok(search_uid, i->name, true);
+                        r = gid_is_ok(c, c->search_uid, i->name, true);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                         else if (r > 0)
@@ -1365,10 +1438,10 @@ static int add_group(Item *i) {
                 }
 
                 i->gid_set = true;
-                i->gid = search_uid;
+                i->gid = c->search_uid;
         }
 
-        r = ordered_hashmap_ensure_put(&todo_gids, NULL, GID_TO_PTR(i->gid), i);
+        r = ordered_hashmap_ensure_put(&c->todo_gids, NULL, GID_TO_PTR(i->gid), i);
         if (r == -EEXIST)
                 return log_error_errno(r, "Requested group %s with GID "GID_FMT " to be created is duplicated or conflicts with another user.", i->name, i->gid);
         if (r == -ENOMEM)
@@ -1382,9 +1455,10 @@ static int add_group(Item *i) {
         return 0;
 }
 
-static int process_item(Item *i) {
+static int process_item(Context *c, Item *i) {
         int r;
 
+        assert(c);
         assert(i);
 
         switch (i->type) {
@@ -1393,7 +1467,7 @@ static int process_item(Item *i) {
                 Item *j = NULL;
 
                 if (!i->gid_set)
-                        j = ordered_hashmap_get(groups, i->group_name ?: i->name);
+                        j = ordered_hashmap_get(c->groups, i->group_name ?: i->name);
 
                 if (j && j->todo_group) {
                         /* When a group with the target name is already in queue,
@@ -1405,22 +1479,22 @@ static int process_item(Item *i) {
                 } else if (i->group_name) {
                         /* When a group name was given instead of a GID and it's
                          * not in queue, then it must already exist. */
-                        r = get_gid_by_name(i->group_name, &i->gid);
+                        r = get_gid_by_name(c, i->group_name, &i->gid);
                         if (r < 0)
                                 return log_error_errno(r, "Group %s not found.", i->group_name);
                         i->gid_set = true;
                         i->id_set_strict = true;
                 } else {
-                        r = add_group(i);
+                        r = add_group(c, i);
                         if (r < 0)
                                 return r;
                 }
 
-                return add_user(i);
+                return add_user(c, i);
         }
 
         case ADD_GROUP:
-                return add_group(i);
+                return add_group(c, i);
 
         default:
                 assert_not_reached();
@@ -1465,20 +1539,22 @@ static Item* item_new(ItemType type, const char *name, const char *filename, uns
         return TAKE_PTR(new);
 }
 
-static int add_implicit(void) {
+static int add_implicit(Context *c) {
         char *g, **l;
         int r;
 
+        assert(c);
+
         /* Implicitly create additional users and groups, if they were listed in "m" lines */
-        ORDERED_HASHMAP_FOREACH_KEY(l, g, members) {
+        ORDERED_HASHMAP_FOREACH_KEY(l, g, c->members) {
                 STRV_FOREACH(m, l)
-                        if (!ordered_hashmap_get(users, *m)) {
+                        if (!ordered_hashmap_get(c->users, *m)) {
                                 _cleanup_(item_freep) Item *j =
                                         item_new(ADD_USER, *m, /* filename= */ NULL, /* line= */ 0);
                                 if (!j)
                                         return log_oom();
 
-                                r = ordered_hashmap_ensure_put(&users, &item_hash_ops, j->name, j);
+                                r = ordered_hashmap_ensure_put(&c->users, &item_hash_ops, j->name, j);
                                 if (r == -ENOMEM)
                                         return log_oom();
                                 if (r < 0)
@@ -1488,14 +1564,14 @@ static int add_implicit(void) {
                                 TAKE_PTR(j);
                         }
 
-                if (!(ordered_hashmap_get(users, g) ||
-                      ordered_hashmap_get(groups, g))) {
+                if (!(ordered_hashmap_get(c->users, g) ||
+                      ordered_hashmap_get(c->groups, g))) {
                         _cleanup_(item_freep) Item *j =
                                 item_new(ADD_GROUP, g, /* filename= */ NULL, /* line= */ 0);
                         if (!j)
                                 return log_oom();
 
-                        r = ordered_hashmap_ensure_put(&groups, &item_hash_ops, j->name, j);
+                        r = ordered_hashmap_ensure_put(&c->groups, &item_hash_ops, j->name, j);
                         if (r == -ENOMEM)
                                 return log_oom();
                         if (r < 0)
@@ -1613,7 +1689,12 @@ static int item_equivalent(Item *a, Item *b) {
         return true;
 }
 
-static int parse_line(const char *fname, unsigned line, const char *buffer) {
+static int parse_line(
+                Context *c,
+                const char *fname,
+                unsigned line,
+                const char *buffer) {
+
         _cleanup_free_ char *action = NULL,
                 *name = NULL, *resolved_name = NULL,
                 *id = NULL, *resolved_id = NULL,
@@ -1626,6 +1707,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         int r;
         const char *p;
 
+        assert(c);
         assert(fname);
         assert(line >= 1);
         assert(buffer);
@@ -1743,7 +1825,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                                           action[0],
                                           description ? "GECOS" : home ? "home directory" : "login shell");
 
-                r = uid_range_add_str(&uid_range, resolved_id);
+                r = uid_range_add_str(&c->uid_range, resolved_id);
                 if (r < 0)
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
                                           "Invalid UID range %s.", resolved_id);
@@ -1770,7 +1852,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                                           action[0],
                                           description ? "GECOS" : home ? "home directory" : "login shell");
 
-                r = string_strv_ordered_hashmap_put(&members, resolved_id, resolved_name);
+                r = string_strv_ordered_hashmap_put(&c->members, resolved_id, resolved_name);
                 if (r < 0)
                         return log_error_errno(r, "Failed to store mapping for %s: %m", resolved_id);
 
@@ -1782,7 +1864,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
                                           "Lines of type 'u' require a user name in the second field.");
 
-                r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
+                r = ordered_hashmap_ensure_allocated(&c->users, &item_hash_ops);
                 if (r < 0)
                         return log_oom();
 
@@ -1823,7 +1905,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 i->home = TAKE_PTR(resolved_home);
                 i->shell = TAKE_PTR(resolved_shell);
 
-                h = users;
+                h = c->users;
                 break;
 
         case ADD_GROUP:
@@ -1837,7 +1919,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                                           action[0],
                                           description ? "GECOS" : home ? "home directory" : "login shell");
 
-                r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
+                r = ordered_hashmap_ensure_allocated(&c->groups, &item_hash_ops);
                 if (r < 0)
                         return log_oom();
 
@@ -1858,7 +1940,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         }
                 }
 
-                h = groups;
+                h = c->groups;
                 break;
 
         default:
@@ -1896,13 +1978,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         return 0;
 }
 
-static int read_config_file(const char *fn, bool ignore_enoent) {
+static int read_config_file(Context *c, const char *fn, bool ignore_enoent) {
         _cleanup_fclose_ FILE *rf = NULL;
         _cleanup_free_ char *pp = NULL;
         FILE *f = NULL;
         unsigned v = 0;
         int r = 0;
 
+        assert(c);
         assert(fn);
 
         if (streq(fn, "-"))
@@ -1937,7 +2020,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
                 if (IN_SET(*l, 0, '#'))
                         continue;
 
-                k = parse_line(fn, v, l);
+                k = parse_line(c, fn, v, l);
                 if (k < 0 && r == 0)
                         r = k;
         }
@@ -2103,16 +2186,18 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-static int parse_arguments(char **args) {
+static int parse_arguments(Context *c, char **args) {
         unsigned pos = 1;
         int r;
 
+        assert(c);
+
         STRV_FOREACH(arg, args) {
                 if (arg_inline)
                         /* Use (argument):n, where n==1 for the first positional arg */
-                        r = parse_line("(argument)", pos, *arg);
+                        r = parse_line(c, "(argument)", pos, *arg);
                 else
-                        r = read_config_file(*arg, /* ignore_enoent= */ false);
+                        r = read_config_file(c, *arg, /* ignore_enoent= */ false);
                 if (r < 0)
                         return r;
 
@@ -2122,11 +2207,13 @@ static int parse_arguments(char **args) {
         return 0;
 }
 
-static int read_config_files(char **args) {
+static int read_config_files(Context *c, char **args) {
         _cleanup_strv_free_ char **files = NULL;
         _cleanup_free_ char *p = NULL;
         int r;
 
+        assert(c);
+
         r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p);
         if (r < 0)
                 return r;
@@ -2135,24 +2222,26 @@ static int read_config_files(char **args) {
                 if (p && path_equal(*f, p)) {
                         log_debug("Parsing arguments at position \"%s\"%s", *f, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
-                        r = parse_arguments(args);
+                        r = parse_arguments(c, args);
                         if (r < 0)
                                 return r;
                 } else {
                         log_debug("Reading config file \"%s\"%s", *f, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
                         /* Just warn, ignore result otherwise */
-                        (void) read_config_file(*f, /* ignore_enoent= */ true);
+                        (void) read_config_file(c, *f, /* ignore_enoent= */ true);
                 }
 
         return 0;
 }
 
-static int read_credential_lines(void) {
+static int read_credential_lines(Context *c) {
         _cleanup_free_ char *j = NULL;
         const char *d;
         int r;
 
+        assert(c);
+
         r = get_credentials_dir(&d);
         if (r == -ENXIO)
                 return 0;
@@ -2163,7 +2252,7 @@ static int read_credential_lines(void) {
         if (!j)
                 return log_oom();
 
-        (void) read_config_file(j, /* ignore_enoent= */ true);
+        (void) read_config_file(c, j, /* ignore_enoent= */ true);
         return 0;
 }
 
@@ -2173,6 +2262,10 @@ static int run(int argc, char *argv[]) {
         _cleanup_(umount_and_freep) char *mounted_dir = NULL;
 #endif
         _cleanup_close_ int lock = -EBADF;
+        _cleanup_(context_done) Context c = {
+                .search_uid = UID_INVALID,
+        };
+
         Item *i;
         int r;
 
@@ -2223,13 +2316,13 @@ static int run(int argc, char *argv[]) {
          * specified, execute just them, and finally, without --replace= or any positional arguments, just
          * read configuration and execute it. */
         if (arg_replace || optind >= argc)
-                r = read_config_files(argv + optind);
+                r = read_config_files(&c, argv + optind);
         else
-                r = parse_arguments(argv + optind);
+                r = parse_arguments(&c, argv + optind);
         if (r < 0)
                 return r;
 
-        r = read_credential_lines();
+        r = read_credential_lines(&c);
         if (r < 0)
                 return r;
 
@@ -2241,29 +2334,29 @@ static int run(int argc, char *argv[]) {
         if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
                 return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
 
-        if (!uid_range) {
+        if (!c.uid_range) {
                 /* Default to default range of SYSTEMD_UID_MIN..SYSTEM_UID_MAX. */
-                r = read_login_defs(&login_defs, NULL, arg_root);
+                r = read_login_defs(&c.login_defs, NULL, arg_root);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read %s%s: %m",
                                                strempty(arg_root), "/etc/login.defs");
 
-                login_defs_need_warning = true;
+                c.login_defs_need_warning = true;
 
                 /* We pick a range that very conservative: we look at compiled-in maximum and the value in
                  * /etc/login.defs. That way the UIDs/GIDs which we allocate will be interpreted correctly,
                  * even if /etc/login.defs is removed later. (The bottom bound doesn't matter much, since
                  * it's only used during allocation, so we use the configured value directly). */
-                uid_t begin = login_defs.system_alloc_uid_min,
-                      end = MIN3((uid_t) SYSTEM_UID_MAX, login_defs.system_uid_max, login_defs.system_gid_max);
+                uid_t begin = c.login_defs.system_alloc_uid_min,
+                      end = MIN3((uid_t) SYSTEM_UID_MAX, c.login_defs.system_uid_max, c.login_defs.system_gid_max);
                 if (begin < end) {
-                        r = uid_range_add(&uid_range, begin, end - begin + 1);
+                        r = uid_range_add(&c.uid_range, begin, end - begin + 1);
                         if (r < 0)
                                 return log_oom();
                 }
         }
 
-        r = add_implicit();
+        r = add_implicit(&c);
         if (r < 0)
                 return r;
 
@@ -2273,21 +2366,21 @@ static int run(int argc, char *argv[]) {
                         return log_error_errno(lock, "Failed to take /etc/passwd lock: %m");
         }
 
-        r = load_user_database();
+        r = load_user_database(&c);
         if (r < 0)
                 return log_error_errno(r, "Failed to load user database: %m");
 
-        r = load_group_database();
+        r = load_group_database(&c);
         if (r < 0)
                 return log_error_errno(r, "Failed to read group database: %m");
 
-        ORDERED_HASHMAP_FOREACH(i, groups)
-                (void) process_item(i);
+        ORDERED_HASHMAP_FOREACH(i, c.groups)
+                (void) process_item(&c, i);
 
-        ORDERED_HASHMAP_FOREACH(i, users)
-                (void) process_item(i);
+        ORDERED_HASHMAP_FOREACH(i, c.users)
+                (void) process_item(&c, i);
 
-        return write_files();
+        return write_files(&c);
 }
 
 DEFINE_MAIN_FUNCTION(run);