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 (strpbrk(d, ":\n"))
1116 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1118 static const Specifier specifier_table[] = {
1119 { 'm', specifier_machine_id, NULL },
1120 { 'b', specifier_boot_id, NULL },
1121 { 'H', specifier_host_name, NULL },
1122 { 'v', specifier_kernel_release, NULL },
1126 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1127 _cleanup_(item_freep) Item *i = NULL;
1143 log_error("[%s:%u] Syntax error.", fname, line);
1147 if (strlen(action) != 1) {
1148 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1152 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1153 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1157 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1159 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1163 if (!valid_user_group_name(resolved_name)) {
1164 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1169 n += strspn(buffer+n, WHITESPACE);
1171 if (STR_IN_SET(buffer + n, "", "-"))
1175 switch (action[0]) {
1178 _cleanup_free_ char *resolved_id = NULL;
1181 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1185 /* Try to extend an existing member or group item */
1187 if (!id || streq(id, "-")) {
1188 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1192 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1194 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1198 if (!valid_user_group_name(resolved_id)) {
1199 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1204 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1208 l = hashmap_get(members, resolved_id);
1210 /* A list for this group name already exists, let's append to it */
1211 r = strv_push(&l, resolved_name);
1215 resolved_name = NULL;
1217 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1219 /* No list for this group name exists yet, create one */
1221 l = new0(char *, 2);
1225 l[0] = resolved_name;
1228 r = hashmap_put(members, resolved_id, l);
1234 resolved_id = resolved_name = NULL;
1241 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1249 if (id && !streq(id, "-")) {
1251 if (path_is_absolute(id)) {
1252 i->uid_path = strdup(id);
1256 path_kill_slashes(i->uid_path);
1259 r = parse_uid(id, &i->uid);
1261 log_error("Failed to parse UID: %s", id);
1270 i->description = unquote(buffer+n, "\"");
1271 if (!i->description)
1274 if (!valid_gecos(i->description)) {
1275 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1284 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1289 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1297 if (id && !streq(id, "-")) {
1299 if (path_is_absolute(id)) {
1300 i->gid_path = strdup(id);
1304 path_kill_slashes(i->gid_path);
1306 r = parse_gid(id, &i->gid);
1308 log_error("Failed to parse GID: %s", id);
1323 i->type = action[0];
1324 i->name = resolved_name;
1325 resolved_name = NULL;
1327 existing = hashmap_get(h, i->name);
1330 /* Two identical items are fine */
1331 if (!item_equal(existing, i))
1332 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1337 r = hashmap_put(h, i->name, i);
1345 static int read_config_file(const char *fn, bool ignore_enoent) {
1346 _cleanup_fclose_ FILE *f = NULL;
1347 char line[LINE_MAX];
1353 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1355 if (ignore_enoent && r == -ENOENT)
1358 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1362 FOREACH_LINE(line, f, break) {
1369 if (*l == '#' || *l == 0)
1372 k = parse_line(fn, v, l);
1373 if (k < 0 && r == 0)
1378 log_error("Failed to read from file %s: %m", fn);
1386 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1390 name = hashmap_first(by_id);
1394 hashmap_remove(by_name, name);
1396 hashmap_steal_first_key(by_id);
1400 while ((name = hashmap_steal_first_key(by_name)))
1403 hashmap_free(by_name);
1404 hashmap_free(by_id);
1407 static int help(void) {
1409 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1410 "Creates system user accounts.\n\n"
1411 " -h --help Show this help\n"
1412 " --version Show package version\n"
1413 " --root=PATH Operate on an alternate filesystem root\n",
1414 program_invocation_short_name);
1419 static int parse_argv(int argc, char *argv[]) {
1422 ARG_VERSION = 0x100,
1426 static const struct option options[] = {
1427 { "help", no_argument, NULL, 'h' },
1428 { "version", no_argument, NULL, ARG_VERSION },
1429 { "root", required_argument, NULL, ARG_ROOT },
1438 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1446 puts(PACKAGE_STRING);
1447 puts(SYSTEMD_FEATURES);
1452 arg_root = path_make_absolute_cwd(optarg);
1456 path_kill_slashes(arg_root);
1463 assert_not_reached("Unhandled option");
1470 int main(int argc, char *argv[]) {
1472 _cleanup_close_ int lock = -1;
1478 r = parse_argv(argc, argv);
1482 log_set_target(LOG_TARGET_AUTO);
1483 log_parse_environment();
1490 if (optind < argc) {
1493 for (j = optind; j < argc; j++) {
1494 k = read_config_file(argv[j], false);
1495 if (k < 0 && r == 0)
1499 _cleanup_strv_free_ char **files = NULL;
1502 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1504 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1508 STRV_FOREACH(f, files) {
1509 k = read_config_file(*f, true);
1510 if (k < 0 && r == 0)
1519 lock = take_password_lock(arg_root);
1521 log_error("Failed to take lock: %s", strerror(-lock));
1525 r = load_user_database();
1527 log_error("Failed to load user database: %s", strerror(-r));
1531 r = load_group_database();
1533 log_error("Failed to read group database: %s", strerror(-r));
1537 HASHMAP_FOREACH(i, groups, iterator)
1540 HASHMAP_FOREACH(i, users, iterator)
1545 log_error("Failed to write files: %s", strerror(-r));
1548 while ((i = hashmap_steal_first(groups)))
1551 while ((i = hashmap_steal_first(users)))
1554 while ((n = hashmap_first_key(members))) {
1555 strv_free(hashmap_steal_first(members));
1559 hashmap_free(groups);
1560 hashmap_free(users);
1561 hashmap_free(members);
1562 hashmap_free(todo_uids);
1563 hashmap_free(todo_gids);
1565 free_database(database_user, database_uid);
1566 free_database(database_group, database_gid);
1570 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;