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. */
#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;
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);
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;
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);
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;
}
#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;
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) {
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);
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 = {
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;
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) {
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;
/* 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. */
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;
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;
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) {
* 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);
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);
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);
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;
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) {
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);
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);
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;
*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;
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
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;
}
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);
/* 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) {
/* 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) {
/* 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)
}
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);
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;
}
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;
}
errno = 0;
g = getgrnam(name);
if (g) {
- *gid = g->gr_gid;
+ *ret_gid = g->gr_gid;
return 0;
}
if (!errno_is_not_exists(errno))
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;
/* 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) {
/* 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) {
/* 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)
}
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)
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) {
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,
} 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();
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)
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)
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,
int r;
const char *p;
+ assert(c);
assert(fname);
assert(line >= 1);
assert(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);
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);
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();
i->home = TAKE_PTR(resolved_home);
i->shell = TAKE_PTR(resolved_shell);
- h = users;
+ h = c->users;
break;
case ADD_GROUP:
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();
}
}
- h = groups;
+ h = c->groups;
break;
default:
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, "-"))
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;
}
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;
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;
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;
if (!j)
return log_oom();
- (void) read_config_file(j, /* ignore_enoent= */ true);
+ (void) read_config_file(c, j, /* ignore_enoent= */ true);
return 0;
}
_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;
* 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;
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;
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);