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[] =
65 "/usr/local/lib/sysusers.d\0"
66 "/usr/lib/sysusers.d\0"
72 static Hashmap *users = NULL, *groups = NULL;
73 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
74 static Hashmap *members = NULL;
76 static Hashmap *database_uid = NULL, *database_user = NULL;
77 static Hashmap *database_gid = NULL, *database_group = NULL;
79 static uid_t search_uid = SYSTEM_UID_MAX;
80 static gid_t search_gid = SYSTEM_GID_MAX;
82 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
83 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
85 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
86 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
88 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
90 static int load_user_database(void) {
91 _cleanup_fclose_ FILE *f = NULL;
92 const char *passwd_path;
96 passwd_path = fix_root("/etc/passwd");
97 f = fopen(passwd_path, "re");
99 return errno == ENOENT ? 0 : -errno;
101 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
105 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
110 while ((pw = fgetpwent(f))) {
114 n = strdup(pw->pw_name);
118 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
119 if (k < 0 && k != -EEXIST) {
124 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
125 if (q < 0 && q != -EEXIST) {
136 if (!IN_SET(errno, 0, ENOENT))
142 static int load_group_database(void) {
143 _cleanup_fclose_ FILE *f = NULL;
144 const char *group_path;
148 group_path = fix_root("/etc/group");
149 f = fopen(group_path, "re");
151 return errno == ENOENT ? 0 : -errno;
153 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
157 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
162 while ((gr = fgetgrent(f))) {
166 n = strdup(gr->gr_name);
170 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
171 if (k < 0 && k != -EEXIST) {
176 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
177 if (q < 0 && q != -EEXIST) {
188 if (!IN_SET(errno, 0, ENOENT))
194 static int make_backup(const char *x) {
195 _cleanup_close_ int src = -1, dst = -1;
197 struct timespec ts[2];
201 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
203 if (errno == ENOENT) /* No backup necessary... */
209 if (fstat(src, &st) < 0)
212 temp = strappenda(x, ".XXXXXX");
213 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
217 r = copy_bytes(src, dst, (off_t) -1);
221 /* Copy over the access mask */
222 if (fchmod(dst, st.st_mode & 07777) < 0) {
227 /* Don't fail on chmod(). If it stays owned by us, then it
228 * isn't too bad... */
229 fchown(dst, st.st_uid, st.st_gid);
235 backup = strappenda(x, "-");
236 if (rename(temp, backup) < 0)
246 static int putgrent_with_members(const struct group *gr, FILE *group) {
252 a = hashmap_get(members, gr->gr_name);
254 _cleanup_strv_free_ char **l = NULL;
258 l = strv_copy(gr->gr_mem);
263 if (strv_find(l, *i))
266 if (strv_extend(&l, *i) < 0)
282 if (putgrent(&t, group) != 0)
283 return errno ? -errno : -EIO;
290 if (putgrent(gr, group) != 0)
291 return errno ? -errno : -EIO;
296 static int write_files(void) {
298 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
299 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
300 const char *passwd_path = NULL, *group_path = NULL;
301 bool group_changed = false;
306 /* We don't patch /etc/shadow or /etc/gshadow here, since we
307 * only create user accounts without passwords anyway. */
309 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
310 _cleanup_fclose_ FILE *original = NULL;
312 group_path = fix_root("/etc/group");
313 r = fopen_temporary(group_path, &group, &group_tmp);
317 if (fchmod(fileno(group), 0644) < 0) {
322 original = fopen(group_path, "re");
327 while ((gr = fgetgrent(original))) {
328 /* Safety checks against name and GID
329 * collisions. Normally, this should
330 * be unnecessary, but given that we
331 * look at the entries anyway here,
332 * let's make an extra verification
333 * step that we don't generate
334 * duplicate entries. */
336 i = hashmap_get(groups, gr->gr_name);
337 if (i && i->todo_group) {
342 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
347 r = putgrent_with_members(gr, group);
352 group_changed = true;
356 if (!IN_SET(errno, 0, ENOENT)) {
361 } else if (errno != ENOENT) {
366 HASHMAP_FOREACH(i, todo_gids, iterator) {
370 .gr_passwd = (char*) "x",
373 r = putgrent_with_members(&n, group);
377 group_changed = true;
380 r = fflush_and_check(group);
385 if (hashmap_size(todo_uids) > 0) {
386 _cleanup_fclose_ FILE *original = NULL;
388 passwd_path = fix_root("/etc/passwd");
389 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
393 if (fchmod(fileno(passwd), 0644) < 0) {
398 original = fopen(passwd_path, "re");
403 while ((pw = fgetpwent(original))) {
405 i = hashmap_get(users, pw->pw_name);
406 if (i && i->todo_user) {
411 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
417 if (putpwent(pw, passwd) < 0) {
418 r = errno ? -errno : -EIO;
424 if (!IN_SET(errno, 0, ENOENT)) {
429 } else if (errno != ENOENT) {
434 HASHMAP_FOREACH(i, todo_uids, iterator) {
439 .pw_gecos = i->description,
440 .pw_passwd = (char*) "x",
443 /* Initialize the home directory and the shell
444 * to nologin, with one exception: for root we
445 * patch in something special */
447 n.pw_shell = (char*) "/bin/sh";
448 n.pw_dir = (char*) "/root";
450 n.pw_shell = (char*) "/sbin/nologin";
451 n.pw_dir = (char*) "/";
455 if (putpwent(&n, passwd) != 0) {
456 r = errno ? -errno : -EIO;
461 r = fflush_and_check(passwd);
466 /* Make a backup of the old files */
467 if (group && group_changed) {
468 r = make_backup(group_path);
474 r = make_backup(passwd_path);
479 /* And make the new files count */
480 if (group && group_changed) {
481 if (rename(group_tmp, group_path) < 0) {
491 if (rename(passwd_tmp, passwd_path) < 0) {
511 static int uid_is_ok(uid_t uid, const char *name) {
517 /* Let's see if we already have assigned the UID a second time */
518 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
521 /* Try to avoid using uids that are already used by a group
522 * that doesn't have the same name as our new user. */
523 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
524 if (i && !streq(i->name, name))
527 /* Let's check the files directly */
528 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
531 n = hashmap_get(database_gid, GID_TO_PTR(uid));
532 if (n && !streq(n, name))
535 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
541 if (!IN_SET(errno, 0, ENOENT))
545 g = getgrgid((gid_t) uid);
547 if (!streq(g->gr_name, name))
549 } else if (!IN_SET(errno, 0, ENOENT))
556 static int root_stat(const char *p, struct stat *st) {
560 if (stat(fix, st) < 0)
566 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
568 bool found_uid = false, found_gid = false;
574 /* First, try to get the gid directly */
575 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
580 /* Then, try to get the uid directly */
581 if ((_uid || (_gid && !found_gid))
583 && root_stat(i->uid_path, &st) >= 0) {
588 /* If we need the gid, but had no success yet, also derive it from the uid path */
589 if (_gid && !found_gid) {
595 /* If that didn't work yet, then let's reuse the gid as uid */
596 if (_uid && !found_uid && i->gid_path) {
601 } else if (root_stat(i->gid_path, &st) >= 0) {
602 uid = (uid_t) st.st_gid;
624 static int add_user(Item *i) {
630 /* Check the database directly */
631 z = hashmap_get(database_user, i->name);
633 log_debug("User %s already exists.", i->name);
634 i->uid = PTR_TO_UID(z);
645 p = getpwnam(i->name);
647 log_debug("User %s already exists.", i->name);
651 free(i->description);
652 i->description = strdup(p->pw_gecos);
655 if (!IN_SET(errno, 0, ENOENT)) {
656 log_error("Failed to check if user %s already exists: %m", i->name);
660 /* And shadow too, just to be sure */
662 sp = getspnam(i->name);
664 log_error("User %s already exists in shadow database, but not in user database.", i->name);
667 if (!IN_SET(errno, 0, ENOENT)) {
668 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
673 /* Try to use the suggested numeric uid */
675 r = uid_is_ok(i->uid, i->name);
677 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
681 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
686 /* If that didn't work, try to read it from the specified path */
690 if (read_id_from_file(i, &c, NULL) > 0) {
692 if (c <= 0 || c > SYSTEM_UID_MAX)
693 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
695 r = uid_is_ok(c, i->name);
697 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
703 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
708 /* Otherwise try to reuse the group ID */
709 if (!i->uid_set && i->gid_set) {
710 r = uid_is_ok((uid_t) i->gid, i->name);
712 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
716 i->uid = (uid_t) i->gid;
721 /* And if that didn't work either, let's try to find a free one */
723 for (; search_uid > 0; search_uid--) {
725 r = uid_is_ok(search_uid, i->name);
727 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
733 if (search_uid <= 0) {
734 log_error("No free user ID available for %s.", i->name);
744 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
748 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
753 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
758 static int gid_is_ok(gid_t gid) {
762 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
765 /* Avoid reusing gids that are already used by a different user */
766 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
769 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
772 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
780 if (!IN_SET(errno, 0, ENOENT))
784 p = getpwuid((uid_t) gid);
787 if (!IN_SET(errno, 0, ENOENT))
794 static int add_group(Item *i) {
800 /* Check the database directly */
801 z = hashmap_get(database_group, i->name);
803 log_debug("Group %s already exists.", i->name);
804 i->gid = PTR_TO_GID(z);
814 g = getgrnam(i->name);
816 log_debug("Group %s already exists.", i->name);
821 if (!IN_SET(errno, 0, ENOENT)) {
822 log_error("Failed to check if group %s already exists: %m", i->name);
827 /* Try to use the suggested numeric gid */
829 r = gid_is_ok(i->gid);
831 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
835 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
840 /* Try to reuse the numeric uid, if there's one */
841 if (!i->gid_set && i->uid_set) {
842 r = gid_is_ok((gid_t) i->uid);
844 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
848 i->gid = (gid_t) i->uid;
853 /* If that didn't work, try to read it from the specified path */
857 if (read_id_from_file(i, NULL, &c) > 0) {
859 if (c <= 0 || c > SYSTEM_GID_MAX)
860 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
864 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
870 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
875 /* And if that didn't work either, let's try to find a free one */
877 for (; search_gid > 0; search_gid--) {
878 r = gid_is_ok(search_gid);
880 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
886 if (search_gid <= 0) {
887 log_error("No free group ID available for %s.", i->name);
897 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
901 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
905 i->todo_group = true;
906 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
911 static int process_item(Item *i) {
928 j = hashmap_get(users, i->name);
930 /* There's already user to be created for this
931 * name, let's process that in one step */
940 j->gid_path = strdup(i->gid_path);
952 assert_not_reached("Unknown item type");
956 static void item_free(Item *i) {
964 free(i->description);
968 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
970 static int add_implicit(void) {
975 /* Implicitly create additional users and groups, if they were listed in "m" lines */
977 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
981 i = hashmap_get(groups, g);
983 _cleanup_(item_freep) Item *j = NULL;
985 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
998 r = hashmap_put(groups, j->name, j);
1002 log_debug("Adding implicit group '%s' due to m line", j->name);
1006 STRV_FOREACH(m, l) {
1008 i = hashmap_get(users, *m);
1010 _cleanup_(item_freep) Item *j = NULL;
1012 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1021 j->name = strdup(*m);
1025 r = hashmap_put(users, j->name, j);
1029 log_debug("Adding implicit user '%s' due to m line", j->name);
1038 static bool item_equal(Item *a, Item *b) {
1042 if (a->type != b->type)
1045 if (!streq_ptr(a->name, b->name))
1048 if (!streq_ptr(a->uid_path, b->uid_path))
1051 if (!streq_ptr(a->gid_path, b->gid_path))
1054 if (!streq_ptr(a->description, b->description))
1057 if (a->uid_set != b->uid_set)
1060 if (a->uid_set && a->uid != b->uid)
1063 if (a->gid_set != b->gid_set)
1066 if (a->gid_set && a->gid != b->gid)
1072 static bool valid_user_group_name(const char *u) {
1079 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1080 !(u[0] >= 'A' && u[0] <= 'Z') &&
1084 for (i = u+1; *i; i++) {
1085 if (!(*i >= 'a' && *i <= 'z') &&
1086 !(*i >= 'A' && *i <= 'Z') &&
1087 !(*i >= '0' && *i <= '9') &&
1093 sz = sysconf(_SC_LOGIN_NAME_MAX);
1096 if ((size_t) (i-u) > (size_t) sz)
1099 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1105 static bool valid_gecos(const char *d) {
1107 if (!utf8_is_valid(d))
1110 if (string_has_cc(d, NULL))
1113 /* Colons are used as field separators, and hence not OK */
1120 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1122 static const Specifier specifier_table[] = {
1123 { 'm', specifier_machine_id, NULL },
1124 { 'b', specifier_boot_id, NULL },
1125 { 'H', specifier_host_name, NULL },
1126 { 'v', specifier_kernel_release, NULL },
1130 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1131 _cleanup_(item_freep) Item *i = NULL;
1147 log_error("[%s:%u] Syntax error.", fname, line);
1151 if (strlen(action) != 1) {
1152 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1156 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1157 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1161 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1163 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1167 if (!valid_user_group_name(resolved_name)) {
1168 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1173 n += strspn(buffer+n, WHITESPACE);
1175 if (STR_IN_SET(buffer + n, "", "-"))
1179 switch (action[0]) {
1182 _cleanup_free_ char *resolved_id = NULL;
1185 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1189 /* Try to extend an existing member or group item */
1191 if (!id || streq(id, "-")) {
1192 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1196 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1198 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1202 if (!valid_user_group_name(resolved_id)) {
1203 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1208 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1212 l = hashmap_get(members, resolved_id);
1214 /* A list for this group name already exists, let's append to it */
1215 r = strv_push(&l, resolved_name);
1219 resolved_name = NULL;
1221 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1223 /* No list for this group name exists yet, create one */
1225 l = new0(char *, 2);
1229 l[0] = resolved_name;
1232 r = hashmap_put(members, resolved_id, l);
1238 resolved_id = resolved_name = NULL;
1245 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1253 if (id && !streq(id, "-")) {
1255 if (path_is_absolute(id)) {
1256 i->uid_path = strdup(id);
1260 path_kill_slashes(i->uid_path);
1263 r = parse_uid(id, &i->uid);
1265 log_error("Failed to parse UID: %s", id);
1274 i->description = unquote(buffer+n, "\"");
1275 if (!i->description)
1278 if (!valid_gecos(i->description)) {
1279 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1288 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1293 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1301 if (id && !streq(id, "-")) {
1303 if (path_is_absolute(id)) {
1304 i->gid_path = strdup(id);
1308 path_kill_slashes(i->gid_path);
1310 r = parse_gid(id, &i->gid);
1312 log_error("Failed to parse GID: %s", id);
1327 i->type = action[0];
1328 i->name = resolved_name;
1329 resolved_name = NULL;
1331 existing = hashmap_get(h, i->name);
1334 /* Two identical items are fine */
1335 if (!item_equal(existing, i))
1336 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1341 r = hashmap_put(h, i->name, i);
1349 static int read_config_file(const char *fn, bool ignore_enoent) {
1350 _cleanup_fclose_ FILE *f = NULL;
1351 char line[LINE_MAX];
1357 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1359 if (ignore_enoent && r == -ENOENT)
1362 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1366 FOREACH_LINE(line, f, break) {
1373 if (*l == '#' || *l == 0)
1376 k = parse_line(fn, v, l);
1377 if (k < 0 && r == 0)
1382 log_error("Failed to read from file %s: %m", fn);
1390 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1394 name = hashmap_first(by_id);
1398 hashmap_remove(by_name, name);
1400 hashmap_steal_first_key(by_id);
1404 while ((name = hashmap_steal_first_key(by_name)))
1407 hashmap_free(by_name);
1408 hashmap_free(by_id);
1411 static int help(void) {
1413 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1414 "Creates system user accounts.\n\n"
1415 " -h --help Show this help\n"
1416 " --version Show package version\n"
1417 " --root=PATH Operate on an alternate filesystem root\n",
1418 program_invocation_short_name);
1423 static int parse_argv(int argc, char *argv[]) {
1426 ARG_VERSION = 0x100,
1430 static const struct option options[] = {
1431 { "help", no_argument, NULL, 'h' },
1432 { "version", no_argument, NULL, ARG_VERSION },
1433 { "root", required_argument, NULL, ARG_ROOT },
1442 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1450 puts(PACKAGE_STRING);
1451 puts(SYSTEMD_FEATURES);
1456 arg_root = path_make_absolute_cwd(optarg);
1460 path_kill_slashes(arg_root);
1467 assert_not_reached("Unhandled option");
1474 int main(int argc, char *argv[]) {
1476 _cleanup_close_ int lock = -1;
1482 r = parse_argv(argc, argv);
1486 log_set_target(LOG_TARGET_AUTO);
1487 log_parse_environment();
1494 if (optind < argc) {
1497 for (j = optind; j < argc; j++) {
1498 k = read_config_file(argv[j], false);
1499 if (k < 0 && r == 0)
1503 _cleanup_strv_free_ char **files = NULL;
1506 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1508 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1512 STRV_FOREACH(f, files) {
1513 k = read_config_file(*f, true);
1514 if (k < 0 && r == 0)
1523 lock = take_password_lock(arg_root);
1525 log_error("Failed to take lock: %s", strerror(-lock));
1529 r = load_user_database();
1531 log_error("Failed to load user database: %s", strerror(-r));
1535 r = load_group_database();
1537 log_error("Failed to read group database: %s", strerror(-r));
1541 HASHMAP_FOREACH(i, groups, iterator)
1544 HASHMAP_FOREACH(i, users, iterator)
1549 log_error("Failed to write files: %s", strerror(-r));
1552 while ((i = hashmap_steal_first(groups)))
1555 while ((i = hashmap_steal_first(users)))
1558 while ((n = hashmap_first_key(members))) {
1559 strv_free(hashmap_steal_first(members));
1563 hashmap_free(groups);
1564 hashmap_free(users);
1565 hashmap_free(members);
1566 hashmap_free(todo_uids);
1567 hashmap_free(todo_gids);
1569 free_database(database_user, database_uid);
1570 free_database(database_group, database_gid);
1574 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;