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 typedef enum ItemType {
62 static char *arg_root = NULL;
64 static const char conf_file_dirs[] =
67 "/usr/local/lib/sysusers.d\0"
68 "/usr/lib/sysusers.d\0"
74 static Hashmap *users = NULL, *groups = NULL;
75 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
76 static Hashmap *members = NULL;
78 static Hashmap *database_uid = NULL, *database_user = NULL;
79 static Hashmap *database_gid = NULL, *database_group = NULL;
81 static uid_t search_uid = SYSTEM_UID_MAX;
82 static gid_t search_gid = SYSTEM_GID_MAX;
84 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
85 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
87 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
88 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
90 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
92 static int load_user_database(void) {
93 _cleanup_fclose_ FILE *f = NULL;
94 const char *passwd_path;
98 passwd_path = fix_root("/etc/passwd");
99 f = fopen(passwd_path, "re");
101 return errno == ENOENT ? 0 : -errno;
103 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
107 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
112 while ((pw = fgetpwent(f))) {
116 n = strdup(pw->pw_name);
120 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
121 if (k < 0 && k != -EEXIST) {
126 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
127 if (q < 0 && q != -EEXIST) {
138 if (!IN_SET(errno, 0, ENOENT))
144 static int load_group_database(void) {
145 _cleanup_fclose_ FILE *f = NULL;
146 const char *group_path;
150 group_path = fix_root("/etc/group");
151 f = fopen(group_path, "re");
153 return errno == ENOENT ? 0 : -errno;
155 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
159 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
164 while ((gr = fgetgrent(f))) {
168 n = strdup(gr->gr_name);
172 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
173 if (k < 0 && k != -EEXIST) {
178 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
179 if (q < 0 && q != -EEXIST) {
190 if (!IN_SET(errno, 0, ENOENT))
196 static int make_backup(const char *x) {
197 _cleanup_close_ int src = -1, dst = -1;
199 struct timespec ts[2];
203 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
205 if (errno == ENOENT) /* No backup necessary... */
211 if (fstat(src, &st) < 0)
214 temp = strappenda(x, ".XXXXXX");
215 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
219 r = copy_bytes(src, dst, (off_t) -1);
223 /* Copy over the access mask */
224 if (fchmod(dst, st.st_mode & 07777) < 0) {
229 /* Don't fail on chmod(). If it stays owned by us, then it
230 * isn't too bad... */
231 fchown(dst, st.st_uid, st.st_gid);
237 backup = strappenda(x, "-");
238 if (rename(temp, backup) < 0)
248 static int putgrent_with_members(const struct group *gr, FILE *group) {
254 a = hashmap_get(members, gr->gr_name);
256 _cleanup_strv_free_ char **l = NULL;
260 l = strv_copy(gr->gr_mem);
265 if (strv_find(l, *i))
268 if (strv_extend(&l, *i) < 0)
284 if (putgrent(&t, group) != 0)
285 return errno ? -errno : -EIO;
292 if (putgrent(gr, group) != 0)
293 return errno ? -errno : -EIO;
298 static int write_files(void) {
300 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
301 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
302 const char *passwd_path = NULL, *group_path = NULL;
303 bool group_changed = false;
308 /* We don't patch /etc/shadow or /etc/gshadow here, since we
309 * only create user accounts without passwords anyway. */
311 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
312 _cleanup_fclose_ FILE *original = NULL;
314 group_path = fix_root("/etc/group");
315 r = label_context_set("/etc/group", S_IFREG);
318 r = fopen_temporary(group_path, &group, &group_tmp);
319 label_context_clear();
323 if (fchmod(fileno(group), 0644) < 0) {
328 original = fopen(group_path, "re");
333 while ((gr = fgetgrent(original))) {
334 /* Safety checks against name and GID
335 * collisions. Normally, this should
336 * be unnecessary, but given that we
337 * look at the entries anyway here,
338 * let's make an extra verification
339 * step that we don't generate
340 * duplicate entries. */
342 i = hashmap_get(groups, gr->gr_name);
343 if (i && i->todo_group) {
348 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
353 r = putgrent_with_members(gr, group);
358 group_changed = true;
362 if (!IN_SET(errno, 0, ENOENT)) {
367 } else if (errno != ENOENT) {
372 HASHMAP_FOREACH(i, todo_gids, iterator) {
376 .gr_passwd = (char*) "x",
379 r = putgrent_with_members(&n, group);
383 group_changed = true;
386 r = fflush_and_check(group);
391 if (hashmap_size(todo_uids) > 0) {
392 _cleanup_fclose_ FILE *original = NULL;
394 passwd_path = fix_root("/etc/passwd");
395 r = label_context_set("/etc/passwd", S_IFREG);
398 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
399 label_context_clear();
404 if (fchmod(fileno(passwd), 0644) < 0) {
409 original = fopen(passwd_path, "re");
414 while ((pw = fgetpwent(original))) {
416 i = hashmap_get(users, pw->pw_name);
417 if (i && i->todo_user) {
422 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
428 if (putpwent(pw, passwd) < 0) {
429 r = errno ? -errno : -EIO;
435 if (!IN_SET(errno, 0, ENOENT)) {
440 } else if (errno != ENOENT) {
445 HASHMAP_FOREACH(i, todo_uids, iterator) {
450 .pw_gecos = i->description,
451 .pw_passwd = (char*) "x",
454 /* Initialize the home directory and the shell
455 * to nologin, with one exception: for root we
456 * patch in something special */
458 n.pw_shell = (char*) "/bin/sh";
459 n.pw_dir = (char*) "/root";
461 n.pw_shell = (char*) "/sbin/nologin";
462 n.pw_dir = (char*) "/";
466 if (putpwent(&n, passwd) != 0) {
467 r = errno ? -errno : -EIO;
472 r = fflush_and_check(passwd);
477 /* Make a backup of the old files */
478 if (group && group_changed) {
479 r = make_backup(group_path);
485 r = make_backup(passwd_path);
490 /* And make the new files count */
491 if (group && group_changed) {
492 if (rename(group_tmp, group_path) < 0) {
502 if (rename(passwd_tmp, passwd_path) < 0) {
522 static int uid_is_ok(uid_t uid, const char *name) {
528 /* Let's see if we already have assigned the UID a second time */
529 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
532 /* Try to avoid using uids that are already used by a group
533 * that doesn't have the same name as our new user. */
534 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
535 if (i && !streq(i->name, name))
538 /* Let's check the files directly */
539 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
542 n = hashmap_get(database_gid, GID_TO_PTR(uid));
543 if (n && !streq(n, name))
546 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
552 if (!IN_SET(errno, 0, ENOENT))
556 g = getgrgid((gid_t) uid);
558 if (!streq(g->gr_name, name))
560 } else if (!IN_SET(errno, 0, ENOENT))
567 static int root_stat(const char *p, struct stat *st) {
571 if (stat(fix, st) < 0)
577 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
579 bool found_uid = false, found_gid = false;
585 /* First, try to get the gid directly */
586 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
591 /* Then, try to get the uid directly */
592 if ((_uid || (_gid && !found_gid))
594 && root_stat(i->uid_path, &st) >= 0) {
599 /* If we need the gid, but had no success yet, also derive it from the uid path */
600 if (_gid && !found_gid) {
606 /* If that didn't work yet, then let's reuse the gid as uid */
607 if (_uid && !found_uid && i->gid_path) {
612 } else if (root_stat(i->gid_path, &st) >= 0) {
613 uid = (uid_t) st.st_gid;
635 static int add_user(Item *i) {
641 /* Check the database directly */
642 z = hashmap_get(database_user, i->name);
644 log_debug("User %s already exists.", i->name);
645 i->uid = PTR_TO_UID(z);
656 p = getpwnam(i->name);
658 log_debug("User %s already exists.", i->name);
662 free(i->description);
663 i->description = strdup(p->pw_gecos);
666 if (!IN_SET(errno, 0, ENOENT)) {
667 log_error("Failed to check if user %s already exists: %m", i->name);
671 /* And shadow too, just to be sure */
673 sp = getspnam(i->name);
675 log_error("User %s already exists in shadow database, but not in user database.", i->name);
678 if (!IN_SET(errno, 0, ENOENT)) {
679 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
684 /* Try to use the suggested numeric uid */
686 r = uid_is_ok(i->uid, i->name);
688 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
692 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
697 /* If that didn't work, try to read it from the specified path */
701 if (read_id_from_file(i, &c, NULL) > 0) {
703 if (c <= 0 || c > SYSTEM_UID_MAX)
704 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
706 r = uid_is_ok(c, i->name);
708 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
714 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
719 /* Otherwise try to reuse the group ID */
720 if (!i->uid_set && i->gid_set) {
721 r = uid_is_ok((uid_t) i->gid, i->name);
723 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
727 i->uid = (uid_t) i->gid;
732 /* And if that didn't work either, let's try to find a free one */
734 for (; search_uid > 0; search_uid--) {
736 r = uid_is_ok(search_uid, i->name);
738 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
744 if (search_uid <= 0) {
745 log_error("No free user ID available for %s.", i->name);
755 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
759 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
764 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
769 static int gid_is_ok(gid_t gid) {
773 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
776 /* Avoid reusing gids that are already used by a different user */
777 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
780 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
783 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
791 if (!IN_SET(errno, 0, ENOENT))
795 p = getpwuid((uid_t) gid);
798 if (!IN_SET(errno, 0, ENOENT))
805 static int add_group(Item *i) {
811 /* Check the database directly */
812 z = hashmap_get(database_group, i->name);
814 log_debug("Group %s already exists.", i->name);
815 i->gid = PTR_TO_GID(z);
825 g = getgrnam(i->name);
827 log_debug("Group %s already exists.", i->name);
832 if (!IN_SET(errno, 0, ENOENT)) {
833 log_error("Failed to check if group %s already exists: %m", i->name);
838 /* Try to use the suggested numeric gid */
840 r = gid_is_ok(i->gid);
842 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
846 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
851 /* Try to reuse the numeric uid, if there's one */
852 if (!i->gid_set && i->uid_set) {
853 r = gid_is_ok((gid_t) i->uid);
855 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
859 i->gid = (gid_t) i->uid;
864 /* If that didn't work, try to read it from the specified path */
868 if (read_id_from_file(i, NULL, &c) > 0) {
870 if (c <= 0 || c > SYSTEM_GID_MAX)
871 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
875 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
881 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
886 /* And if that didn't work either, let's try to find a free one */
888 for (; search_gid > 0; search_gid--) {
889 r = gid_is_ok(search_gid);
891 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
897 if (search_gid <= 0) {
898 log_error("No free group ID available for %s.", i->name);
908 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
912 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
916 i->todo_group = true;
917 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
922 static int process_item(Item *i) {
939 j = hashmap_get(users, i->name);
941 /* There's already user to be created for this
942 * name, let's process that in one step */
951 j->gid_path = strdup(i->gid_path);
963 assert_not_reached("Unknown item type");
967 static void item_free(Item *i) {
975 free(i->description);
979 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
981 static int add_implicit(void) {
986 /* Implicitly create additional users and groups, if they were listed in "m" lines */
988 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
992 i = hashmap_get(groups, g);
994 _cleanup_(item_freep) Item *j = NULL;
996 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1004 j->type = ADD_GROUP;
1005 j->name = strdup(g);
1009 r = hashmap_put(groups, j->name, j);
1013 log_debug("Adding implicit group '%s' due to m line", j->name);
1017 STRV_FOREACH(m, l) {
1019 i = hashmap_get(users, *m);
1021 _cleanup_(item_freep) Item *j = NULL;
1023 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1032 j->name = strdup(*m);
1036 r = hashmap_put(users, j->name, j);
1040 log_debug("Adding implicit user '%s' due to m line", j->name);
1049 static bool item_equal(Item *a, Item *b) {
1053 if (a->type != b->type)
1056 if (!streq_ptr(a->name, b->name))
1059 if (!streq_ptr(a->uid_path, b->uid_path))
1062 if (!streq_ptr(a->gid_path, b->gid_path))
1065 if (!streq_ptr(a->description, b->description))
1068 if (a->uid_set != b->uid_set)
1071 if (a->uid_set && a->uid != b->uid)
1074 if (a->gid_set != b->gid_set)
1077 if (a->gid_set && a->gid != b->gid)
1083 static bool valid_user_group_name(const char *u) {
1090 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1091 !(u[0] >= 'A' && u[0] <= 'Z') &&
1095 for (i = u+1; *i; i++) {
1096 if (!(*i >= 'a' && *i <= 'z') &&
1097 !(*i >= 'A' && *i <= 'Z') &&
1098 !(*i >= '0' && *i <= '9') &&
1104 sz = sysconf(_SC_LOGIN_NAME_MAX);
1107 if ((size_t) (i-u) > (size_t) sz)
1110 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1116 static bool valid_gecos(const char *d) {
1118 if (!utf8_is_valid(d))
1121 if (string_has_cc(d, NULL))
1124 /* Colons are used as field separators, and hence not OK */
1131 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1133 static const Specifier specifier_table[] = {
1134 { 'm', specifier_machine_id, NULL },
1135 { 'b', specifier_boot_id, NULL },
1136 { 'H', specifier_host_name, NULL },
1137 { 'v', specifier_kernel_release, NULL },
1141 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1142 _cleanup_(item_freep) Item *i = NULL;
1158 log_error("[%s:%u] Syntax error.", fname, line);
1162 if (strlen(action) != 1) {
1163 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1167 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1168 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1172 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1174 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1178 if (!valid_user_group_name(resolved_name)) {
1179 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1184 n += strspn(buffer+n, WHITESPACE);
1186 if (STR_IN_SET(buffer + n, "", "-"))
1190 switch (action[0]) {
1193 _cleanup_free_ char *resolved_id = NULL;
1196 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1200 /* Try to extend an existing member or group item */
1202 if (!id || streq(id, "-")) {
1203 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1207 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1209 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1213 if (!valid_user_group_name(resolved_id)) {
1214 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1219 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1223 l = hashmap_get(members, resolved_id);
1225 /* A list for this group name already exists, let's append to it */
1226 r = strv_push(&l, resolved_name);
1230 resolved_name = NULL;
1232 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1234 /* No list for this group name exists yet, create one */
1236 l = new0(char *, 2);
1240 l[0] = resolved_name;
1243 r = hashmap_put(members, resolved_id, l);
1249 resolved_id = resolved_name = NULL;
1256 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1264 if (id && !streq(id, "-")) {
1266 if (path_is_absolute(id)) {
1267 i->uid_path = strdup(id);
1271 path_kill_slashes(i->uid_path);
1274 r = parse_uid(id, &i->uid);
1276 log_error("Failed to parse UID: %s", id);
1285 i->description = unquote(buffer+n, "\"");
1286 if (!i->description)
1289 if (!valid_gecos(i->description)) {
1290 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1299 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1304 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1312 if (id && !streq(id, "-")) {
1314 if (path_is_absolute(id)) {
1315 i->gid_path = strdup(id);
1319 path_kill_slashes(i->gid_path);
1321 r = parse_gid(id, &i->gid);
1323 log_error("Failed to parse GID: %s", id);
1338 i->type = action[0];
1339 i->name = resolved_name;
1340 resolved_name = NULL;
1342 existing = hashmap_get(h, i->name);
1345 /* Two identical items are fine */
1346 if (!item_equal(existing, i))
1347 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1352 r = hashmap_put(h, i->name, i);
1360 static int read_config_file(const char *fn, bool ignore_enoent) {
1361 _cleanup_fclose_ FILE *f = NULL;
1362 char line[LINE_MAX];
1368 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1370 if (ignore_enoent && r == -ENOENT)
1373 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1377 FOREACH_LINE(line, f, break) {
1384 if (*l == '#' || *l == 0)
1387 k = parse_line(fn, v, l);
1388 if (k < 0 && r == 0)
1393 log_error("Failed to read from file %s: %m", fn);
1401 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1405 name = hashmap_first(by_id);
1409 hashmap_remove(by_name, name);
1411 hashmap_steal_first_key(by_id);
1415 while ((name = hashmap_steal_first_key(by_name)))
1418 hashmap_free(by_name);
1419 hashmap_free(by_id);
1422 static int help(void) {
1424 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1425 "Creates system user accounts.\n\n"
1426 " -h --help Show this help\n"
1427 " --version Show package version\n"
1428 " --root=PATH Operate on an alternate filesystem root\n",
1429 program_invocation_short_name);
1434 static int parse_argv(int argc, char *argv[]) {
1437 ARG_VERSION = 0x100,
1441 static const struct option options[] = {
1442 { "help", no_argument, NULL, 'h' },
1443 { "version", no_argument, NULL, ARG_VERSION },
1444 { "root", required_argument, NULL, ARG_ROOT },
1453 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1461 puts(PACKAGE_STRING);
1462 puts(SYSTEMD_FEATURES);
1467 arg_root = path_make_absolute_cwd(optarg);
1471 path_kill_slashes(arg_root);
1478 assert_not_reached("Unhandled option");
1485 int main(int argc, char *argv[]) {
1487 _cleanup_close_ int lock = -1;
1493 r = parse_argv(argc, argv);
1497 log_set_target(LOG_TARGET_AUTO);
1498 log_parse_environment();
1507 if (optind < argc) {
1510 for (j = optind; j < argc; j++) {
1511 k = read_config_file(argv[j], false);
1512 if (k < 0 && r == 0)
1516 _cleanup_strv_free_ char **files = NULL;
1519 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1521 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1525 STRV_FOREACH(f, files) {
1526 k = read_config_file(*f, true);
1527 if (k < 0 && r == 0)
1536 lock = take_password_lock(arg_root);
1538 log_error("Failed to take lock: %s", strerror(-lock));
1542 r = load_user_database();
1544 log_error("Failed to load user database: %s", strerror(-r));
1548 r = load_group_database();
1550 log_error("Failed to read group database: %s", strerror(-r));
1554 HASHMAP_FOREACH(i, groups, iterator)
1557 HASHMAP_FOREACH(i, users, iterator)
1562 log_error("Failed to write files: %s", strerror(-r));
1565 while ((i = hashmap_steal_first(groups)))
1568 while ((i = hashmap_steal_first(users)))
1571 while ((n = hashmap_first_key(members))) {
1572 strv_free(hashmap_steal_first(members));
1576 hashmap_free(groups);
1577 hashmap_free(users);
1578 hashmap_free(members);
1579 hashmap_free(todo_uids);
1580 hashmap_free(todo_gids);
1582 free_database(database_user, database_uid);
1583 free_database(database_group, database_gid);
1587 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;