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"
41 #include "uid-range.h"
43 typedef enum ItemType {
68 static char *arg_root = NULL;
70 static const char conf_file_dirs[] =
73 "/usr/local/lib/sysusers.d\0"
74 "/usr/lib/sysusers.d\0"
80 static Hashmap *users = NULL, *groups = NULL;
81 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
82 static Hashmap *members = NULL;
84 static Hashmap *database_uid = NULL, *database_user = NULL;
85 static Hashmap *database_gid = NULL, *database_group = NULL;
87 static uid_t search_uid = (uid_t) -1;
88 static UidRange *uid_range = NULL;
89 static unsigned n_uid_range = 0;
91 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
92 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
94 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
95 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
97 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
99 static int load_user_database(void) {
100 _cleanup_fclose_ FILE *f = NULL;
101 const char *passwd_path;
105 passwd_path = fix_root("/etc/passwd");
106 f = fopen(passwd_path, "re");
108 return errno == ENOENT ? 0 : -errno;
110 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
114 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
119 while ((pw = fgetpwent(f))) {
123 n = strdup(pw->pw_name);
127 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
128 if (k < 0 && k != -EEXIST) {
133 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
134 if (q < 0 && q != -EEXIST) {
145 if (!IN_SET(errno, 0, ENOENT))
151 static int load_group_database(void) {
152 _cleanup_fclose_ FILE *f = NULL;
153 const char *group_path;
157 group_path = fix_root("/etc/group");
158 f = fopen(group_path, "re");
160 return errno == ENOENT ? 0 : -errno;
162 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
166 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
171 while ((gr = fgetgrent(f))) {
175 n = strdup(gr->gr_name);
179 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
180 if (k < 0 && k != -EEXIST) {
185 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
186 if (q < 0 && q != -EEXIST) {
197 if (!IN_SET(errno, 0, ENOENT))
203 static int make_backup(const char *target, const char *x) {
204 _cleanup_close_ int src = -1;
205 _cleanup_fclose_ FILE *dst = NULL;
207 struct timespec ts[2];
211 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
213 if (errno == ENOENT) /* No backup necessary... */
219 if (fstat(src, &st) < 0)
222 r = fopen_temporary_label(target, x, &dst, &temp);
226 r = copy_bytes(src, fileno(dst), (off_t) -1);
230 /* Don't fail on chmod() or chown(). If it stays owned by us
231 * and/or unreadable by others, then it isn't too bad... */
233 backup = strappenda(x, "-");
235 /* Copy over the access mask */
236 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
237 log_warning("Failed to change mode on %s: %m", backup);
239 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
240 log_warning("Failed to change ownership of %s: %m", backup);
244 futimens(fileno(dst), ts);
246 if (rename(temp, backup) < 0)
256 static int putgrent_with_members(const struct group *gr, FILE *group) {
262 a = hashmap_get(members, gr->gr_name);
264 _cleanup_strv_free_ char **l = NULL;
268 l = strv_copy(gr->gr_mem);
273 if (strv_find(l, *i))
276 if (strv_extend(&l, *i) < 0)
292 if (putgrent(&t, group) != 0)
293 return errno ? -errno : -EIO;
300 if (putgrent(gr, group) != 0)
301 return errno ? -errno : -EIO;
306 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
312 a = hashmap_get(members, sg->sg_namp);
314 _cleanup_strv_free_ char **l = NULL;
318 l = strv_copy(sg->sg_mem);
323 if (strv_find(l, *i))
326 if (strv_extend(&l, *i) < 0)
342 if (putsgent(&t, gshadow) != 0)
343 return errno ? -errno : -EIO;
350 if (putsgent(sg, gshadow) != 0)
351 return errno ? -errno : -EIO;
356 static int write_files(void) {
358 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
359 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
360 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
361 bool group_changed = false;
366 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
367 _cleanup_fclose_ FILE *original = NULL;
369 /* First we update the actual group list file */
370 group_path = fix_root("/etc/group");
371 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
375 if (fchmod(fileno(group), 0644) < 0) {
380 original = fopen(group_path, "re");
385 while ((gr = fgetgrent(original))) {
386 /* Safety checks against name and GID
387 * collisions. Normally, this should
388 * be unnecessary, but given that we
389 * look at the entries anyway here,
390 * let's make an extra verification
391 * step that we don't generate
392 * duplicate entries. */
394 i = hashmap_get(groups, gr->gr_name);
395 if (i && i->todo_group) {
400 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
405 r = putgrent_with_members(gr, group);
409 group_changed = true;
413 if (!IN_SET(errno, 0, ENOENT)) {
418 } else if (errno != ENOENT) {
423 HASHMAP_FOREACH(i, todo_gids, iterator) {
427 .gr_passwd = (char*) "x",
430 r = putgrent_with_members(&n, group);
434 group_changed = true;
437 r = fflush_and_check(group);
446 /* OK, now also update the shadow file for the group list */
447 gshadow_path = fix_root("/etc/gshadow");
448 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
452 if (fchmod(fileno(gshadow), 0000) < 0) {
457 original = fopen(gshadow_path, "re");
462 while ((sg = fgetsgent(original))) {
464 i = hashmap_get(groups, sg->sg_namp);
465 if (i && i->todo_group) {
470 r = putsgent_with_members(sg, gshadow);
474 group_changed = true;
478 if (!IN_SET(errno, 0, ENOENT)) {
483 } else if (errno != ENOENT) {
488 HASHMAP_FOREACH(i, todo_gids, iterator) {
491 .sg_passwd = (char*) "!!",
494 r = putsgent_with_members(&n, gshadow);
498 group_changed = true;
501 r = fflush_and_check(gshadow);
506 if (hashmap_size(todo_uids) > 0) {
507 _cleanup_fclose_ FILE *original = NULL;
510 /* First we update the user database itself */
511 passwd_path = fix_root("/etc/passwd");
512 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
516 if (fchmod(fileno(passwd), 0644) < 0) {
521 original = fopen(passwd_path, "re");
526 while ((pw = fgetpwent(original))) {
528 i = hashmap_get(users, pw->pw_name);
529 if (i && i->todo_user) {
534 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
540 if (putpwent(pw, passwd) < 0) {
541 r = errno ? -errno : -EIO;
547 if (!IN_SET(errno, 0, ENOENT)) {
552 } else if (errno != ENOENT) {
557 HASHMAP_FOREACH(i, todo_uids, iterator) {
562 .pw_gecos = i->description,
564 /* "x" means the password is stored in
566 .pw_passwd = (char*) "x",
568 /* We default to the root directory as home */
569 .pw_dir = i->home ? i->home : (char*) "/",
571 /* Initialize the shell to nologin,
572 * with one exception: for root we
573 * patch in something special */
574 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
578 if (putpwent(&n, passwd) != 0) {
579 r = errno ? -errno : -EIO;
584 r = fflush_and_check(passwd);
593 /* The we update the shadow database */
594 shadow_path = fix_root("/etc/shadow");
595 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
599 if (fchmod(fileno(shadow), 0000) < 0) {
604 original = fopen(shadow_path, "re");
609 while ((sp = fgetspent(original))) {
611 i = hashmap_get(users, sp->sp_namp);
612 if (i && i->todo_user) {
618 if (putspent(sp, shadow) < 0) {
619 r = errno ? -errno : -EIO;
625 if (!IN_SET(errno, 0, ENOENT)) {
629 } else if (errno != ENOENT) {
634 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
635 HASHMAP_FOREACH(i, todo_uids, iterator) {
638 .sp_pwdp = (char*) "!!",
645 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
649 if (putspent(&n, shadow) != 0) {
650 r = errno ? -errno : -EIO;
655 r = fflush_and_check(shadow);
660 /* Make a backup of the old files */
663 r = make_backup("/etc/group", group_path);
668 r = make_backup("/etc/gshadow", gshadow_path);
675 r = make_backup("/etc/passwd", passwd_path);
680 r = make_backup("/etc/shadow", shadow_path);
685 /* And make the new files count */
688 if (rename(group_tmp, group_path) < 0) {
697 if (rename(gshadow_tmp, gshadow_path) < 0) {
708 if (rename(passwd_tmp, passwd_path) < 0) {
717 if (rename(shadow_tmp, shadow_path) < 0) {
741 static int uid_is_ok(uid_t uid, const char *name) {
747 /* Let's see if we already have assigned the UID a second time */
748 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
751 /* Try to avoid using uids that are already used by a group
752 * that doesn't have the same name as our new user. */
753 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
754 if (i && !streq(i->name, name))
757 /* Let's check the files directly */
758 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
761 n = hashmap_get(database_gid, GID_TO_PTR(uid));
762 if (n && !streq(n, name))
765 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
771 if (!IN_SET(errno, 0, ENOENT))
775 g = getgrgid((gid_t) uid);
777 if (!streq(g->gr_name, name))
779 } else if (!IN_SET(errno, 0, ENOENT))
786 static int root_stat(const char *p, struct stat *st) {
790 if (stat(fix, st) < 0)
796 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
798 bool found_uid = false, found_gid = false;
804 /* First, try to get the gid directly */
805 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
810 /* Then, try to get the uid directly */
811 if ((_uid || (_gid && !found_gid))
813 && root_stat(i->uid_path, &st) >= 0) {
818 /* If we need the gid, but had no success yet, also derive it from the uid path */
819 if (_gid && !found_gid) {
825 /* If that didn't work yet, then let's reuse the gid as uid */
826 if (_uid && !found_uid && i->gid_path) {
831 } else if (root_stat(i->gid_path, &st) >= 0) {
832 uid = (uid_t) st.st_gid;
854 static int add_user(Item *i) {
860 /* Check the database directly */
861 z = hashmap_get(database_user, i->name);
863 log_debug("User %s already exists.", i->name);
864 i->uid = PTR_TO_UID(z);
875 p = getpwnam(i->name);
877 log_debug("User %s already exists.", i->name);
881 free(i->description);
882 i->description = strdup(p->pw_gecos);
885 if (!IN_SET(errno, 0, ENOENT)) {
886 log_error("Failed to check if user %s already exists: %m", i->name);
890 /* And shadow too, just to be sure */
892 sp = getspnam(i->name);
894 log_error("User %s already exists in shadow database, but not in user database.", i->name);
897 if (!IN_SET(errno, 0, ENOENT)) {
898 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
903 /* Try to use the suggested numeric uid */
905 r = uid_is_ok(i->uid, i->name);
907 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
911 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
916 /* If that didn't work, try to read it from the specified path */
920 if (read_id_from_file(i, &c, NULL) > 0) {
922 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
923 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
925 r = uid_is_ok(c, i->name);
927 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
933 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
938 /* Otherwise try to reuse the group ID */
939 if (!i->uid_set && i->gid_set) {
940 r = uid_is_ok((uid_t) i->gid, i->name);
942 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
946 i->uid = (uid_t) i->gid;
951 /* And if that didn't work either, let's try to find a free one */
954 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
956 log_error("No free user ID available for %s.", i->name);
960 r = uid_is_ok(search_uid, i->name);
962 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
972 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
976 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
981 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
986 static int gid_is_ok(gid_t gid) {
990 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
993 /* Avoid reusing gids that are already used by a different user */
994 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
997 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1000 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1008 if (!IN_SET(errno, 0, ENOENT))
1012 p = getpwuid((uid_t) gid);
1015 if (!IN_SET(errno, 0, ENOENT))
1022 static int add_group(Item *i) {
1028 /* Check the database directly */
1029 z = hashmap_get(database_group, i->name);
1031 log_debug("Group %s already exists.", i->name);
1032 i->gid = PTR_TO_GID(z);
1037 /* Also check NSS */
1042 g = getgrnam(i->name);
1044 log_debug("Group %s already exists.", i->name);
1049 if (!IN_SET(errno, 0, ENOENT)) {
1050 log_error("Failed to check if group %s already exists: %m", i->name);
1055 /* Try to use the suggested numeric gid */
1057 r = gid_is_ok(i->gid);
1059 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1063 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1068 /* Try to reuse the numeric uid, if there's one */
1069 if (!i->gid_set && i->uid_set) {
1070 r = gid_is_ok((gid_t) i->uid);
1072 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1076 i->gid = (gid_t) i->uid;
1081 /* If that didn't work, try to read it from the specified path */
1085 if (read_id_from_file(i, NULL, &c) > 0) {
1087 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1088 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1092 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1098 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1103 /* And if that didn't work either, let's try to find a free one */
1106 /* We look for new GIDs in the UID pool! */
1107 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1109 log_error("No free group ID available for %s.", i->name);
1113 r = gid_is_ok(search_uid);
1115 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1122 i->gid = search_uid;
1125 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
1129 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1133 i->todo_group = true;
1134 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1139 static int process_item(Item *i) {
1156 j = hashmap_get(users, i->name);
1158 /* There's already user to be created for this
1159 * name, let's process that in one step */
1168 j->gid_path = strdup(i->gid_path);
1176 return add_group(i);
1180 assert_not_reached("Unknown item type");
1184 static void item_free(Item *i) {
1192 free(i->description);
1196 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1198 static int add_implicit(void) {
1203 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1205 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1209 i = hashmap_get(groups, g);
1211 _cleanup_(item_freep) Item *j = NULL;
1213 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1221 j->type = ADD_GROUP;
1222 j->name = strdup(g);
1226 r = hashmap_put(groups, j->name, j);
1230 log_debug("Adding implicit group '%s' due to m line", j->name);
1234 STRV_FOREACH(m, l) {
1236 i = hashmap_get(users, *m);
1238 _cleanup_(item_freep) Item *j = NULL;
1240 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1249 j->name = strdup(*m);
1253 r = hashmap_put(users, j->name, j);
1257 log_debug("Adding implicit user '%s' due to m line", j->name);
1266 static bool item_equal(Item *a, Item *b) {
1270 if (a->type != b->type)
1273 if (!streq_ptr(a->name, b->name))
1276 if (!streq_ptr(a->uid_path, b->uid_path))
1279 if (!streq_ptr(a->gid_path, b->gid_path))
1282 if (!streq_ptr(a->description, b->description))
1285 if (a->uid_set != b->uid_set)
1288 if (a->uid_set && a->uid != b->uid)
1291 if (a->gid_set != b->gid_set)
1294 if (a->gid_set && a->gid != b->gid)
1297 if (!streq_ptr(a->home, b->home))
1303 static bool valid_user_group_name(const char *u) {
1310 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1311 !(u[0] >= 'A' && u[0] <= 'Z') &&
1315 for (i = u+1; *i; i++) {
1316 if (!(*i >= 'a' && *i <= 'z') &&
1317 !(*i >= 'A' && *i <= 'Z') &&
1318 !(*i >= '0' && *i <= '9') &&
1324 sz = sysconf(_SC_LOGIN_NAME_MAX);
1327 if ((size_t) (i-u) > (size_t) sz)
1330 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1336 static bool valid_gecos(const char *d) {
1341 if (!utf8_is_valid(d))
1344 if (string_has_cc(d, NULL))
1347 /* Colons are used as field separators, and hence not OK */
1354 static bool valid_home(const char *p) {
1359 if (!utf8_is_valid(p))
1362 if (string_has_cc(p, NULL))
1365 if (!path_is_absolute(p))
1368 if (!path_is_safe(p))
1371 /* Colons are used as field separators, and hence not OK */
1378 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1380 static const Specifier specifier_table[] = {
1381 { 'm', specifier_machine_id, NULL },
1382 { 'b', specifier_boot_id, NULL },
1383 { 'H', specifier_host_name, NULL },
1384 { 'v', specifier_kernel_release, NULL },
1388 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
1389 _cleanup_(item_freep) Item *i = NULL;
1401 r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
1403 log_error("[%s:%u] Syntax error.", fname, line);
1407 log_error("[%s:%u] Missing action and name columns.", fname, line);
1411 log_error("[%s:%u] Trailing garbage.", fname, line);
1416 if (strlen(action) != 1) {
1417 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1421 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1422 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1427 if (isempty(name) || streq(name, "-")) {
1433 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1435 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1439 if (!valid_user_group_name(resolved_name)) {
1440 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1446 if (isempty(id) || streq(id, "-")) {
1452 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1454 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1459 /* Verify description */
1460 if (isempty(description) || streq(description, "-")) {
1466 if (!valid_gecos(description)) {
1467 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1473 if (isempty(home) || streq(home, "-")) {
1479 if (!valid_home(home)) {
1480 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1485 switch (action[0]) {
1488 if (resolved_name) {
1489 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1494 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1499 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1504 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1508 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1510 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1519 /* Try to extend an existing member or group item */
1521 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1526 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1530 if (!valid_user_group_name(resolved_id)) {
1531 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1536 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1541 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1545 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1549 l = hashmap_get(members, resolved_id);
1551 /* A list for this group name already exists, let's append to it */
1552 r = strv_push(&l, resolved_name);
1556 resolved_name = NULL;
1558 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1560 /* No list for this group name exists yet, create one */
1562 l = new0(char *, 2);
1566 l[0] = resolved_name;
1569 r = hashmap_put(members, resolved_id, l);
1575 resolved_id = resolved_name = NULL;
1583 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1587 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1596 if (path_is_absolute(resolved_id)) {
1597 i->uid_path = resolved_id;
1600 path_kill_slashes(i->uid_path);
1602 r = parse_uid(resolved_id, &i->uid);
1604 log_error("Failed to parse UID: %s", id);
1612 i->description = description;
1623 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1628 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1633 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1637 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1646 if (path_is_absolute(resolved_id)) {
1647 i->gid_path = resolved_id;
1650 path_kill_slashes(i->gid_path);
1652 r = parse_gid(resolved_id, &i->gid);
1654 log_error("Failed to parse GID: %s", id);
1669 i->type = action[0];
1670 i->name = resolved_name;
1671 resolved_name = NULL;
1673 existing = hashmap_get(h, i->name);
1676 /* Two identical items are fine */
1677 if (!item_equal(existing, i))
1678 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1683 r = hashmap_put(h, i->name, i);
1691 static int read_config_file(const char *fn, bool ignore_enoent) {
1692 _cleanup_fclose_ FILE *rf = NULL;
1694 char line[LINE_MAX];
1703 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1705 if (ignore_enoent && r == -ENOENT)
1708 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1715 FOREACH_LINE(line, f, break) {
1722 if (*l == '#' || *l == 0)
1725 k = parse_line(fn, v, l);
1726 if (k < 0 && r == 0)
1731 log_error("Failed to read from file %s: %m", fn);
1739 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1743 name = hashmap_first(by_id);
1747 hashmap_remove(by_name, name);
1749 hashmap_steal_first_key(by_id);
1753 while ((name = hashmap_steal_first_key(by_name)))
1756 hashmap_free(by_name);
1757 hashmap_free(by_id);
1760 static void help(void) {
1761 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1762 "Creates system user accounts.\n\n"
1763 " -h --help Show this help\n"
1764 " --version Show package version\n"
1765 " --root=PATH Operate on an alternate filesystem root\n"
1766 , program_invocation_short_name);
1769 static int parse_argv(int argc, char *argv[]) {
1772 ARG_VERSION = 0x100,
1776 static const struct option options[] = {
1777 { "help", no_argument, NULL, 'h' },
1778 { "version", no_argument, NULL, ARG_VERSION },
1779 { "root", required_argument, NULL, ARG_ROOT },
1788 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1797 puts(PACKAGE_STRING);
1798 puts(SYSTEMD_FEATURES);
1803 arg_root = path_make_absolute_cwd(optarg);
1807 path_kill_slashes(arg_root);
1814 assert_not_reached("Unhandled option");
1820 int main(int argc, char *argv[]) {
1822 _cleanup_close_ int lock = -1;
1828 r = parse_argv(argc, argv);
1832 log_set_target(LOG_TARGET_AUTO);
1833 log_parse_environment();
1838 r = label_init(NULL);
1840 log_error("SELinux setup failed: %s", strerror(-r));
1844 if (optind < argc) {
1847 for (j = optind; j < argc; j++) {
1848 k = read_config_file(argv[j], false);
1849 if (k < 0 && r == 0)
1853 _cleanup_strv_free_ char **files = NULL;
1856 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1858 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1862 STRV_FOREACH(f, files) {
1863 k = read_config_file(*f, true);
1864 if (k < 0 && r == 0)
1870 /* Default to default range of 1..SYSTEMD_UID_MAX */
1871 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1882 lock = take_password_lock(arg_root);
1884 log_error("Failed to take lock: %s", strerror(-lock));
1888 r = load_user_database();
1890 log_error("Failed to load user database: %s", strerror(-r));
1894 r = load_group_database();
1896 log_error("Failed to read group database: %s", strerror(-r));
1900 HASHMAP_FOREACH(i, groups, iterator)
1903 HASHMAP_FOREACH(i, users, iterator)
1908 log_error("Failed to write files: %s", strerror(-r));
1911 while ((i = hashmap_steal_first(groups)))
1914 while ((i = hashmap_steal_first(users)))
1917 while ((n = hashmap_first_key(members))) {
1918 strv_free(hashmap_steal_first(members));
1922 hashmap_free(groups);
1923 hashmap_free(users);
1924 hashmap_free(members);
1925 hashmap_free(todo_uids);
1926 hashmap_free(todo_gids);
1928 free_database(database_user, database_uid);
1929 free_database(database_group, database_gid);
1933 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;