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"
42 #include "selinux-util.h"
44 typedef enum ItemType {
69 static char *arg_root = NULL;
71 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysusers");
73 static Hashmap *users = NULL, *groups = NULL;
74 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
75 static Hashmap *members = NULL;
77 static Hashmap *database_uid = NULL, *database_user = NULL;
78 static Hashmap *database_gid = NULL, *database_group = NULL;
80 static uid_t search_uid = UID_INVALID;
81 static UidRange *uid_range = NULL;
82 static unsigned n_uid_range = 0;
84 #define fix_root(x) (arg_root ? strjoina(arg_root, x) : x)
86 static int load_user_database(void) {
87 _cleanup_fclose_ FILE *f = NULL;
88 const char *passwd_path;
92 passwd_path = fix_root("/etc/passwd");
93 f = fopen(passwd_path, "re");
95 return errno == ENOENT ? 0 : -errno;
97 r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
101 r = hashmap_ensure_allocated(&database_uid, NULL);
106 while ((pw = fgetpwent(f))) {
110 n = strdup(pw->pw_name);
114 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
115 if (k < 0 && k != -EEXIST) {
120 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
121 if (q < 0 && q != -EEXIST) {
132 if (!IN_SET(errno, 0, ENOENT))
138 static int load_group_database(void) {
139 _cleanup_fclose_ FILE *f = NULL;
140 const char *group_path;
144 group_path = fix_root("/etc/group");
145 f = fopen(group_path, "re");
147 return errno == ENOENT ? 0 : -errno;
149 r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
153 r = hashmap_ensure_allocated(&database_gid, NULL);
158 while ((gr = fgetgrent(f))) {
162 n = strdup(gr->gr_name);
166 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
167 if (k < 0 && k != -EEXIST) {
172 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
173 if (q < 0 && q != -EEXIST) {
184 if (!IN_SET(errno, 0, ENOENT))
190 static int make_backup(const char *target, const char *x) {
191 _cleanup_close_ int src = -1;
192 _cleanup_fclose_ FILE *dst = NULL;
194 struct timespec ts[2];
198 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
200 if (errno == ENOENT) /* No backup necessary... */
206 if (fstat(src, &st) < 0)
209 r = fopen_temporary_label(target, x, &dst, &temp);
213 r = copy_bytes(src, fileno(dst), (off_t) -1, true);
217 /* Don't fail on chmod() or chown(). If it stays owned by us
218 * and/or unreadable by others, then it isn't too bad... */
220 backup = strjoina(x, "-");
222 /* Copy over the access mask */
223 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
224 log_warning_errno(errno, "Failed to change mode on %s: %m", backup);
226 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
227 log_warning_errno(errno, "Failed to change ownership of %s: %m", backup);
231 if (futimens(fileno(dst), ts) < 0)
232 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
234 if (rename(temp, backup) < 0)
244 static int putgrent_with_members(const struct group *gr, FILE *group) {
250 a = hashmap_get(members, gr->gr_name);
252 _cleanup_strv_free_ char **l = NULL;
256 l = strv_copy(gr->gr_mem);
261 if (strv_find(l, *i))
264 if (strv_extend(&l, *i) < 0)
280 if (putgrent(&t, group) != 0)
281 return errno ? -errno : -EIO;
288 if (putgrent(gr, group) != 0)
289 return errno ? -errno : -EIO;
294 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
300 a = hashmap_get(members, sg->sg_namp);
302 _cleanup_strv_free_ char **l = NULL;
306 l = strv_copy(sg->sg_mem);
311 if (strv_find(l, *i))
314 if (strv_extend(&l, *i) < 0)
330 if (putsgent(&t, gshadow) != 0)
331 return errno ? -errno : -EIO;
338 if (putsgent(sg, gshadow) != 0)
339 return errno ? -errno : -EIO;
344 static int sync_rights(FILE *from, FILE *to) {
347 if (fstat(fileno(from), &st) < 0)
350 if (fchmod(fileno(to), st.st_mode & 07777) < 0)
353 if (fchown(fileno(to), st.st_uid, st.st_gid) < 0)
359 static int write_files(void) {
361 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
362 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
363 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
364 bool group_changed = false;
369 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
370 _cleanup_fclose_ FILE *original = NULL;
372 /* First we update the actual group list file */
373 group_path = fix_root("/etc/group");
374 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
378 original = fopen(group_path, "re");
382 r = sync_rights(original, group);
387 while ((gr = fgetgrent(original))) {
388 /* Safety checks against name and GID
389 * collisions. Normally, this should
390 * be unnecessary, but given that we
391 * look at the entries anyway here,
392 * let's make an extra verification
393 * step that we don't generate
394 * duplicate entries. */
396 i = hashmap_get(groups, gr->gr_name);
397 if (i && i->todo_group) {
402 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
407 r = putgrent_with_members(gr, group);
411 group_changed = true;
415 if (!IN_SET(errno, 0, ENOENT)) {
420 } else if (errno != ENOENT) {
423 } else if (fchmod(fileno(group), 0644) < 0) {
428 HASHMAP_FOREACH(i, todo_gids, iterator) {
432 .gr_passwd = (char*) "x",
435 r = putgrent_with_members(&n, group);
439 group_changed = true;
442 r = fflush_and_check(group);
451 /* OK, now also update the shadow file for the group list */
452 gshadow_path = fix_root("/etc/gshadow");
453 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
457 original = fopen(gshadow_path, "re");
461 r = sync_rights(original, gshadow);
466 while ((sg = fgetsgent(original))) {
468 i = hashmap_get(groups, sg->sg_namp);
469 if (i && i->todo_group) {
474 r = putsgent_with_members(sg, gshadow);
478 group_changed = true;
482 if (!IN_SET(errno, 0, ENOENT)) {
487 } else if (errno != ENOENT) {
490 } else if (fchmod(fileno(gshadow), 0000) < 0) {
495 HASHMAP_FOREACH(i, todo_gids, iterator) {
498 .sg_passwd = (char*) "!!",
501 r = putsgent_with_members(&n, gshadow);
505 group_changed = true;
508 r = fflush_and_check(gshadow);
513 if (hashmap_size(todo_uids) > 0) {
514 _cleanup_fclose_ FILE *original = NULL;
517 /* First we update the user database itself */
518 passwd_path = fix_root("/etc/passwd");
519 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
523 original = fopen(passwd_path, "re");
527 r = sync_rights(original, passwd);
532 while ((pw = fgetpwent(original))) {
534 i = hashmap_get(users, pw->pw_name);
535 if (i && i->todo_user) {
540 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
546 if (putpwent(pw, passwd) < 0) {
547 r = errno ? -errno : -EIO;
553 if (!IN_SET(errno, 0, ENOENT)) {
558 } else if (errno != ENOENT) {
561 } else if (fchmod(fileno(passwd), 0644) < 0) {
566 HASHMAP_FOREACH(i, todo_uids, iterator) {
571 .pw_gecos = i->description,
573 /* "x" means the password is stored in
575 .pw_passwd = (char*) "x",
577 /* We default to the root directory as home */
578 .pw_dir = i->home ? i->home : (char*) "/",
580 /* Initialize the shell to nologin,
581 * with one exception: for root we
582 * patch in something special */
583 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
587 if (putpwent(&n, passwd) != 0) {
588 r = errno ? -errno : -EIO;
593 r = fflush_and_check(passwd);
602 /* The we update the shadow database */
603 shadow_path = fix_root("/etc/shadow");
604 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
608 original = fopen(shadow_path, "re");
612 r = sync_rights(original, shadow);
617 while ((sp = fgetspent(original))) {
619 i = hashmap_get(users, sp->sp_namp);
620 if (i && i->todo_user) {
626 if (putspent(sp, shadow) < 0) {
627 r = errno ? -errno : -EIO;
633 if (!IN_SET(errno, 0, ENOENT)) {
637 } else if (errno != ENOENT) {
640 } else if (fchmod(fileno(shadow), 0000) < 0) {
645 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
646 HASHMAP_FOREACH(i, todo_uids, iterator) {
649 .sp_pwdp = (char*) "!!",
656 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
660 if (putspent(&n, shadow) != 0) {
661 r = errno ? -errno : -EIO;
666 r = fflush_and_check(shadow);
671 /* Make a backup of the old files */
674 r = make_backup("/etc/group", group_path);
679 r = make_backup("/etc/gshadow", gshadow_path);
686 r = make_backup("/etc/passwd", passwd_path);
691 r = make_backup("/etc/shadow", shadow_path);
696 /* And make the new files count */
699 if (rename(group_tmp, group_path) < 0) {
708 if (rename(gshadow_tmp, gshadow_path) < 0) {
719 if (rename(passwd_tmp, passwd_path) < 0) {
728 if (rename(shadow_tmp, shadow_path) < 0) {
752 static int uid_is_ok(uid_t uid, const char *name) {
758 /* Let's see if we already have assigned the UID a second time */
759 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
762 /* Try to avoid using uids that are already used by a group
763 * that doesn't have the same name as our new user. */
764 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
765 if (i && !streq(i->name, name))
768 /* Let's check the files directly */
769 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
772 n = hashmap_get(database_gid, GID_TO_PTR(uid));
773 if (n && !streq(n, name))
776 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
782 if (!IN_SET(errno, 0, ENOENT))
786 g = getgrgid((gid_t) uid);
788 if (!streq(g->gr_name, name))
790 } else if (!IN_SET(errno, 0, ENOENT))
797 static int root_stat(const char *p, struct stat *st) {
801 if (stat(fix, st) < 0)
807 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
809 bool found_uid = false, found_gid = false;
815 /* First, try to get the gid directly */
816 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
821 /* Then, try to get the uid directly */
822 if ((_uid || (_gid && !found_gid))
824 && root_stat(i->uid_path, &st) >= 0) {
829 /* If we need the gid, but had no success yet, also derive it from the uid path */
830 if (_gid && !found_gid) {
836 /* If that didn't work yet, then let's reuse the gid as uid */
837 if (_uid && !found_uid && i->gid_path) {
842 } else if (root_stat(i->gid_path, &st) >= 0) {
843 uid = (uid_t) st.st_gid;
865 static int add_user(Item *i) {
871 /* Check the database directly */
872 z = hashmap_get(database_user, i->name);
874 log_debug("User %s already exists.", i->name);
875 i->uid = PTR_TO_UID(z);
886 p = getpwnam(i->name);
888 log_debug("User %s already exists.", i->name);
892 free(i->description);
893 i->description = strdup(p->pw_gecos);
896 if (!IN_SET(errno, 0, ENOENT))
897 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
899 /* And shadow too, just to be sure */
901 sp = getspnam(i->name);
903 log_error("User %s already exists in shadow database, but not in user database.", i->name);
906 if (!IN_SET(errno, 0, ENOENT))
907 return log_error_errno(errno, "Failed to check if user %s already exists in shadow database: %m", i->name);
910 /* Try to use the suggested numeric uid */
912 r = uid_is_ok(i->uid, i->name);
914 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
916 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
921 /* If that didn't work, try to read it from the specified path */
925 if (read_id_from_file(i, &c, NULL) > 0) {
927 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
928 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
930 r = uid_is_ok(c, i->name);
932 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
937 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
942 /* Otherwise try to reuse the group ID */
943 if (!i->uid_set && i->gid_set) {
944 r = uid_is_ok((uid_t) i->gid, i->name);
946 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
948 i->uid = (uid_t) i->gid;
953 /* And if that didn't work either, let's try to find a free one */
956 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
958 log_error("No free user ID available for %s.", i->name);
962 r = uid_is_ok(search_uid, i->name);
964 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
973 r = hashmap_ensure_allocated(&todo_uids, NULL);
977 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
982 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
987 static int gid_is_ok(gid_t gid) {
991 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
994 /* Avoid reusing gids that are already used by a different user */
995 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
998 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1001 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1009 if (!IN_SET(errno, 0, ENOENT))
1013 p = getpwuid((uid_t) gid);
1016 if (!IN_SET(errno, 0, ENOENT))
1023 static int add_group(Item *i) {
1029 /* Check the database directly */
1030 z = hashmap_get(database_group, i->name);
1032 log_debug("Group %s already exists.", i->name);
1033 i->gid = PTR_TO_GID(z);
1038 /* Also check NSS */
1043 g = getgrnam(i->name);
1045 log_debug("Group %s already exists.", i->name);
1050 if (!IN_SET(errno, 0, ENOENT))
1051 return log_error_errno(errno, "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 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1060 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1065 /* Try to reuse the numeric uid, if there's one */
1066 if (!i->gid_set && i->uid_set) {
1067 r = gid_is_ok((gid_t) i->uid);
1069 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1071 i->gid = (gid_t) i->uid;
1076 /* If that didn't work, try to read it from the specified path */
1080 if (read_id_from_file(i, NULL, &c) > 0) {
1082 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1083 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1087 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1092 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1097 /* And if that didn't work either, let's try to find a free one */
1100 /* We look for new GIDs in the UID pool! */
1101 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1103 log_error("No free group ID available for %s.", i->name);
1107 r = gid_is_ok(search_uid);
1109 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1115 i->gid = search_uid;
1118 r = hashmap_ensure_allocated(&todo_gids, NULL);
1122 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1126 i->todo_group = true;
1127 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1132 static int process_item(Item *i) {
1149 j = hashmap_get(users, i->name);
1151 /* There's already user to be created for this
1152 * name, let's process that in one step */
1161 j->gid_path = strdup(i->gid_path);
1169 return add_group(i);
1173 assert_not_reached("Unknown item type");
1177 static void item_free(Item *i) {
1185 free(i->description);
1189 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1191 static int add_implicit(void) {
1196 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1198 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1202 i = hashmap_get(groups, g);
1204 _cleanup_(item_freep) Item *j = NULL;
1206 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1214 j->type = ADD_GROUP;
1215 j->name = strdup(g);
1219 r = hashmap_put(groups, j->name, j);
1223 log_debug("Adding implicit group '%s' due to m line", j->name);
1227 STRV_FOREACH(m, l) {
1229 i = hashmap_get(users, *m);
1231 _cleanup_(item_freep) Item *j = NULL;
1233 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1242 j->name = strdup(*m);
1246 r = hashmap_put(users, j->name, j);
1250 log_debug("Adding implicit user '%s' due to m line", j->name);
1259 static bool item_equal(Item *a, Item *b) {
1263 if (a->type != b->type)
1266 if (!streq_ptr(a->name, b->name))
1269 if (!streq_ptr(a->uid_path, b->uid_path))
1272 if (!streq_ptr(a->gid_path, b->gid_path))
1275 if (!streq_ptr(a->description, b->description))
1278 if (a->uid_set != b->uid_set)
1281 if (a->uid_set && a->uid != b->uid)
1284 if (a->gid_set != b->gid_set)
1287 if (a->gid_set && a->gid != b->gid)
1290 if (!streq_ptr(a->home, b->home))
1296 static bool valid_user_group_name(const char *u) {
1303 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1304 !(u[0] >= 'A' && u[0] <= 'Z') &&
1308 for (i = u+1; *i; i++) {
1309 if (!(*i >= 'a' && *i <= 'z') &&
1310 !(*i >= 'A' && *i <= 'Z') &&
1311 !(*i >= '0' && *i <= '9') &&
1317 sz = sysconf(_SC_LOGIN_NAME_MAX);
1320 if ((size_t) (i-u) > (size_t) sz)
1323 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1329 static bool valid_gecos(const char *d) {
1334 if (!utf8_is_valid(d))
1337 if (string_has_cc(d, NULL))
1340 /* Colons are used as field separators, and hence not OK */
1347 static bool valid_home(const char *p) {
1352 if (!utf8_is_valid(p))
1355 if (string_has_cc(p, NULL))
1358 if (!path_is_absolute(p))
1361 if (!path_is_safe(p))
1364 /* Colons are used as field separators, and hence not OK */
1371 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1373 static const Specifier specifier_table[] = {
1374 { 'm', specifier_machine_id, NULL },
1375 { 'b', specifier_boot_id, NULL },
1376 { 'H', specifier_host_name, NULL },
1377 { 'v', specifier_kernel_release, NULL },
1381 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
1382 _cleanup_(item_freep) Item *i = NULL;
1394 r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
1396 log_error("[%s:%u] Syntax error.", fname, line);
1400 log_error("[%s:%u] Missing action and name columns.", fname, line);
1404 log_error("[%s:%u] Trailing garbage.", fname, line);
1409 if (strlen(action) != 1) {
1410 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1414 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1415 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1420 if (isempty(name) || streq(name, "-")) {
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);
1439 if (isempty(id) || streq(id, "-")) {
1445 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1447 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1452 /* Verify description */
1453 if (isempty(description) || streq(description, "-")) {
1459 if (!valid_gecos(description)) {
1460 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1466 if (isempty(home) || streq(home, "-")) {
1472 if (!valid_home(home)) {
1473 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1478 switch (action[0]) {
1481 if (resolved_name) {
1482 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1487 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1492 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1497 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1501 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1503 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1512 /* Try to extend an existing member or group item */
1514 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1519 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1523 if (!valid_user_group_name(resolved_id)) {
1524 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1529 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1534 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1538 r = hashmap_ensure_allocated(&members, &string_hash_ops);
1542 l = hashmap_get(members, resolved_id);
1544 /* A list for this group name already exists, let's append to it */
1545 r = strv_push(&l, resolved_name);
1549 resolved_name = NULL;
1551 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1553 /* No list for this group name exists yet, create one */
1555 l = new0(char *, 2);
1559 l[0] = resolved_name;
1562 r = hashmap_put(members, resolved_id, l);
1568 resolved_id = resolved_name = NULL;
1576 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1580 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1589 if (path_is_absolute(resolved_id)) {
1590 i->uid_path = resolved_id;
1593 path_kill_slashes(i->uid_path);
1595 r = parse_uid(resolved_id, &i->uid);
1597 log_error("Failed to parse UID: %s", id);
1605 i->description = description;
1616 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1621 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1626 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1630 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1639 if (path_is_absolute(resolved_id)) {
1640 i->gid_path = resolved_id;
1643 path_kill_slashes(i->gid_path);
1645 r = parse_gid(resolved_id, &i->gid);
1647 log_error("Failed to parse GID: %s", id);
1662 i->type = action[0];
1663 i->name = resolved_name;
1664 resolved_name = NULL;
1666 existing = hashmap_get(h, i->name);
1669 /* Two identical items are fine */
1670 if (!item_equal(existing, i))
1671 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1676 r = hashmap_put(h, i->name, i);
1684 static int read_config_file(const char *fn, bool ignore_enoent) {
1685 _cleanup_fclose_ FILE *rf = NULL;
1687 char line[LINE_MAX];
1696 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1698 if (ignore_enoent && r == -ENOENT)
1701 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1707 FOREACH_LINE(line, f, break) {
1714 if (*l == '#' || *l == 0)
1717 k = parse_line(fn, v, l);
1718 if (k < 0 && r == 0)
1723 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1731 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1735 name = hashmap_first(by_id);
1739 hashmap_remove(by_name, name);
1741 hashmap_steal_first_key(by_id);
1745 while ((name = hashmap_steal_first_key(by_name)))
1748 hashmap_free(by_name);
1749 hashmap_free(by_id);
1752 static void help(void) {
1753 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1754 "Creates system user accounts.\n\n"
1755 " -h --help Show this help\n"
1756 " --version Show package version\n"
1757 " --root=PATH Operate on an alternate filesystem root\n"
1758 , program_invocation_short_name);
1761 static int parse_argv(int argc, char *argv[]) {
1764 ARG_VERSION = 0x100,
1768 static const struct option options[] = {
1769 { "help", no_argument, NULL, 'h' },
1770 { "version", no_argument, NULL, ARG_VERSION },
1771 { "root", required_argument, NULL, ARG_ROOT },
1780 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1789 puts(PACKAGE_STRING);
1790 puts(SYSTEMD_FEATURES);
1795 arg_root = path_make_absolute_cwd(optarg);
1799 path_kill_slashes(arg_root);
1806 assert_not_reached("Unhandled option");
1812 int main(int argc, char *argv[]) {
1814 _cleanup_close_ int lock = -1;
1820 r = parse_argv(argc, argv);
1824 log_set_target(LOG_TARGET_AUTO);
1825 log_parse_environment();
1830 r = mac_selinux_init(NULL);
1832 log_error_errno(r, "SELinux setup failed: %m");
1836 if (optind < argc) {
1839 for (j = optind; j < argc; j++) {
1840 k = read_config_file(argv[j], false);
1841 if (k < 0 && r == 0)
1845 _cleanup_strv_free_ char **files = NULL;
1848 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1850 log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
1854 STRV_FOREACH(f, files) {
1855 k = read_config_file(*f, true);
1856 if (k < 0 && r == 0)
1862 /* Default to default range of 1..SYSTEMD_UID_MAX */
1863 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1874 lock = take_password_lock(arg_root);
1876 log_error_errno(lock, "Failed to take lock: %m");
1880 r = load_user_database();
1882 log_error_errno(r, "Failed to load user database: %m");
1886 r = load_group_database();
1888 log_error_errno(r, "Failed to read group database: %m");
1892 HASHMAP_FOREACH(i, groups, iterator)
1895 HASHMAP_FOREACH(i, users, iterator)
1900 log_error_errno(r, "Failed to write files: %m");
1903 while ((i = hashmap_steal_first(groups)))
1906 while ((i = hashmap_steal_first(users)))
1909 while ((n = hashmap_first_key(members))) {
1910 strv_free(hashmap_steal_first(members));
1914 hashmap_free(groups);
1915 hashmap_free(users);
1916 hashmap_free(members);
1917 hashmap_free(todo_uids);
1918 hashmap_free(todo_gids);
1920 free_database(database_user, database_uid);
1921 free_database(database_group, database_gid);
1925 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;