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 #include "fileio-label.h"
41 typedef enum ItemType {
64 static char *arg_root = NULL;
66 static const char conf_file_dirs[] =
69 "/usr/local/lib/sysusers.d\0"
70 "/usr/lib/sysusers.d\0"
76 static Hashmap *users = NULL, *groups = NULL;
77 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
78 static Hashmap *members = NULL;
80 static Hashmap *database_uid = NULL, *database_user = NULL;
81 static Hashmap *database_gid = NULL, *database_group = NULL;
83 static uid_t search_uid = SYSTEM_UID_MAX;
84 static gid_t search_gid = SYSTEM_GID_MAX;
86 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
87 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
89 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
90 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
92 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
94 static int load_user_database(void) {
95 _cleanup_fclose_ FILE *f = NULL;
96 const char *passwd_path;
100 passwd_path = fix_root("/etc/passwd");
101 f = fopen(passwd_path, "re");
103 return errno == ENOENT ? 0 : -errno;
105 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
109 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
114 while ((pw = fgetpwent(f))) {
118 n = strdup(pw->pw_name);
122 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
123 if (k < 0 && k != -EEXIST) {
128 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
129 if (q < 0 && q != -EEXIST) {
140 if (!IN_SET(errno, 0, ENOENT))
146 static int load_group_database(void) {
147 _cleanup_fclose_ FILE *f = NULL;
148 const char *group_path;
152 group_path = fix_root("/etc/group");
153 f = fopen(group_path, "re");
155 return errno == ENOENT ? 0 : -errno;
157 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
161 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
166 while ((gr = fgetgrent(f))) {
170 n = strdup(gr->gr_name);
174 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
175 if (k < 0 && k != -EEXIST) {
180 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
181 if (q < 0 && q != -EEXIST) {
192 if (!IN_SET(errno, 0, ENOENT))
198 static int make_backup(const char *x) {
199 _cleanup_close_ int src = -1, dst = -1;
201 struct timespec ts[2];
205 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
207 if (errno == ENOENT) /* No backup necessary... */
213 if (fstat(src, &st) < 0)
216 temp = strappenda(x, ".XXXXXX");
217 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
221 r = copy_bytes(src, dst, (off_t) -1);
225 /* Copy over the access mask */
226 if (fchmod(dst, st.st_mode & 07777) < 0) {
231 /* Don't fail on chmod(). If it stays owned by us, then it
232 * isn't too bad... */
233 fchown(dst, st.st_uid, st.st_gid);
239 backup = strappenda(x, "-");
240 if (rename(temp, backup) < 0)
250 static int putgrent_with_members(const struct group *gr, FILE *group) {
256 a = hashmap_get(members, gr->gr_name);
258 _cleanup_strv_free_ char **l = NULL;
262 l = strv_copy(gr->gr_mem);
267 if (strv_find(l, *i))
270 if (strv_extend(&l, *i) < 0)
286 if (putgrent(&t, group) != 0)
287 return errno ? -errno : -EIO;
294 if (putgrent(gr, group) != 0)
295 return errno ? -errno : -EIO;
300 static int write_files(void) {
302 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
303 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
304 const char *passwd_path = NULL, *group_path = NULL;
305 bool group_changed = false;
310 /* We don't patch /etc/shadow or /etc/gshadow here, since we
311 * only create user accounts without passwords anyway. */
313 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
314 _cleanup_fclose_ FILE *original = NULL;
316 group_path = fix_root("/etc/group");
317 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
321 if (fchmod(fileno(group), 0644) < 0) {
326 original = fopen(group_path, "re");
331 while ((gr = fgetgrent(original))) {
332 /* Safety checks against name and GID
333 * collisions. Normally, this should
334 * be unnecessary, but given that we
335 * look at the entries anyway here,
336 * let's make an extra verification
337 * step that we don't generate
338 * duplicate entries. */
340 i = hashmap_get(groups, gr->gr_name);
341 if (i && i->todo_group) {
346 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
351 r = putgrent_with_members(gr, group);
356 group_changed = true;
360 if (!IN_SET(errno, 0, ENOENT)) {
365 } else if (errno != ENOENT) {
370 HASHMAP_FOREACH(i, todo_gids, iterator) {
374 .gr_passwd = (char*) "x",
377 r = putgrent_with_members(&n, group);
381 group_changed = true;
384 r = fflush_and_check(group);
389 if (hashmap_size(todo_uids) > 0) {
390 _cleanup_fclose_ FILE *original = NULL;
392 passwd_path = fix_root("/etc/passwd");
393 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
397 if (fchmod(fileno(passwd), 0644) < 0) {
402 original = fopen(passwd_path, "re");
407 while ((pw = fgetpwent(original))) {
409 i = hashmap_get(users, pw->pw_name);
410 if (i && i->todo_user) {
415 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
421 if (putpwent(pw, passwd) < 0) {
422 r = errno ? -errno : -EIO;
428 if (!IN_SET(errno, 0, ENOENT)) {
433 } else if (errno != ENOENT) {
438 HASHMAP_FOREACH(i, todo_uids, iterator) {
443 .pw_gecos = i->description,
444 .pw_passwd = (char*) "x",
447 /* Initialize the home directory and the shell
448 * to nologin, with one exception: for root we
449 * patch in something special */
451 n.pw_shell = (char*) "/bin/sh";
452 n.pw_dir = (char*) "/root";
454 n.pw_shell = (char*) "/sbin/nologin";
455 n.pw_dir = (char*) "/";
459 if (putpwent(&n, passwd) != 0) {
460 r = errno ? -errno : -EIO;
465 r = fflush_and_check(passwd);
470 /* Make a backup of the old files */
471 if (group && group_changed) {
472 r = make_backup(group_path);
478 r = make_backup(passwd_path);
483 /* And make the new files count */
484 if (group && group_changed) {
485 if (rename(group_tmp, group_path) < 0) {
495 if (rename(passwd_tmp, passwd_path) < 0) {
515 static int uid_is_ok(uid_t uid, const char *name) {
521 /* Let's see if we already have assigned the UID a second time */
522 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
525 /* Try to avoid using uids that are already used by a group
526 * that doesn't have the same name as our new user. */
527 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
528 if (i && !streq(i->name, name))
531 /* Let's check the files directly */
532 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
535 n = hashmap_get(database_gid, GID_TO_PTR(uid));
536 if (n && !streq(n, name))
539 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
545 if (!IN_SET(errno, 0, ENOENT))
549 g = getgrgid((gid_t) uid);
551 if (!streq(g->gr_name, name))
553 } else if (!IN_SET(errno, 0, ENOENT))
560 static int root_stat(const char *p, struct stat *st) {
564 if (stat(fix, st) < 0)
570 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
572 bool found_uid = false, found_gid = false;
578 /* First, try to get the gid directly */
579 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
584 /* Then, try to get the uid directly */
585 if ((_uid || (_gid && !found_gid))
587 && root_stat(i->uid_path, &st) >= 0) {
592 /* If we need the gid, but had no success yet, also derive it from the uid path */
593 if (_gid && !found_gid) {
599 /* If that didn't work yet, then let's reuse the gid as uid */
600 if (_uid && !found_uid && i->gid_path) {
605 } else if (root_stat(i->gid_path, &st) >= 0) {
606 uid = (uid_t) st.st_gid;
628 static int add_user(Item *i) {
634 /* Check the database directly */
635 z = hashmap_get(database_user, i->name);
637 log_debug("User %s already exists.", i->name);
638 i->uid = PTR_TO_UID(z);
649 p = getpwnam(i->name);
651 log_debug("User %s already exists.", i->name);
655 free(i->description);
656 i->description = strdup(p->pw_gecos);
659 if (!IN_SET(errno, 0, ENOENT)) {
660 log_error("Failed to check if user %s already exists: %m", i->name);
664 /* And shadow too, just to be sure */
666 sp = getspnam(i->name);
668 log_error("User %s already exists in shadow database, but not in user database.", i->name);
671 if (!IN_SET(errno, 0, ENOENT)) {
672 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
677 /* Try to use the suggested numeric uid */
679 r = uid_is_ok(i->uid, i->name);
681 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
685 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
690 /* If that didn't work, try to read it from the specified path */
694 if (read_id_from_file(i, &c, NULL) > 0) {
696 if (c <= 0 || c > SYSTEM_UID_MAX)
697 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
699 r = uid_is_ok(c, i->name);
701 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
707 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
712 /* Otherwise try to reuse the group ID */
713 if (!i->uid_set && i->gid_set) {
714 r = uid_is_ok((uid_t) i->gid, i->name);
716 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
720 i->uid = (uid_t) i->gid;
725 /* And if that didn't work either, let's try to find a free one */
727 for (; search_uid > 0; search_uid--) {
729 r = uid_is_ok(search_uid, i->name);
731 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
737 if (search_uid <= 0) {
738 log_error("No free user ID available for %s.", i->name);
748 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
752 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
757 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
762 static int gid_is_ok(gid_t gid) {
766 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
769 /* Avoid reusing gids that are already used by a different user */
770 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
773 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
776 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
784 if (!IN_SET(errno, 0, ENOENT))
788 p = getpwuid((uid_t) gid);
791 if (!IN_SET(errno, 0, ENOENT))
798 static int add_group(Item *i) {
804 /* Check the database directly */
805 z = hashmap_get(database_group, i->name);
807 log_debug("Group %s already exists.", i->name);
808 i->gid = PTR_TO_GID(z);
818 g = getgrnam(i->name);
820 log_debug("Group %s already exists.", i->name);
825 if (!IN_SET(errno, 0, ENOENT)) {
826 log_error("Failed to check if group %s already exists: %m", i->name);
831 /* Try to use the suggested numeric gid */
833 r = gid_is_ok(i->gid);
835 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
839 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
844 /* Try to reuse the numeric uid, if there's one */
845 if (!i->gid_set && i->uid_set) {
846 r = gid_is_ok((gid_t) i->uid);
848 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
852 i->gid = (gid_t) i->uid;
857 /* If that didn't work, try to read it from the specified path */
861 if (read_id_from_file(i, NULL, &c) > 0) {
863 if (c <= 0 || c > SYSTEM_GID_MAX)
864 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
868 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
874 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
879 /* And if that didn't work either, let's try to find a free one */
881 for (; search_gid > 0; search_gid--) {
882 r = gid_is_ok(search_gid);
884 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
890 if (search_gid <= 0) {
891 log_error("No free group ID available for %s.", i->name);
901 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
905 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
909 i->todo_group = true;
910 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
915 static int process_item(Item *i) {
932 j = hashmap_get(users, i->name);
934 /* There's already user to be created for this
935 * name, let's process that in one step */
944 j->gid_path = strdup(i->gid_path);
956 assert_not_reached("Unknown item type");
960 static void item_free(Item *i) {
968 free(i->description);
972 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
974 static int add_implicit(void) {
979 /* Implicitly create additional users and groups, if they were listed in "m" lines */
981 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
985 i = hashmap_get(groups, g);
987 _cleanup_(item_freep) Item *j = NULL;
989 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1002 r = hashmap_put(groups, j->name, j);
1006 log_debug("Adding implicit group '%s' due to m line", j->name);
1010 STRV_FOREACH(m, l) {
1012 i = hashmap_get(users, *m);
1014 _cleanup_(item_freep) Item *j = NULL;
1016 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1025 j->name = strdup(*m);
1029 r = hashmap_put(users, j->name, j);
1033 log_debug("Adding implicit user '%s' due to m line", j->name);
1042 static bool item_equal(Item *a, Item *b) {
1046 if (a->type != b->type)
1049 if (!streq_ptr(a->name, b->name))
1052 if (!streq_ptr(a->uid_path, b->uid_path))
1055 if (!streq_ptr(a->gid_path, b->gid_path))
1058 if (!streq_ptr(a->description, b->description))
1061 if (a->uid_set != b->uid_set)
1064 if (a->uid_set && a->uid != b->uid)
1067 if (a->gid_set != b->gid_set)
1070 if (a->gid_set && a->gid != b->gid)
1076 static bool valid_user_group_name(const char *u) {
1083 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1084 !(u[0] >= 'A' && u[0] <= 'Z') &&
1088 for (i = u+1; *i; i++) {
1089 if (!(*i >= 'a' && *i <= 'z') &&
1090 !(*i >= 'A' && *i <= 'Z') &&
1091 !(*i >= '0' && *i <= '9') &&
1097 sz = sysconf(_SC_LOGIN_NAME_MAX);
1100 if ((size_t) (i-u) > (size_t) sz)
1103 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1109 static bool valid_gecos(const char *d) {
1111 if (!utf8_is_valid(d))
1114 if (string_has_cc(d, NULL))
1117 /* Colons are used as field separators, and hence not OK */
1124 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1126 static const Specifier specifier_table[] = {
1127 { 'm', specifier_machine_id, NULL },
1128 { 'b', specifier_boot_id, NULL },
1129 { 'H', specifier_host_name, NULL },
1130 { 'v', specifier_kernel_release, NULL },
1134 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1135 _cleanup_(item_freep) Item *i = NULL;
1151 log_error("[%s:%u] Syntax error.", fname, line);
1155 if (strlen(action) != 1) {
1156 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1160 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1161 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1165 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1167 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1171 if (!valid_user_group_name(resolved_name)) {
1172 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1177 n += strspn(buffer+n, WHITESPACE);
1179 if (STR_IN_SET(buffer + n, "", "-"))
1183 switch (action[0]) {
1186 _cleanup_free_ char *resolved_id = NULL;
1189 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1193 /* Try to extend an existing member or group item */
1195 if (!id || streq(id, "-")) {
1196 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1200 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1202 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1206 if (!valid_user_group_name(resolved_id)) {
1207 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1212 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1216 l = hashmap_get(members, resolved_id);
1218 /* A list for this group name already exists, let's append to it */
1219 r = strv_push(&l, resolved_name);
1223 resolved_name = NULL;
1225 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1227 /* No list for this group name exists yet, create one */
1229 l = new0(char *, 2);
1233 l[0] = resolved_name;
1236 r = hashmap_put(members, resolved_id, l);
1242 resolved_id = resolved_name = NULL;
1249 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1257 if (id && !streq(id, "-")) {
1259 if (path_is_absolute(id)) {
1260 i->uid_path = strdup(id);
1264 path_kill_slashes(i->uid_path);
1267 r = parse_uid(id, &i->uid);
1269 log_error("Failed to parse UID: %s", id);
1278 i->description = unquote(buffer+n, "\"");
1279 if (!i->description)
1282 if (!valid_gecos(i->description)) {
1283 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1292 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1297 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1305 if (id && !streq(id, "-")) {
1307 if (path_is_absolute(id)) {
1308 i->gid_path = strdup(id);
1312 path_kill_slashes(i->gid_path);
1314 r = parse_gid(id, &i->gid);
1316 log_error("Failed to parse GID: %s", id);
1331 i->type = action[0];
1332 i->name = resolved_name;
1333 resolved_name = NULL;
1335 existing = hashmap_get(h, i->name);
1338 /* Two identical items are fine */
1339 if (!item_equal(existing, i))
1340 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1345 r = hashmap_put(h, i->name, i);
1353 static int read_config_file(const char *fn, bool ignore_enoent) {
1354 _cleanup_fclose_ FILE *f = NULL;
1355 char line[LINE_MAX];
1361 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1363 if (ignore_enoent && r == -ENOENT)
1366 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1370 FOREACH_LINE(line, f, break) {
1377 if (*l == '#' || *l == 0)
1380 k = parse_line(fn, v, l);
1381 if (k < 0 && r == 0)
1386 log_error("Failed to read from file %s: %m", fn);
1394 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1398 name = hashmap_first(by_id);
1402 hashmap_remove(by_name, name);
1404 hashmap_steal_first_key(by_id);
1408 while ((name = hashmap_steal_first_key(by_name)))
1411 hashmap_free(by_name);
1412 hashmap_free(by_id);
1415 static int help(void) {
1417 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1418 "Creates system user accounts.\n\n"
1419 " -h --help Show this help\n"
1420 " --version Show package version\n"
1421 " --root=PATH Operate on an alternate filesystem root\n",
1422 program_invocation_short_name);
1427 static int parse_argv(int argc, char *argv[]) {
1430 ARG_VERSION = 0x100,
1434 static const struct option options[] = {
1435 { "help", no_argument, NULL, 'h' },
1436 { "version", no_argument, NULL, ARG_VERSION },
1437 { "root", required_argument, NULL, ARG_ROOT },
1446 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1454 puts(PACKAGE_STRING);
1455 puts(SYSTEMD_FEATURES);
1460 arg_root = path_make_absolute_cwd(optarg);
1464 path_kill_slashes(arg_root);
1471 assert_not_reached("Unhandled option");
1478 int main(int argc, char *argv[]) {
1480 _cleanup_close_ int lock = -1;
1486 r = parse_argv(argc, argv);
1490 log_set_target(LOG_TARGET_AUTO);
1491 log_parse_environment();
1500 if (optind < argc) {
1503 for (j = optind; j < argc; j++) {
1504 k = read_config_file(argv[j], false);
1505 if (k < 0 && r == 0)
1509 _cleanup_strv_free_ char **files = NULL;
1512 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1514 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1518 STRV_FOREACH(f, files) {
1519 k = read_config_file(*f, true);
1520 if (k < 0 && r == 0)
1529 lock = take_password_lock(arg_root);
1531 log_error("Failed to take lock: %s", strerror(-lock));
1535 r = load_user_database();
1537 log_error("Failed to load user database: %s", strerror(-r));
1541 r = load_group_database();
1543 log_error("Failed to read group database: %s", strerror(-r));
1547 HASHMAP_FOREACH(i, groups, iterator)
1550 HASHMAP_FOREACH(i, users, iterator)
1555 log_error("Failed to write files: %s", strerror(-r));
1558 while ((i = hashmap_steal_first(groups)))
1561 while ((i = hashmap_steal_first(users)))
1564 while ((n = hashmap_first_key(members))) {
1565 strv_free(hashmap_steal_first(members));
1569 hashmap_free(groups);
1570 hashmap_free(users);
1571 hashmap_free(members);
1572 hashmap_free(todo_uids);
1573 hashmap_free(todo_gids);
1575 free_database(database_user, database_uid);
1576 free_database(database_group, database_gid);
1580 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;