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>
30 #include "specifier.h"
31 #include "path-util.h"
34 #include "conf-files.h"
38 typedef enum ItemType {
61 static char *arg_root = NULL;
63 static const char conf_file_dirs[] =
64 "/usr/local/lib/sysusers.d\0"
65 "/usr/lib/sysusers.d\0"
71 static Hashmap *users = NULL, *groups = NULL;
72 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
73 static Hashmap *members = NULL;
75 static Hashmap *database_uid = NULL, *database_user = NULL;
76 static Hashmap *database_gid = NULL, *database_group = NULL;
78 static uid_t search_uid = SYSTEM_UID_MAX;
79 static gid_t search_gid = SYSTEM_GID_MAX;
81 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
82 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
84 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
85 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
87 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
89 static int load_user_database(void) {
90 _cleanup_fclose_ FILE *f = NULL;
91 const char *passwd_path;
95 passwd_path = fix_root("/etc/passwd");
96 f = fopen(passwd_path, "re");
98 return errno == ENOENT ? 0 : -errno;
100 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
104 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
109 while ((pw = fgetpwent(f))) {
113 n = strdup(pw->pw_name);
117 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
118 if (k < 0 && k != -EEXIST) {
123 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
124 if (q < 0 && q != -EEXIST) {
135 if (!IN_SET(errno, 0, ENOENT))
141 static int load_group_database(void) {
142 _cleanup_fclose_ FILE *f = NULL;
143 const char *group_path;
147 group_path = fix_root("/etc/group");
148 f = fopen(group_path, "re");
150 return errno == ENOENT ? 0 : -errno;
152 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
156 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
161 while ((gr = fgetgrent(f))) {
165 n = strdup(gr->gr_name);
169 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
170 if (k < 0 && k != -EEXIST) {
175 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
176 if (q < 0 && q != -EEXIST) {
187 if (!IN_SET(errno, 0, ENOENT))
193 static int make_backup(const char *x) {
194 _cleanup_close_ int src = -1, dst = -1;
196 struct timespec ts[2];
200 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
202 if (errno == ENOENT) /* No backup necessary... */
208 if (fstat(src, &st) < 0)
211 temp = strappenda(x, ".XXXXXX");
212 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
216 r = copy_bytes(src, dst, (off_t) -1);
220 /* Copy over the access mask */
221 if (fchmod(dst, st.st_mode & 07777) < 0) {
226 /* Don't fail on chmod(). If it stays owned by us, then it
227 * isn't too bad... */
228 fchown(dst, st.st_uid, st.st_gid);
234 backup = strappenda(x, "-");
235 if (rename(temp, backup) < 0)
245 static int putgrent_with_members(const struct group *gr, FILE *group) {
251 a = hashmap_get(members, gr->gr_name);
253 _cleanup_strv_free_ char **l = NULL;
257 l = strv_copy(gr->gr_mem);
262 if (strv_find(l, *i))
265 if (strv_extend(&l, *i) < 0)
281 if (putgrent(&t, group) != 0)
282 return errno ? -errno : -EIO;
289 if (putgrent(gr, group) != 0)
290 return errno ? -errno : -EIO;
295 static int write_files(void) {
297 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
298 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
299 const char *passwd_path = NULL, *group_path = NULL;
300 bool group_changed = false;
305 /* We don't patch /etc/shadow or /etc/gshadow here, since we
306 * only create user accounts without passwords anyway. */
308 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
309 _cleanup_fclose_ FILE *original = NULL;
311 group_path = fix_root("/etc/group");
312 r = fopen_temporary(group_path, &group, &group_tmp);
316 if (fchmod(fileno(group), 0644) < 0) {
321 original = fopen(group_path, "re");
326 while ((gr = fgetgrent(original))) {
327 /* Safety checks against name and GID
328 * collisions. Normally, this should
329 * be unnecessary, but given that we
330 * look at the entries anyway here,
331 * let's make an extra verification
332 * step that we don't generate
333 * duplicate entries. */
335 i = hashmap_get(groups, gr->gr_name);
336 if (i && i->todo_group) {
341 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
346 r = putgrent_with_members(gr, group);
351 group_changed = true;
355 if (!IN_SET(errno, 0, ENOENT)) {
360 } else if (errno != ENOENT) {
365 HASHMAP_FOREACH(i, todo_gids, iterator) {
369 .gr_passwd = (char*) "x",
372 r = putgrent_with_members(&n, group);
376 group_changed = true;
379 r = fflush_and_check(group);
384 if (hashmap_size(todo_uids) > 0) {
385 _cleanup_fclose_ FILE *original = NULL;
387 passwd_path = fix_root("/etc/passwd");
388 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
392 if (fchmod(fileno(passwd), 0644) < 0) {
397 original = fopen(passwd_path, "re");
402 while ((pw = fgetpwent(original))) {
404 i = hashmap_get(users, pw->pw_name);
405 if (i && i->todo_user) {
410 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
416 if (putpwent(pw, passwd) < 0) {
417 r = errno ? -errno : -EIO;
423 if (!IN_SET(errno, 0, ENOENT)) {
428 } else if (errno != ENOENT) {
433 HASHMAP_FOREACH(i, todo_uids, iterator) {
438 .pw_gecos = i->description,
439 .pw_passwd = (char*) "x",
442 /* Initialize the home directory and the shell
443 * to nologin, with one exception: for root we
444 * patch in something special */
446 n.pw_shell = (char*) "/bin/sh";
447 n.pw_dir = (char*) "/root";
449 n.pw_shell = (char*) "/sbin/nologin";
450 n.pw_dir = (char*) "/";
454 if (putpwent(&n, passwd) != 0) {
455 r = errno ? -errno : -EIO;
460 r = fflush_and_check(passwd);
465 /* Make a backup of the old files */
466 if (group && group_changed) {
467 r = make_backup(group_path);
473 r = make_backup(passwd_path);
478 /* And make the new files count */
479 if (group && group_changed) {
480 if (rename(group_tmp, group_path) < 0) {
490 if (rename(passwd_tmp, passwd_path) < 0) {
510 static int uid_is_ok(uid_t uid, const char *name) {
516 /* Let's see if we already have assigned the UID a second time */
517 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
520 /* Try to avoid using uids that are already used by a group
521 * that doesn't have the same name as our new user. */
522 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
523 if (i && !streq(i->name, name))
526 /* Let's check the files directly */
527 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
530 n = hashmap_get(database_gid, GID_TO_PTR(uid));
531 if (n && !streq(n, name))
534 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
540 if (!IN_SET(errno, 0, ENOENT))
544 g = getgrgid((gid_t) uid);
546 if (!streq(g->gr_name, name))
548 } else if (!IN_SET(errno, 0, ENOENT))
555 static int root_stat(const char *p, struct stat *st) {
559 if (stat(fix, st) < 0)
565 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
567 bool found_uid = false, found_gid = false;
573 /* First, try to get the gid directly */
574 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
579 /* Then, try to get the uid directly */
580 if ((_uid || (_gid && !found_gid))
582 && root_stat(i->uid_path, &st) >= 0) {
587 /* If we need the gid, but had no success yet, also derive it from the uid path */
588 if (_gid && !found_gid) {
594 /* If that didn't work yet, then let's reuse the gid as uid */
595 if (_uid && !found_uid && i->gid_path) {
600 } else if (root_stat(i->gid_path, &st) >= 0) {
601 uid = (uid_t) st.st_gid;
623 static int add_user(Item *i) {
629 /* Check the database directly */
630 z = hashmap_get(database_user, i->name);
632 log_debug("User %s already exists.", i->name);
633 i->uid = PTR_TO_UID(z);
644 p = getpwnam(i->name);
646 log_debug("User %s already exists.", i->name);
650 free(i->description);
651 i->description = strdup(p->pw_gecos);
654 if (!IN_SET(errno, 0, ENOENT)) {
655 log_error("Failed to check if user %s already exists: %m", i->name);
659 /* And shadow too, just to be sure */
661 sp = getspnam(i->name);
663 log_error("User %s already exists in shadow database, but not in user database.", i->name);
666 if (!IN_SET(errno, 0, ENOENT)) {
667 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
672 /* Try to use the suggested numeric uid */
674 r = uid_is_ok(i->uid, i->name);
676 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
680 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
685 /* If that didn't work, try to read it from the specified path */
689 if (read_id_from_file(i, &c, NULL) > 0) {
691 if (c <= 0 || c > SYSTEM_UID_MAX)
692 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
694 r = uid_is_ok(c, i->name);
696 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
702 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
707 /* Otherwise try to reuse the group ID */
708 if (!i->uid_set && i->gid_set) {
709 r = uid_is_ok((uid_t) i->gid, i->name);
711 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
715 i->uid = (uid_t) i->gid;
720 /* And if that didn't work either, let's try to find a free one */
722 for (; search_uid > 0; search_uid--) {
724 r = uid_is_ok(search_uid, i->name);
726 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
732 if (search_uid <= 0) {
733 log_error("No free user ID available for %s.", i->name);
743 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
747 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
752 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
757 static int gid_is_ok(gid_t gid) {
761 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
764 /* Avoid reusing gids that are already used by a different user */
765 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
768 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
771 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
779 if (!IN_SET(errno, 0, ENOENT))
783 p = getpwuid((uid_t) gid);
786 if (!IN_SET(errno, 0, ENOENT))
793 static int add_group(Item *i) {
799 /* Check the database directly */
800 z = hashmap_get(database_group, i->name);
802 log_debug("Group %s already exists.", i->name);
803 i->gid = PTR_TO_GID(z);
813 g = getgrnam(i->name);
815 log_debug("Group %s already exists.", i->name);
820 if (!IN_SET(errno, 0, ENOENT)) {
821 log_error("Failed to check if group %s already exists: %m", i->name);
826 /* Try to use the suggested numeric gid */
828 r = gid_is_ok(i->gid);
830 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
834 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
839 /* Try to reuse the numeric uid, if there's one */
840 if (!i->gid_set && i->uid_set) {
841 r = gid_is_ok((gid_t) i->uid);
843 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
847 i->gid = (gid_t) i->uid;
852 /* If that didn't work, try to read it from the specified path */
856 if (read_id_from_file(i, NULL, &c) > 0) {
858 if (c <= 0 || c > SYSTEM_GID_MAX)
859 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
863 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
869 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
874 /* And if that didn't work either, let's try to find a free one */
876 for (; search_gid > 0; search_gid--) {
877 r = gid_is_ok(search_gid);
879 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
885 if (search_gid <= 0) {
886 log_error("No free group ID available for %s.", i->name);
896 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
900 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
904 i->todo_group = true;
905 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
910 static int process_item(Item *i) {
927 j = hashmap_get(users, i->name);
929 /* There's already user to be created for this
930 * name, let's process that in one step */
939 j->gid_path = strdup(i->gid_path);
951 assert_not_reached("Unknown item type");
955 static void item_free(Item *i) {
963 free(i->description);
967 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
969 static int add_implicit(void) {
974 /* Implicitly create additional users and groups, if they were listed in "m" lines */
976 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
980 i = hashmap_get(groups, g);
982 _cleanup_(item_freep) Item *j = NULL;
984 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
997 r = hashmap_put(groups, j->name, j);
1001 log_debug("Adding implicit group '%s' due to m line", j->name);
1005 STRV_FOREACH(m, l) {
1007 i = hashmap_get(users, *m);
1009 _cleanup_(item_freep) Item *j = NULL;
1011 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1020 j->name = strdup(*m);
1024 r = hashmap_put(users, j->name, j);
1028 log_debug("Adding implicit user '%s' due to m line", j->name);
1037 static bool item_equal(Item *a, Item *b) {
1041 if (a->type != b->type)
1044 if (!streq_ptr(a->name, b->name))
1047 if (!streq_ptr(a->uid_path, b->uid_path))
1050 if (!streq_ptr(a->gid_path, b->gid_path))
1053 if (!streq_ptr(a->description, b->description))
1056 if (a->uid_set != b->uid_set)
1059 if (a->uid_set && a->uid != b->uid)
1062 if (a->gid_set != b->gid_set)
1065 if (a->gid_set && a->gid != b->gid)
1071 static bool valid_user_group_name(const char *u) {
1078 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1079 !(u[0] >= 'A' && u[0] <= 'Z') &&
1083 for (i = u+1; *i; i++) {
1084 if (!(*i >= 'a' && *i <= 'z') &&
1085 !(*i >= 'A' && *i <= 'Z') &&
1086 !(*i >= '0' && *i <= '9') &&
1092 sz = sysconf(_SC_LOGIN_NAME_MAX);
1095 if ((size_t) (i-u) > (size_t) sz)
1101 static bool valid_gecos(const char *d) {
1103 if (!utf8_is_valid(d))
1106 if (strpbrk(d, ":\n"))
1112 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1114 static const Specifier specifier_table[] = {
1115 { 'm', specifier_machine_id, NULL },
1116 { 'b', specifier_boot_id, NULL },
1117 { 'H', specifier_host_name, NULL },
1118 { 'v', specifier_kernel_release, NULL },
1122 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1123 _cleanup_(item_freep) Item *i = NULL;
1139 log_error("[%s:%u] Syntax error.", fname, line);
1143 if (strlen(action) != 1) {
1144 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1148 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1149 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1153 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1155 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1159 if (!valid_user_group_name(resolved_name)) {
1160 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1165 n += strspn(buffer+n, WHITESPACE);
1167 if (STR_IN_SET(buffer + n, "", "-"))
1171 switch (action[0]) {
1174 _cleanup_free_ char *resolved_id = NULL;
1177 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1181 /* Try to extend an existing member or group item */
1183 if (!id || streq(id, "-")) {
1184 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1188 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1190 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1194 if (!valid_user_group_name(resolved_id)) {
1195 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1200 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1204 l = hashmap_get(members, resolved_id);
1206 /* A list for this group name already exists, let's append to it */
1207 r = strv_push(&l, resolved_name);
1211 resolved_name = NULL;
1213 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1215 /* No list for this group name exists yet, create one */
1217 l = new0(char *, 2);
1221 l[0] = resolved_name;
1224 r = hashmap_put(members, resolved_id, l);
1230 resolved_id = resolved_name = NULL;
1237 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1245 if (id && !streq(id, "-")) {
1247 if (path_is_absolute(id)) {
1248 i->uid_path = strdup(id);
1252 path_kill_slashes(i->uid_path);
1255 r = parse_uid(id, &i->uid);
1257 log_error("Failed to parse UID: %s", id);
1266 i->description = unquote(buffer+n, "\"");
1267 if (!i->description)
1270 if (!valid_gecos(i->description)) {
1271 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1280 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1285 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1293 if (id && !streq(id, "-")) {
1295 if (path_is_absolute(id)) {
1296 i->gid_path = strdup(id);
1300 path_kill_slashes(i->gid_path);
1302 r = parse_gid(id, &i->gid);
1304 log_error("Failed to parse GID: %s", id);
1319 i->type = action[0];
1320 i->name = resolved_name;
1321 resolved_name = NULL;
1323 existing = hashmap_get(h, i->name);
1326 /* Two identical items are fine */
1327 if (!item_equal(existing, i))
1328 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1333 r = hashmap_put(h, i->name, i);
1341 static int read_config_file(const char *fn, bool ignore_enoent) {
1342 _cleanup_fclose_ FILE *f = NULL;
1343 char line[LINE_MAX];
1349 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1351 if (ignore_enoent && r == -ENOENT)
1354 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1358 FOREACH_LINE(line, f, break) {
1365 if (*l == '#' || *l == 0)
1368 k = parse_line(fn, v, l);
1369 if (k < 0 && r == 0)
1374 log_error("Failed to read from file %s: %m", fn);
1382 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1386 name = hashmap_first(by_id);
1390 hashmap_remove(by_name, name);
1392 hashmap_steal_first_key(by_id);
1396 while ((name = hashmap_steal_first_key(by_name)))
1399 hashmap_free(by_name);
1400 hashmap_free(by_id);
1403 static int help(void) {
1405 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1406 "Creates system user accounts.\n\n"
1407 " -h --help Show this help\n"
1408 " --version Show package version\n"
1409 " --root=PATH Operate on an alternate filesystem root\n",
1410 program_invocation_short_name);
1415 static int parse_argv(int argc, char *argv[]) {
1418 ARG_VERSION = 0x100,
1422 static const struct option options[] = {
1423 { "help", no_argument, NULL, 'h' },
1424 { "version", no_argument, NULL, ARG_VERSION },
1425 { "root", required_argument, NULL, ARG_ROOT },
1434 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1442 puts(PACKAGE_STRING);
1443 puts(SYSTEMD_FEATURES);
1448 arg_root = path_make_absolute_cwd(optarg);
1452 path_kill_slashes(arg_root);
1459 assert_not_reached("Unhandled option");
1466 int main(int argc, char *argv[]) {
1468 _cleanup_close_ int lock = -1;
1474 r = parse_argv(argc, argv);
1478 log_set_target(LOG_TARGET_AUTO);
1479 log_parse_environment();
1486 if (optind < argc) {
1489 for (j = optind; j < argc; j++) {
1490 k = read_config_file(argv[j], false);
1491 if (k < 0 && r == 0)
1495 _cleanup_strv_free_ char **files = NULL;
1498 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1500 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1504 STRV_FOREACH(f, files) {
1505 k = read_config_file(*f, true);
1506 if (k < 0 && r == 0)
1515 lock = take_password_lock(arg_root);
1517 log_error("Failed to take lock: %s", strerror(-lock));
1521 r = load_user_database();
1523 log_error("Failed to load user database: %s", strerror(-r));
1527 r = load_group_database();
1529 log_error("Failed to read group database: %s", strerror(-r));
1533 HASHMAP_FOREACH(i, groups, iterator)
1536 HASHMAP_FOREACH(i, users, iterator)
1541 log_error("Failed to write files: %s", strerror(-r));
1544 while ((i = hashmap_steal_first(groups)))
1547 while ((i = hashmap_steal_first(users)))
1550 while ((n = hashmap_first_key(members))) {
1551 strv_free(hashmap_steal_first(members));
1555 hashmap_free(groups);
1556 hashmap_free(users);
1557 hashmap_free(members);
1558 hashmap_free(todo_uids);
1559 hashmap_free(todo_gids);
1561 free_database(database_user, database_uid);
1562 free_database(database_group, database_gid);
1566 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;