1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/types.h>
31 #include "specifier.h"
32 #include "path-util.h"
35 #include "conf-files.h"
39 #include "fileio-label.h"
41 typedef enum ItemType {
64 static char *arg_root = NULL;
66 static const char conf_file_dirs[] =
69 "/usr/local/lib/sysusers.d\0"
70 "/usr/lib/sysusers.d\0"
76 static Hashmap *users = NULL, *groups = NULL;
77 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
78 static Hashmap *members = NULL;
80 static Hashmap *database_uid = NULL, *database_user = NULL;
81 static Hashmap *database_gid = NULL, *database_group = NULL;
83 static uid_t search_uid = SYSTEM_UID_MAX;
84 static gid_t search_gid = SYSTEM_GID_MAX;
86 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
87 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
89 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
90 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
92 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
94 static int load_user_database(void) {
95 _cleanup_fclose_ FILE *f = NULL;
96 const char *passwd_path;
100 passwd_path = fix_root("/etc/passwd");
101 f = fopen(passwd_path, "re");
103 return errno == ENOENT ? 0 : -errno;
105 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
109 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
114 while ((pw = fgetpwent(f))) {
118 n = strdup(pw->pw_name);
122 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
123 if (k < 0 && k != -EEXIST) {
128 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
129 if (q < 0 && q != -EEXIST) {
140 if (!IN_SET(errno, 0, ENOENT))
146 static int load_group_database(void) {
147 _cleanup_fclose_ FILE *f = NULL;
148 const char *group_path;
152 group_path = fix_root("/etc/group");
153 f = fopen(group_path, "re");
155 return errno == ENOENT ? 0 : -errno;
157 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
161 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
166 while ((gr = fgetgrent(f))) {
170 n = strdup(gr->gr_name);
174 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
175 if (k < 0 && k != -EEXIST) {
180 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
181 if (q < 0 && q != -EEXIST) {
192 if (!IN_SET(errno, 0, ENOENT))
198 static int make_backup(const char *target, const char *x) {
199 _cleanup_close_ int src = -1;
200 _cleanup_fclose_ FILE *dst = NULL;
202 struct timespec ts[2];
206 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
208 if (errno == ENOENT) /* No backup necessary... */
214 if (fstat(src, &st) < 0)
217 r = fopen_temporary_label(target, x, &dst, &temp);
221 r = copy_bytes(src, fileno(dst), (off_t) -1);
225 /* Don't fail on chmod() or chown(). If it stays owned by us
226 * and/or unreadable by others, then it isn't too bad... */
228 backup = strappenda(x, "-");
230 /* Copy over the access mask */
231 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
232 log_warning("Failed to change mode on %s: %m", backup);
234 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
235 log_warning("Failed to change ownership of %s: %m", backup);
239 futimens(fileno(dst), ts);
241 if (rename(temp, backup) < 0)
251 static int putgrent_with_members(const struct group *gr, FILE *group) {
257 a = hashmap_get(members, gr->gr_name);
259 _cleanup_strv_free_ char **l = NULL;
263 l = strv_copy(gr->gr_mem);
268 if (strv_find(l, *i))
271 if (strv_extend(&l, *i) < 0)
287 if (putgrent(&t, group) != 0)
288 return errno ? -errno : -EIO;
295 if (putgrent(gr, group) != 0)
296 return errno ? -errno : -EIO;
301 static int write_files(void) {
303 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
304 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
305 const char *passwd_path = NULL, *group_path = NULL;
306 bool group_changed = false;
311 /* We don't patch /etc/shadow or /etc/gshadow here, since we
312 * only create user accounts without passwords anyway. */
314 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
315 _cleanup_fclose_ FILE *original = NULL;
317 group_path = fix_root("/etc/group");
318 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
322 if (fchmod(fileno(group), 0644) < 0) {
327 original = fopen(group_path, "re");
332 while ((gr = fgetgrent(original))) {
333 /* Safety checks against name and GID
334 * collisions. Normally, this should
335 * be unnecessary, but given that we
336 * look at the entries anyway here,
337 * let's make an extra verification
338 * step that we don't generate
339 * duplicate entries. */
341 i = hashmap_get(groups, gr->gr_name);
342 if (i && i->todo_group) {
347 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
352 r = putgrent_with_members(gr, group);
357 group_changed = true;
361 if (!IN_SET(errno, 0, ENOENT)) {
366 } else if (errno != ENOENT) {
371 HASHMAP_FOREACH(i, todo_gids, iterator) {
375 .gr_passwd = (char*) "x",
378 r = putgrent_with_members(&n, group);
382 group_changed = true;
385 r = fflush_and_check(group);
390 if (hashmap_size(todo_uids) > 0) {
391 _cleanup_fclose_ FILE *original = NULL;
393 passwd_path = fix_root("/etc/passwd");
394 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
398 if (fchmod(fileno(passwd), 0644) < 0) {
403 original = fopen(passwd_path, "re");
408 while ((pw = fgetpwent(original))) {
410 i = hashmap_get(users, pw->pw_name);
411 if (i && i->todo_user) {
416 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
422 if (putpwent(pw, passwd) < 0) {
423 r = errno ? -errno : -EIO;
429 if (!IN_SET(errno, 0, ENOENT)) {
434 } else if (errno != ENOENT) {
439 HASHMAP_FOREACH(i, todo_uids, iterator) {
444 .pw_gecos = i->description,
445 .pw_passwd = (char*) "x",
448 /* Initialize the home directory and the shell
449 * to nologin, with one exception: for root we
450 * patch in something special */
452 n.pw_shell = (char*) "/bin/sh";
453 n.pw_dir = (char*) "/root";
455 n.pw_shell = (char*) "/sbin/nologin";
456 n.pw_dir = (char*) "/";
460 if (putpwent(&n, passwd) != 0) {
461 r = errno ? -errno : -EIO;
466 r = fflush_and_check(passwd);
471 /* Make a backup of the old files */
472 if (group && group_changed) {
473 r = make_backup("/etc/group", group_path);
479 r = make_backup("/etc/passwd", passwd_path);
484 /* And make the new files count */
485 if (group && group_changed) {
486 if (rename(group_tmp, group_path) < 0) {
496 if (rename(passwd_tmp, passwd_path) < 0) {
516 static int uid_is_ok(uid_t uid, const char *name) {
522 /* Let's see if we already have assigned the UID a second time */
523 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
526 /* Try to avoid using uids that are already used by a group
527 * that doesn't have the same name as our new user. */
528 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
529 if (i && !streq(i->name, name))
532 /* Let's check the files directly */
533 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
536 n = hashmap_get(database_gid, GID_TO_PTR(uid));
537 if (n && !streq(n, name))
540 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
546 if (!IN_SET(errno, 0, ENOENT))
550 g = getgrgid((gid_t) uid);
552 if (!streq(g->gr_name, name))
554 } else if (!IN_SET(errno, 0, ENOENT))
561 static int root_stat(const char *p, struct stat *st) {
565 if (stat(fix, st) < 0)
571 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
573 bool found_uid = false, found_gid = false;
579 /* First, try to get the gid directly */
580 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
585 /* Then, try to get the uid directly */
586 if ((_uid || (_gid && !found_gid))
588 && root_stat(i->uid_path, &st) >= 0) {
593 /* If we need the gid, but had no success yet, also derive it from the uid path */
594 if (_gid && !found_gid) {
600 /* If that didn't work yet, then let's reuse the gid as uid */
601 if (_uid && !found_uid && i->gid_path) {
606 } else if (root_stat(i->gid_path, &st) >= 0) {
607 uid = (uid_t) st.st_gid;
629 static int add_user(Item *i) {
635 /* Check the database directly */
636 z = hashmap_get(database_user, i->name);
638 log_debug("User %s already exists.", i->name);
639 i->uid = PTR_TO_UID(z);
650 p = getpwnam(i->name);
652 log_debug("User %s already exists.", i->name);
656 free(i->description);
657 i->description = strdup(p->pw_gecos);
660 if (!IN_SET(errno, 0, ENOENT)) {
661 log_error("Failed to check if user %s already exists: %m", i->name);
665 /* And shadow too, just to be sure */
667 sp = getspnam(i->name);
669 log_error("User %s already exists in shadow database, but not in user database.", i->name);
672 if (!IN_SET(errno, 0, ENOENT)) {
673 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
678 /* Try to use the suggested numeric uid */
680 r = uid_is_ok(i->uid, i->name);
682 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
686 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
691 /* If that didn't work, try to read it from the specified path */
695 if (read_id_from_file(i, &c, NULL) > 0) {
697 if (c <= 0 || c > SYSTEM_UID_MAX)
698 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
700 r = uid_is_ok(c, i->name);
702 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
708 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
713 /* Otherwise try to reuse the group ID */
714 if (!i->uid_set && i->gid_set) {
715 r = uid_is_ok((uid_t) i->gid, i->name);
717 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
721 i->uid = (uid_t) i->gid;
726 /* And if that didn't work either, let's try to find a free one */
728 for (; search_uid > 0; search_uid--) {
730 r = uid_is_ok(search_uid, i->name);
732 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
738 if (search_uid <= 0) {
739 log_error("No free user ID available for %s.", i->name);
749 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
753 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
758 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
763 static int gid_is_ok(gid_t gid) {
767 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
770 /* Avoid reusing gids that are already used by a different user */
771 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
774 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
777 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
785 if (!IN_SET(errno, 0, ENOENT))
789 p = getpwuid((uid_t) gid);
792 if (!IN_SET(errno, 0, ENOENT))
799 static int add_group(Item *i) {
805 /* Check the database directly */
806 z = hashmap_get(database_group, i->name);
808 log_debug("Group %s already exists.", i->name);
809 i->gid = PTR_TO_GID(z);
819 g = getgrnam(i->name);
821 log_debug("Group %s already exists.", i->name);
826 if (!IN_SET(errno, 0, ENOENT)) {
827 log_error("Failed to check if group %s already exists: %m", i->name);
832 /* Try to use the suggested numeric gid */
834 r = gid_is_ok(i->gid);
836 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
840 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
845 /* Try to reuse the numeric uid, if there's one */
846 if (!i->gid_set && i->uid_set) {
847 r = gid_is_ok((gid_t) i->uid);
849 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
853 i->gid = (gid_t) i->uid;
858 /* If that didn't work, try to read it from the specified path */
862 if (read_id_from_file(i, NULL, &c) > 0) {
864 if (c <= 0 || c > SYSTEM_GID_MAX)
865 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
869 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
875 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
880 /* And if that didn't work either, let's try to find a free one */
882 for (; search_gid > 0; search_gid--) {
883 r = gid_is_ok(search_gid);
885 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
891 if (search_gid <= 0) {
892 log_error("No free group ID available for %s.", i->name);
902 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
906 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
910 i->todo_group = true;
911 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
916 static int process_item(Item *i) {
933 j = hashmap_get(users, i->name);
935 /* There's already user to be created for this
936 * name, let's process that in one step */
945 j->gid_path = strdup(i->gid_path);
957 assert_not_reached("Unknown item type");
961 static void item_free(Item *i) {
969 free(i->description);
973 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
975 static int add_implicit(void) {
980 /* Implicitly create additional users and groups, if they were listed in "m" lines */
982 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
986 i = hashmap_get(groups, g);
988 _cleanup_(item_freep) Item *j = NULL;
990 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1003 r = hashmap_put(groups, j->name, j);
1007 log_debug("Adding implicit group '%s' due to m line", j->name);
1011 STRV_FOREACH(m, l) {
1013 i = hashmap_get(users, *m);
1015 _cleanup_(item_freep) Item *j = NULL;
1017 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1026 j->name = strdup(*m);
1030 r = hashmap_put(users, j->name, j);
1034 log_debug("Adding implicit user '%s' due to m line", j->name);
1043 static bool item_equal(Item *a, Item *b) {
1047 if (a->type != b->type)
1050 if (!streq_ptr(a->name, b->name))
1053 if (!streq_ptr(a->uid_path, b->uid_path))
1056 if (!streq_ptr(a->gid_path, b->gid_path))
1059 if (!streq_ptr(a->description, b->description))
1062 if (a->uid_set != b->uid_set)
1065 if (a->uid_set && a->uid != b->uid)
1068 if (a->gid_set != b->gid_set)
1071 if (a->gid_set && a->gid != b->gid)
1077 static bool valid_user_group_name(const char *u) {
1084 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1085 !(u[0] >= 'A' && u[0] <= 'Z') &&
1089 for (i = u+1; *i; i++) {
1090 if (!(*i >= 'a' && *i <= 'z') &&
1091 !(*i >= 'A' && *i <= 'Z') &&
1092 !(*i >= '0' && *i <= '9') &&
1098 sz = sysconf(_SC_LOGIN_NAME_MAX);
1101 if ((size_t) (i-u) > (size_t) sz)
1104 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1110 static bool valid_gecos(const char *d) {
1112 if (!utf8_is_valid(d))
1115 if (string_has_cc(d, NULL))
1118 /* Colons are used as field separators, and hence not OK */
1125 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1127 static const Specifier specifier_table[] = {
1128 { 'm', specifier_machine_id, NULL },
1129 { 'b', specifier_boot_id, NULL },
1130 { 'H', specifier_host_name, NULL },
1131 { 'v', specifier_kernel_release, NULL },
1135 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1136 _cleanup_(item_freep) Item *i = NULL;
1152 log_error("[%s:%u] Syntax error.", fname, line);
1156 if (strlen(action) != 1) {
1157 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1161 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1162 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1166 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1168 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1172 if (!valid_user_group_name(resolved_name)) {
1173 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1178 n += strspn(buffer+n, WHITESPACE);
1180 if (STR_IN_SET(buffer + n, "", "-"))
1184 switch (action[0]) {
1187 _cleanup_free_ char *resolved_id = NULL;
1190 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1194 /* Try to extend an existing member or group item */
1196 if (!id || streq(id, "-")) {
1197 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1201 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1203 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1207 if (!valid_user_group_name(resolved_id)) {
1208 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1213 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1217 l = hashmap_get(members, resolved_id);
1219 /* A list for this group name already exists, let's append to it */
1220 r = strv_push(&l, resolved_name);
1224 resolved_name = NULL;
1226 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1228 /* No list for this group name exists yet, create one */
1230 l = new0(char *, 2);
1234 l[0] = resolved_name;
1237 r = hashmap_put(members, resolved_id, l);
1243 resolved_id = resolved_name = NULL;
1250 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1258 if (id && !streq(id, "-")) {
1260 if (path_is_absolute(id)) {
1261 i->uid_path = strdup(id);
1265 path_kill_slashes(i->uid_path);
1268 r = parse_uid(id, &i->uid);
1270 log_error("Failed to parse UID: %s", id);
1279 i->description = unquote(buffer+n, "\"");
1280 if (!i->description)
1283 if (!valid_gecos(i->description)) {
1284 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1293 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1298 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1306 if (id && !streq(id, "-")) {
1308 if (path_is_absolute(id)) {
1309 i->gid_path = strdup(id);
1313 path_kill_slashes(i->gid_path);
1315 r = parse_gid(id, &i->gid);
1317 log_error("Failed to parse GID: %s", id);
1332 i->type = action[0];
1333 i->name = resolved_name;
1334 resolved_name = NULL;
1336 existing = hashmap_get(h, i->name);
1339 /* Two identical items are fine */
1340 if (!item_equal(existing, i))
1341 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1346 r = hashmap_put(h, i->name, i);
1354 static int read_config_file(const char *fn, bool ignore_enoent) {
1355 _cleanup_fclose_ FILE *f = NULL;
1356 char line[LINE_MAX];
1362 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1364 if (ignore_enoent && r == -ENOENT)
1367 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1371 FOREACH_LINE(line, f, break) {
1378 if (*l == '#' || *l == 0)
1381 k = parse_line(fn, v, l);
1382 if (k < 0 && r == 0)
1387 log_error("Failed to read from file %s: %m", fn);
1395 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1399 name = hashmap_first(by_id);
1403 hashmap_remove(by_name, name);
1405 hashmap_steal_first_key(by_id);
1409 while ((name = hashmap_steal_first_key(by_name)))
1412 hashmap_free(by_name);
1413 hashmap_free(by_id);
1416 static void help(void) {
1417 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1418 "Creates system user accounts.\n\n"
1419 " -h --help Show this help\n"
1420 " --version Show package version\n"
1421 " --root=PATH Operate on an alternate filesystem root\n"
1422 , program_invocation_short_name);
1425 static int parse_argv(int argc, char *argv[]) {
1428 ARG_VERSION = 0x100,
1432 static const struct option options[] = {
1433 { "help", no_argument, NULL, 'h' },
1434 { "version", no_argument, NULL, ARG_VERSION },
1435 { "root", required_argument, NULL, ARG_ROOT },
1444 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1453 puts(PACKAGE_STRING);
1454 puts(SYSTEMD_FEATURES);
1459 arg_root = path_make_absolute_cwd(optarg);
1463 path_kill_slashes(arg_root);
1470 assert_not_reached("Unhandled option");
1476 int main(int argc, char *argv[]) {
1478 _cleanup_close_ int lock = -1;
1484 r = parse_argv(argc, argv);
1488 log_set_target(LOG_TARGET_AUTO);
1489 log_parse_environment();
1494 r = label_init(NULL);
1496 log_error("SELinux setup failed: %s", strerror(-r));
1500 if (optind < argc) {
1503 for (j = optind; j < argc; j++) {
1504 k = read_config_file(argv[j], false);
1505 if (k < 0 && r == 0)
1509 _cleanup_strv_free_ char **files = NULL;
1512 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1514 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1518 STRV_FOREACH(f, files) {
1519 k = read_config_file(*f, true);
1520 if (k < 0 && r == 0)
1529 lock = take_password_lock(arg_root);
1531 log_error("Failed to take lock: %s", strerror(-lock));
1535 r = load_user_database();
1537 log_error("Failed to load user database: %s", strerror(-r));
1541 r = load_group_database();
1543 log_error("Failed to read group database: %s", strerror(-r));
1547 HASHMAP_FOREACH(i, groups, iterator)
1550 HASHMAP_FOREACH(i, users, iterator)
1555 log_error("Failed to write files: %s", strerror(-r));
1558 while ((i = hashmap_steal_first(groups)))
1561 while ((i = hashmap_steal_first(users)))
1564 while ((n = hashmap_first_key(members))) {
1565 strv_free(hashmap_steal_first(members));
1569 hashmap_free(groups);
1570 hashmap_free(users);
1571 hashmap_free(members);
1572 hashmap_free(todo_uids);
1573 hashmap_free(todo_gids);
1575 free_database(database_user, database_uid);
1576 free_database(database_group, database_gid);
1580 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;