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 = fopen_temporary(group_path, &group, &group_tmp);
319 if (fchmod(fileno(group), 0644) < 0) {
324 original = fopen(group_path, "re");
329 while ((gr = fgetgrent(original))) {
330 /* Safety checks against name and GID
331 * collisions. Normally, this should
332 * be unnecessary, but given that we
333 * look at the entries anyway here,
334 * let's make an extra verification
335 * step that we don't generate
336 * duplicate entries. */
338 i = hashmap_get(groups, gr->gr_name);
339 if (i && i->todo_group) {
344 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
349 r = putgrent_with_members(gr, group);
354 group_changed = true;
358 if (!IN_SET(errno, 0, ENOENT)) {
363 } else if (errno != ENOENT) {
368 HASHMAP_FOREACH(i, todo_gids, iterator) {
372 .gr_passwd = (char*) "x",
375 r = putgrent_with_members(&n, group);
379 group_changed = true;
382 r = fflush_and_check(group);
387 if (hashmap_size(todo_uids) > 0) {
388 _cleanup_fclose_ FILE *original = NULL;
390 passwd_path = fix_root("/etc/passwd");
391 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
395 if (fchmod(fileno(passwd), 0644) < 0) {
400 original = fopen(passwd_path, "re");
405 while ((pw = fgetpwent(original))) {
407 i = hashmap_get(users, pw->pw_name);
408 if (i && i->todo_user) {
413 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
419 if (putpwent(pw, passwd) < 0) {
420 r = errno ? -errno : -EIO;
426 if (!IN_SET(errno, 0, ENOENT)) {
431 } else if (errno != ENOENT) {
436 HASHMAP_FOREACH(i, todo_uids, iterator) {
441 .pw_gecos = i->description,
442 .pw_passwd = (char*) "x",
445 /* Initialize the home directory and the shell
446 * to nologin, with one exception: for root we
447 * patch in something special */
449 n.pw_shell = (char*) "/bin/sh";
450 n.pw_dir = (char*) "/root";
452 n.pw_shell = (char*) "/sbin/nologin";
453 n.pw_dir = (char*) "/";
457 if (putpwent(&n, passwd) != 0) {
458 r = errno ? -errno : -EIO;
463 r = fflush_and_check(passwd);
468 /* Make a backup of the old files */
469 if (group && group_changed) {
470 r = make_backup(group_path);
476 r = make_backup(passwd_path);
481 /* And make the new files count */
482 if (group && group_changed) {
483 if (rename(group_tmp, group_path) < 0) {
493 if (rename(passwd_tmp, passwd_path) < 0) {
513 static int uid_is_ok(uid_t uid, const char *name) {
519 /* Let's see if we already have assigned the UID a second time */
520 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
523 /* Try to avoid using uids that are already used by a group
524 * that doesn't have the same name as our new user. */
525 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
526 if (i && !streq(i->name, name))
529 /* Let's check the files directly */
530 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
533 n = hashmap_get(database_gid, GID_TO_PTR(uid));
534 if (n && !streq(n, name))
537 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
543 if (!IN_SET(errno, 0, ENOENT))
547 g = getgrgid((gid_t) uid);
549 if (!streq(g->gr_name, name))
551 } else if (!IN_SET(errno, 0, ENOENT))
558 static int root_stat(const char *p, struct stat *st) {
562 if (stat(fix, st) < 0)
568 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
570 bool found_uid = false, found_gid = false;
576 /* First, try to get the gid directly */
577 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
582 /* Then, try to get the uid directly */
583 if ((_uid || (_gid && !found_gid))
585 && root_stat(i->uid_path, &st) >= 0) {
590 /* If we need the gid, but had no success yet, also derive it from the uid path */
591 if (_gid && !found_gid) {
597 /* If that didn't work yet, then let's reuse the gid as uid */
598 if (_uid && !found_uid && i->gid_path) {
603 } else if (root_stat(i->gid_path, &st) >= 0) {
604 uid = (uid_t) st.st_gid;
626 static int add_user(Item *i) {
632 /* Check the database directly */
633 z = hashmap_get(database_user, i->name);
635 log_debug("User %s already exists.", i->name);
636 i->uid = PTR_TO_UID(z);
647 p = getpwnam(i->name);
649 log_debug("User %s already exists.", i->name);
653 free(i->description);
654 i->description = strdup(p->pw_gecos);
657 if (!IN_SET(errno, 0, ENOENT)) {
658 log_error("Failed to check if user %s already exists: %m", i->name);
662 /* And shadow too, just to be sure */
664 sp = getspnam(i->name);
666 log_error("User %s already exists in shadow database, but not in user database.", i->name);
669 if (!IN_SET(errno, 0, ENOENT)) {
670 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
675 /* Try to use the suggested numeric uid */
677 r = uid_is_ok(i->uid, i->name);
679 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
683 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
688 /* If that didn't work, try to read it from the specified path */
692 if (read_id_from_file(i, &c, NULL) > 0) {
694 if (c <= 0 || c > SYSTEM_UID_MAX)
695 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
697 r = uid_is_ok(c, i->name);
699 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
705 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
710 /* Otherwise try to reuse the group ID */
711 if (!i->uid_set && i->gid_set) {
712 r = uid_is_ok((uid_t) i->gid, i->name);
714 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
718 i->uid = (uid_t) i->gid;
723 /* And if that didn't work either, let's try to find a free one */
725 for (; search_uid > 0; search_uid--) {
727 r = uid_is_ok(search_uid, i->name);
729 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
735 if (search_uid <= 0) {
736 log_error("No free user ID available for %s.", i->name);
746 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
750 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
755 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
760 static int gid_is_ok(gid_t gid) {
764 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
767 /* Avoid reusing gids that are already used by a different user */
768 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
771 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
774 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
782 if (!IN_SET(errno, 0, ENOENT))
786 p = getpwuid((uid_t) gid);
789 if (!IN_SET(errno, 0, ENOENT))
796 static int add_group(Item *i) {
802 /* Check the database directly */
803 z = hashmap_get(database_group, i->name);
805 log_debug("Group %s already exists.", i->name);
806 i->gid = PTR_TO_GID(z);
816 g = getgrnam(i->name);
818 log_debug("Group %s already exists.", i->name);
823 if (!IN_SET(errno, 0, ENOENT)) {
824 log_error("Failed to check if group %s already exists: %m", i->name);
829 /* Try to use the suggested numeric gid */
831 r = gid_is_ok(i->gid);
833 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
837 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
842 /* Try to reuse the numeric uid, if there's one */
843 if (!i->gid_set && i->uid_set) {
844 r = gid_is_ok((gid_t) i->uid);
846 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
850 i->gid = (gid_t) i->uid;
855 /* If that didn't work, try to read it from the specified path */
859 if (read_id_from_file(i, NULL, &c) > 0) {
861 if (c <= 0 || c > SYSTEM_GID_MAX)
862 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
866 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
872 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
877 /* And if that didn't work either, let's try to find a free one */
879 for (; search_gid > 0; search_gid--) {
880 r = gid_is_ok(search_gid);
882 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
888 if (search_gid <= 0) {
889 log_error("No free group ID available for %s.", i->name);
899 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
903 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
907 i->todo_group = true;
908 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
913 static int process_item(Item *i) {
930 j = hashmap_get(users, i->name);
932 /* There's already user to be created for this
933 * name, let's process that in one step */
942 j->gid_path = strdup(i->gid_path);
954 assert_not_reached("Unknown item type");
958 static void item_free(Item *i) {
966 free(i->description);
970 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
972 static int add_implicit(void) {
977 /* Implicitly create additional users and groups, if they were listed in "m" lines */
979 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
983 i = hashmap_get(groups, g);
985 _cleanup_(item_freep) Item *j = NULL;
987 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1000 r = hashmap_put(groups, j->name, j);
1004 log_debug("Adding implicit group '%s' due to m line", j->name);
1008 STRV_FOREACH(m, l) {
1010 i = hashmap_get(users, *m);
1012 _cleanup_(item_freep) Item *j = NULL;
1014 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1023 j->name = strdup(*m);
1027 r = hashmap_put(users, j->name, j);
1031 log_debug("Adding implicit user '%s' due to m line", j->name);
1040 static bool item_equal(Item *a, Item *b) {
1044 if (a->type != b->type)
1047 if (!streq_ptr(a->name, b->name))
1050 if (!streq_ptr(a->uid_path, b->uid_path))
1053 if (!streq_ptr(a->gid_path, b->gid_path))
1056 if (!streq_ptr(a->description, b->description))
1059 if (a->uid_set != b->uid_set)
1062 if (a->uid_set && a->uid != b->uid)
1065 if (a->gid_set != b->gid_set)
1068 if (a->gid_set && a->gid != b->gid)
1074 static bool valid_user_group_name(const char *u) {
1081 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1082 !(u[0] >= 'A' && u[0] <= 'Z') &&
1086 for (i = u+1; *i; i++) {
1087 if (!(*i >= 'a' && *i <= 'z') &&
1088 !(*i >= 'A' && *i <= 'Z') &&
1089 !(*i >= '0' && *i <= '9') &&
1095 sz = sysconf(_SC_LOGIN_NAME_MAX);
1098 if ((size_t) (i-u) > (size_t) sz)
1101 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1107 static bool valid_gecos(const char *d) {
1109 if (!utf8_is_valid(d))
1112 if (string_has_cc(d, NULL))
1115 /* Colons are used as field separators, and hence not OK */
1122 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1124 static const Specifier specifier_table[] = {
1125 { 'm', specifier_machine_id, NULL },
1126 { 'b', specifier_boot_id, NULL },
1127 { 'H', specifier_host_name, NULL },
1128 { 'v', specifier_kernel_release, NULL },
1132 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1133 _cleanup_(item_freep) Item *i = NULL;
1149 log_error("[%s:%u] Syntax error.", fname, line);
1153 if (strlen(action) != 1) {
1154 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1158 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1159 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1163 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1165 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1169 if (!valid_user_group_name(resolved_name)) {
1170 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1175 n += strspn(buffer+n, WHITESPACE);
1177 if (STR_IN_SET(buffer + n, "", "-"))
1181 switch (action[0]) {
1184 _cleanup_free_ char *resolved_id = NULL;
1187 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1191 /* Try to extend an existing member or group item */
1193 if (!id || streq(id, "-")) {
1194 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1198 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1200 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1204 if (!valid_user_group_name(resolved_id)) {
1205 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1210 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1214 l = hashmap_get(members, resolved_id);
1216 /* A list for this group name already exists, let's append to it */
1217 r = strv_push(&l, resolved_name);
1221 resolved_name = NULL;
1223 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1225 /* No list for this group name exists yet, create one */
1227 l = new0(char *, 2);
1231 l[0] = resolved_name;
1234 r = hashmap_put(members, resolved_id, l);
1240 resolved_id = resolved_name = NULL;
1247 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1255 if (id && !streq(id, "-")) {
1257 if (path_is_absolute(id)) {
1258 i->uid_path = strdup(id);
1262 path_kill_slashes(i->uid_path);
1265 r = parse_uid(id, &i->uid);
1267 log_error("Failed to parse UID: %s", id);
1276 i->description = unquote(buffer+n, "\"");
1277 if (!i->description)
1280 if (!valid_gecos(i->description)) {
1281 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1290 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1295 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1303 if (id && !streq(id, "-")) {
1305 if (path_is_absolute(id)) {
1306 i->gid_path = strdup(id);
1310 path_kill_slashes(i->gid_path);
1312 r = parse_gid(id, &i->gid);
1314 log_error("Failed to parse GID: %s", id);
1329 i->type = action[0];
1330 i->name = resolved_name;
1331 resolved_name = NULL;
1333 existing = hashmap_get(h, i->name);
1336 /* Two identical items are fine */
1337 if (!item_equal(existing, i))
1338 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1343 r = hashmap_put(h, i->name, i);
1351 static int read_config_file(const char *fn, bool ignore_enoent) {
1352 _cleanup_fclose_ FILE *f = NULL;
1353 char line[LINE_MAX];
1359 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1361 if (ignore_enoent && r == -ENOENT)
1364 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1368 FOREACH_LINE(line, f, break) {
1375 if (*l == '#' || *l == 0)
1378 k = parse_line(fn, v, l);
1379 if (k < 0 && r == 0)
1384 log_error("Failed to read from file %s: %m", fn);
1392 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1396 name = hashmap_first(by_id);
1400 hashmap_remove(by_name, name);
1402 hashmap_steal_first_key(by_id);
1406 while ((name = hashmap_steal_first_key(by_name)))
1409 hashmap_free(by_name);
1410 hashmap_free(by_id);
1413 static int help(void) {
1415 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1416 "Creates system user accounts.\n\n"
1417 " -h --help Show this help\n"
1418 " --version Show package version\n"
1419 " --root=PATH Operate on an alternate filesystem root\n",
1420 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) {
1452 puts(PACKAGE_STRING);
1453 puts(SYSTEMD_FEATURES);
1458 arg_root = path_make_absolute_cwd(optarg);
1462 path_kill_slashes(arg_root);
1469 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();
1496 if (optind < argc) {
1499 for (j = optind; j < argc; j++) {
1500 k = read_config_file(argv[j], false);
1501 if (k < 0 && r == 0)
1505 _cleanup_strv_free_ char **files = NULL;
1508 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1510 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1514 STRV_FOREACH(f, files) {
1515 k = read_config_file(*f, true);
1516 if (k < 0 && r == 0)
1525 lock = take_password_lock(arg_root);
1527 log_error("Failed to take lock: %s", strerror(-lock));
1531 r = load_user_database();
1533 log_error("Failed to load user database: %s", strerror(-r));
1537 r = load_group_database();
1539 log_error("Failed to read group database: %s", strerror(-r));
1543 HASHMAP_FOREACH(i, groups, iterator)
1546 HASHMAP_FOREACH(i, users, iterator)
1551 log_error("Failed to write files: %s", strerror(-r));
1554 while ((i = hashmap_steal_first(groups)))
1557 while ((i = hashmap_steal_first(users)))
1560 while ((n = hashmap_first_key(members))) {
1561 strv_free(hashmap_steal_first(members));
1565 hashmap_free(groups);
1566 hashmap_free(users);
1567 hashmap_free(members);
1568 hashmap_free(todo_uids);
1569 hashmap_free(todo_gids);
1571 free_database(database_user, database_uid);
1572 free_database(database_group, database_gid);
1576 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;