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>
32 #include "specifier.h"
33 #include "path-util.h"
36 #include "conf-files.h"
40 #include "fileio-label.h"
42 typedef enum ItemType {
66 static char *arg_root = NULL;
68 static const char conf_file_dirs[] =
71 "/usr/local/lib/sysusers.d\0"
72 "/usr/lib/sysusers.d\0"
78 static Hashmap *users = NULL, *groups = NULL;
79 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
80 static Hashmap *members = NULL;
82 static Hashmap *database_uid = NULL, *database_user = NULL;
83 static Hashmap *database_gid = NULL, *database_group = NULL;
85 static uid_t search_uid = SYSTEM_UID_MAX;
86 static gid_t search_gid = SYSTEM_GID_MAX;
88 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
89 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
91 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
92 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
94 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
96 static int load_user_database(void) {
97 _cleanup_fclose_ FILE *f = NULL;
98 const char *passwd_path;
102 passwd_path = fix_root("/etc/passwd");
103 f = fopen(passwd_path, "re");
105 return errno == ENOENT ? 0 : -errno;
107 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
111 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
116 while ((pw = fgetpwent(f))) {
120 n = strdup(pw->pw_name);
124 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
125 if (k < 0 && k != -EEXIST) {
130 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
131 if (q < 0 && q != -EEXIST) {
142 if (!IN_SET(errno, 0, ENOENT))
148 static int load_group_database(void) {
149 _cleanup_fclose_ FILE *f = NULL;
150 const char *group_path;
154 group_path = fix_root("/etc/group");
155 f = fopen(group_path, "re");
157 return errno == ENOENT ? 0 : -errno;
159 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
163 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
168 while ((gr = fgetgrent(f))) {
172 n = strdup(gr->gr_name);
176 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
177 if (k < 0 && k != -EEXIST) {
182 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
183 if (q < 0 && q != -EEXIST) {
194 if (!IN_SET(errno, 0, ENOENT))
200 static int make_backup(const char *target, const char *x) {
201 _cleanup_close_ int src = -1;
202 _cleanup_fclose_ FILE *dst = NULL;
204 struct timespec ts[2];
208 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
210 if (errno == ENOENT) /* No backup necessary... */
216 if (fstat(src, &st) < 0)
219 r = fopen_temporary_label(target, x, &dst, &temp);
223 r = copy_bytes(src, fileno(dst), (off_t) -1);
227 /* Don't fail on chmod() or chown(). If it stays owned by us
228 * and/or unreadable by others, then it isn't too bad... */
230 backup = strappenda(x, "-");
232 /* Copy over the access mask */
233 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
234 log_warning("Failed to change mode on %s: %m", backup);
236 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
237 log_warning("Failed to change ownership of %s: %m", backup);
241 futimens(fileno(dst), ts);
243 if (rename(temp, backup) < 0)
253 static int putgrent_with_members(const struct group *gr, FILE *group) {
259 a = hashmap_get(members, gr->gr_name);
261 _cleanup_strv_free_ char **l = NULL;
265 l = strv_copy(gr->gr_mem);
270 if (strv_find(l, *i))
273 if (strv_extend(&l, *i) < 0)
289 if (putgrent(&t, group) != 0)
290 return errno ? -errno : -EIO;
297 if (putgrent(gr, group) != 0)
298 return errno ? -errno : -EIO;
303 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
309 a = hashmap_get(members, sg->sg_namp);
311 _cleanup_strv_free_ char **l = NULL;
315 l = strv_copy(sg->sg_mem);
320 if (strv_find(l, *i))
323 if (strv_extend(&l, *i) < 0)
339 if (putsgent(&t, gshadow) != 0)
340 return errno ? -errno : -EIO;
347 if (putsgent(sg, gshadow) != 0)
348 return errno ? -errno : -EIO;
353 static int write_files(void) {
355 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
356 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
357 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
358 bool group_changed = false;
363 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
364 _cleanup_fclose_ FILE *original = NULL;
366 /* First we update the actual group list file */
367 group_path = fix_root("/etc/group");
368 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
372 if (fchmod(fileno(group), 0644) < 0) {
377 original = fopen(group_path, "re");
382 while ((gr = fgetgrent(original))) {
383 /* Safety checks against name and GID
384 * collisions. Normally, this should
385 * be unnecessary, but given that we
386 * look at the entries anyway here,
387 * let's make an extra verification
388 * step that we don't generate
389 * duplicate entries. */
391 i = hashmap_get(groups, gr->gr_name);
392 if (i && i->todo_group) {
397 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
402 r = putgrent_with_members(gr, group);
406 group_changed = true;
410 if (!IN_SET(errno, 0, ENOENT)) {
415 } else if (errno != ENOENT) {
420 HASHMAP_FOREACH(i, todo_gids, iterator) {
424 .gr_passwd = (char*) "x",
427 r = putgrent_with_members(&n, group);
431 group_changed = true;
434 r = fflush_and_check(group);
443 /* OK, now also update the shadow file for the group list */
444 gshadow_path = fix_root("/etc/gshadow");
445 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
449 if (fchmod(fileno(gshadow), 0000) < 0) {
454 original = fopen(gshadow_path, "re");
459 while ((sg = fgetsgent(original))) {
461 i = hashmap_get(groups, sg->sg_namp);
462 if (i && i->todo_group) {
467 r = putsgent_with_members(sg, gshadow);
471 group_changed = true;
475 if (!IN_SET(errno, 0, ENOENT)) {
480 } else if (errno != ENOENT) {
485 HASHMAP_FOREACH(i, todo_gids, iterator) {
488 .sg_passwd = (char*) "!!",
491 r = putsgent_with_members(&n, gshadow);
495 group_changed = true;
498 r = fflush_and_check(gshadow);
503 if (hashmap_size(todo_uids) > 0) {
504 _cleanup_fclose_ FILE *original = NULL;
507 /* First we update the user database itself */
508 passwd_path = fix_root("/etc/passwd");
509 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
513 if (fchmod(fileno(passwd), 0644) < 0) {
518 original = fopen(passwd_path, "re");
523 while ((pw = fgetpwent(original))) {
525 i = hashmap_get(users, pw->pw_name);
526 if (i && i->todo_user) {
531 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
537 if (putpwent(pw, passwd) < 0) {
538 r = errno ? -errno : -EIO;
544 if (!IN_SET(errno, 0, ENOENT)) {
549 } else if (errno != ENOENT) {
554 HASHMAP_FOREACH(i, todo_uids, iterator) {
559 .pw_gecos = i->description,
561 /* "x" means the password is stored in
563 .pw_passwd = (char*) "x",
565 /* We default to the root directory as home */
566 .pw_dir = i->home ? i->home : (char*) "/",
568 /* Initialize the shell to nologin,
569 * with one exception: for root we
570 * patch in something special */
571 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
575 if (putpwent(&n, passwd) != 0) {
576 r = errno ? -errno : -EIO;
581 r = fflush_and_check(passwd);
590 /* The we update the shadow database */
591 shadow_path = fix_root("/etc/shadow");
592 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
596 if (fchmod(fileno(shadow), 0000) < 0) {
601 original = fopen(shadow_path, "re");
606 while ((sp = fgetspent(original))) {
608 i = hashmap_get(users, sp->sp_namp);
609 if (i && i->todo_user) {
615 if (putspent(sp, shadow) < 0) {
616 r = errno ? -errno : -EIO;
622 if (!IN_SET(errno, 0, ENOENT)) {
626 } else if (errno != ENOENT) {
631 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
632 HASHMAP_FOREACH(i, todo_uids, iterator) {
635 .sp_pwdp = (char*) "!!",
642 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
646 if (putspent(&n, shadow) != 0) {
647 r = errno ? -errno : -EIO;
652 r = fflush_and_check(shadow);
657 /* Make a backup of the old files */
660 r = make_backup("/etc/group", group_path);
665 r = make_backup("/etc/gshadow", gshadow_path);
672 r = make_backup("/etc/passwd", passwd_path);
677 r = make_backup("/etc/shadow", shadow_path);
682 /* And make the new files count */
685 if (rename(group_tmp, group_path) < 0) {
694 if (rename(gshadow_tmp, gshadow_path) < 0) {
705 if (rename(passwd_tmp, passwd_path) < 0) {
714 if (rename(shadow_tmp, shadow_path) < 0) {
738 static int uid_is_ok(uid_t uid, const char *name) {
744 /* Let's see if we already have assigned the UID a second time */
745 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
748 /* Try to avoid using uids that are already used by a group
749 * that doesn't have the same name as our new user. */
750 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
751 if (i && !streq(i->name, name))
754 /* Let's check the files directly */
755 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
758 n = hashmap_get(database_gid, GID_TO_PTR(uid));
759 if (n && !streq(n, name))
762 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
768 if (!IN_SET(errno, 0, ENOENT))
772 g = getgrgid((gid_t) uid);
774 if (!streq(g->gr_name, name))
776 } else if (!IN_SET(errno, 0, ENOENT))
783 static int root_stat(const char *p, struct stat *st) {
787 if (stat(fix, st) < 0)
793 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
795 bool found_uid = false, found_gid = false;
801 /* First, try to get the gid directly */
802 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
807 /* Then, try to get the uid directly */
808 if ((_uid || (_gid && !found_gid))
810 && root_stat(i->uid_path, &st) >= 0) {
815 /* If we need the gid, but had no success yet, also derive it from the uid path */
816 if (_gid && !found_gid) {
822 /* If that didn't work yet, then let's reuse the gid as uid */
823 if (_uid && !found_uid && i->gid_path) {
828 } else if (root_stat(i->gid_path, &st) >= 0) {
829 uid = (uid_t) st.st_gid;
851 static int add_user(Item *i) {
857 /* Check the database directly */
858 z = hashmap_get(database_user, i->name);
860 log_debug("User %s already exists.", i->name);
861 i->uid = PTR_TO_UID(z);
872 p = getpwnam(i->name);
874 log_debug("User %s already exists.", i->name);
878 free(i->description);
879 i->description = strdup(p->pw_gecos);
882 if (!IN_SET(errno, 0, ENOENT)) {
883 log_error("Failed to check if user %s already exists: %m", i->name);
887 /* And shadow too, just to be sure */
889 sp = getspnam(i->name);
891 log_error("User %s already exists in shadow database, but not in user database.", i->name);
894 if (!IN_SET(errno, 0, ENOENT)) {
895 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
900 /* Try to use the suggested numeric uid */
902 r = uid_is_ok(i->uid, i->name);
904 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
908 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
913 /* If that didn't work, try to read it from the specified path */
917 if (read_id_from_file(i, &c, NULL) > 0) {
919 if (c <= 0 || c > SYSTEM_UID_MAX)
920 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
922 r = uid_is_ok(c, i->name);
924 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
930 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
935 /* Otherwise try to reuse the group ID */
936 if (!i->uid_set && i->gid_set) {
937 r = uid_is_ok((uid_t) i->gid, i->name);
939 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
943 i->uid = (uid_t) i->gid;
948 /* And if that didn't work either, let's try to find a free one */
950 for (; search_uid > 0; search_uid--) {
952 r = uid_is_ok(search_uid, i->name);
954 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
960 if (search_uid <= 0) {
961 log_error("No free user ID available for %s.", i->name);
971 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
975 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
980 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
985 static int gid_is_ok(gid_t gid) {
989 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
992 /* Avoid reusing gids that are already used by a different user */
993 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
996 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
999 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1007 if (!IN_SET(errno, 0, ENOENT))
1011 p = getpwuid((uid_t) gid);
1014 if (!IN_SET(errno, 0, ENOENT))
1021 static int add_group(Item *i) {
1027 /* Check the database directly */
1028 z = hashmap_get(database_group, i->name);
1030 log_debug("Group %s already exists.", i->name);
1031 i->gid = PTR_TO_GID(z);
1036 /* Also check NSS */
1041 g = getgrnam(i->name);
1043 log_debug("Group %s already exists.", i->name);
1048 if (!IN_SET(errno, 0, ENOENT)) {
1049 log_error("Failed to check if group %s already exists: %m", i->name);
1054 /* Try to use the suggested numeric gid */
1056 r = gid_is_ok(i->gid);
1058 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1062 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1067 /* Try to reuse the numeric uid, if there's one */
1068 if (!i->gid_set && i->uid_set) {
1069 r = gid_is_ok((gid_t) i->uid);
1071 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1075 i->gid = (gid_t) i->uid;
1080 /* If that didn't work, try to read it from the specified path */
1084 if (read_id_from_file(i, NULL, &c) > 0) {
1086 if (c <= 0 || c > SYSTEM_GID_MAX)
1087 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1091 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1097 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1102 /* And if that didn't work either, let's try to find a free one */
1104 for (; search_gid > 0; search_gid--) {
1105 r = gid_is_ok(search_gid);
1107 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1113 if (search_gid <= 0) {
1114 log_error("No free group ID available for %s.", i->name);
1119 i->gid = search_gid;
1124 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
1128 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1132 i->todo_group = true;
1133 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1138 static int process_item(Item *i) {
1155 j = hashmap_get(users, i->name);
1157 /* There's already user to be created for this
1158 * name, let's process that in one step */
1167 j->gid_path = strdup(i->gid_path);
1175 return add_group(i);
1179 assert_not_reached("Unknown item type");
1183 static void item_free(Item *i) {
1191 free(i->description);
1195 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1197 static int add_implicit(void) {
1202 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1204 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1208 i = hashmap_get(groups, g);
1210 _cleanup_(item_freep) Item *j = NULL;
1212 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1220 j->type = ADD_GROUP;
1221 j->name = strdup(g);
1225 r = hashmap_put(groups, j->name, j);
1229 log_debug("Adding implicit group '%s' due to m line", j->name);
1233 STRV_FOREACH(m, l) {
1235 i = hashmap_get(users, *m);
1237 _cleanup_(item_freep) Item *j = NULL;
1239 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1248 j->name = strdup(*m);
1252 r = hashmap_put(users, j->name, j);
1256 log_debug("Adding implicit user '%s' due to m line", j->name);
1265 static bool item_equal(Item *a, Item *b) {
1269 if (a->type != b->type)
1272 if (!streq_ptr(a->name, b->name))
1275 if (!streq_ptr(a->uid_path, b->uid_path))
1278 if (!streq_ptr(a->gid_path, b->gid_path))
1281 if (!streq_ptr(a->description, b->description))
1284 if (a->uid_set != b->uid_set)
1287 if (a->uid_set && a->uid != b->uid)
1290 if (a->gid_set != b->gid_set)
1293 if (a->gid_set && a->gid != b->gid)
1296 if (!streq_ptr(a->home, b->home))
1302 static bool valid_user_group_name(const char *u) {
1309 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1310 !(u[0] >= 'A' && u[0] <= 'Z') &&
1314 for (i = u+1; *i; i++) {
1315 if (!(*i >= 'a' && *i <= 'z') &&
1316 !(*i >= 'A' && *i <= 'Z') &&
1317 !(*i >= '0' && *i <= '9') &&
1323 sz = sysconf(_SC_LOGIN_NAME_MAX);
1326 if ((size_t) (i-u) > (size_t) sz)
1329 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1335 static bool valid_gecos(const char *d) {
1340 if (!utf8_is_valid(d))
1343 if (string_has_cc(d, NULL))
1346 /* Colons are used as field separators, and hence not OK */
1353 static bool valid_home(const char *p) {
1358 if (!utf8_is_valid(p))
1361 if (string_has_cc(p, NULL))
1364 if (!path_is_absolute(p))
1367 if (!path_is_safe(p))
1370 /* Colons are used as field separators, and hence not OK */
1377 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1379 static const Specifier specifier_table[] = {
1380 { 'm', specifier_machine_id, NULL },
1381 { 'b', specifier_boot_id, NULL },
1382 { 'H', specifier_host_name, NULL },
1383 { 'v', specifier_kernel_release, NULL },
1387 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *description = NULL, *home = NULL;
1388 _cleanup_(item_freep) Item *i = NULL;
1400 r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
1402 log_error("[%s:%u] Syntax error.", fname, line);
1406 log_error("[%s:%u] Missing action and name columns.", fname, line);
1410 log_error("[%s:%u] Trailing garbage.", fname, line);
1415 if (strlen(action) != 1) {
1416 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1420 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
1421 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1426 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1428 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1432 if (!valid_user_group_name(resolved_name)) {
1433 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1437 /* Simplify remaining columns */
1438 if (isempty(id) || streq(id, "-")) {
1443 if (isempty(description) || streq(description, "-")) {
1448 if (isempty(home) || streq(home, "-")) {
1453 switch (action[0]) {
1456 _cleanup_free_ char *resolved_id = NULL;
1459 /* Try to extend an existing member or group item */
1462 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1467 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1472 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1476 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1478 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1482 if (!valid_user_group_name(resolved_id)) {
1483 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1487 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1491 l = hashmap_get(members, resolved_id);
1493 /* A list for this group name already exists, let's append to it */
1494 r = strv_push(&l, resolved_name);
1498 resolved_name = NULL;
1500 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1502 /* No list for this group name exists yet, create one */
1504 l = new0(char *, 2);
1508 l[0] = resolved_name;
1511 r = hashmap_put(members, resolved_id, l);
1517 resolved_id = resolved_name = NULL;
1524 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1533 if (path_is_absolute(id)) {
1537 path_kill_slashes(i->uid_path);
1539 r = parse_uid(id, &i->uid);
1541 log_error("Failed to parse UID: %s", id);
1550 if (!valid_gecos(description)) {
1551 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1555 i->description = description;
1560 if (!valid_home(home)) {
1561 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1575 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1580 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1584 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1593 if (path_is_absolute(id)) {
1597 path_kill_slashes(i->gid_path);
1599 r = parse_gid(id, &i->gid);
1601 log_error("Failed to parse GID: %s", id);
1615 i->type = action[0];
1616 i->name = resolved_name;
1617 resolved_name = NULL;
1619 existing = hashmap_get(h, i->name);
1622 /* Two identical items are fine */
1623 if (!item_equal(existing, i))
1624 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1629 r = hashmap_put(h, i->name, i);
1637 static int read_config_file(const char *fn, bool ignore_enoent) {
1638 _cleanup_fclose_ FILE *rf = NULL;
1640 char line[LINE_MAX];
1649 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1651 if (ignore_enoent && r == -ENOENT)
1654 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1661 FOREACH_LINE(line, f, break) {
1668 if (*l == '#' || *l == 0)
1671 k = parse_line(fn, v, l);
1672 if (k < 0 && r == 0)
1677 log_error("Failed to read from file %s: %m", fn);
1685 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1689 name = hashmap_first(by_id);
1693 hashmap_remove(by_name, name);
1695 hashmap_steal_first_key(by_id);
1699 while ((name = hashmap_steal_first_key(by_name)))
1702 hashmap_free(by_name);
1703 hashmap_free(by_id);
1706 static void help(void) {
1707 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1708 "Creates system user accounts.\n\n"
1709 " -h --help Show this help\n"
1710 " --version Show package version\n"
1711 " --root=PATH Operate on an alternate filesystem root\n"
1712 , program_invocation_short_name);
1715 static int parse_argv(int argc, char *argv[]) {
1718 ARG_VERSION = 0x100,
1722 static const struct option options[] = {
1723 { "help", no_argument, NULL, 'h' },
1724 { "version", no_argument, NULL, ARG_VERSION },
1725 { "root", required_argument, NULL, ARG_ROOT },
1734 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1743 puts(PACKAGE_STRING);
1744 puts(SYSTEMD_FEATURES);
1749 arg_root = path_make_absolute_cwd(optarg);
1753 path_kill_slashes(arg_root);
1760 assert_not_reached("Unhandled option");
1766 int main(int argc, char *argv[]) {
1768 _cleanup_close_ int lock = -1;
1774 r = parse_argv(argc, argv);
1778 log_set_target(LOG_TARGET_AUTO);
1779 log_parse_environment();
1784 r = label_init(NULL);
1786 log_error("SELinux setup failed: %s", strerror(-r));
1790 if (optind < argc) {
1793 for (j = optind; j < argc; j++) {
1794 k = read_config_file(argv[j], false);
1795 if (k < 0 && r == 0)
1799 _cleanup_strv_free_ char **files = NULL;
1802 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1804 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1808 STRV_FOREACH(f, files) {
1809 k = read_config_file(*f, true);
1810 if (k < 0 && r == 0)
1819 lock = take_password_lock(arg_root);
1821 log_error("Failed to take lock: %s", strerror(-lock));
1825 r = load_user_database();
1827 log_error("Failed to load user database: %s", strerror(-r));
1831 r = load_group_database();
1833 log_error("Failed to read group database: %s", strerror(-r));
1837 HASHMAP_FOREACH(i, groups, iterator)
1840 HASHMAP_FOREACH(i, users, iterator)
1845 log_error("Failed to write files: %s", strerror(-r));
1848 while ((i = hashmap_steal_first(groups)))
1851 while ((i = hashmap_steal_first(users)))
1854 while ((n = hashmap_first_key(members))) {
1855 strv_free(hashmap_steal_first(members));
1859 hashmap_free(groups);
1860 hashmap_free(users);
1861 hashmap_free(members);
1862 hashmap_free(todo_uids);
1863 hashmap_free(todo_gids);
1865 free_database(database_user, database_uid);
1866 free_database(database_group, database_gid);
1870 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;