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 UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
85 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
87 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
88 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
90 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
92 static int load_user_database(void) {
93 _cleanup_fclose_ FILE *f = NULL;
94 const char *passwd_path;
98 passwd_path = fix_root("/etc/passwd");
99 f = fopen(passwd_path, "re");
101 return errno == ENOENT ? 0 : -errno;
103 r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
107 r = hashmap_ensure_allocated(&database_uid, NULL);
112 while ((pw = fgetpwent(f))) {
116 n = strdup(pw->pw_name);
120 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
121 if (k < 0 && k != -EEXIST) {
126 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
127 if (q < 0 && q != -EEXIST) {
138 if (!IN_SET(errno, 0, ENOENT))
144 static int load_group_database(void) {
145 _cleanup_fclose_ FILE *f = NULL;
146 const char *group_path;
150 group_path = fix_root("/etc/group");
151 f = fopen(group_path, "re");
153 return errno == ENOENT ? 0 : -errno;
155 r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
159 r = hashmap_ensure_allocated(&database_gid, NULL);
164 while ((gr = fgetgrent(f))) {
168 n = strdup(gr->gr_name);
172 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
173 if (k < 0 && k != -EEXIST) {
178 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
179 if (q < 0 && q != -EEXIST) {
190 if (!IN_SET(errno, 0, ENOENT))
196 static int make_backup(const char *target, const char *x) {
197 _cleanup_close_ int src = -1;
198 _cleanup_fclose_ FILE *dst = NULL;
200 struct timespec ts[2];
204 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
206 if (errno == ENOENT) /* No backup necessary... */
212 if (fstat(src, &st) < 0)
215 r = fopen_temporary_label(target, x, &dst, &temp);
219 r = copy_bytes(src, fileno(dst), (off_t) -1, true);
223 /* Don't fail on chmod() or chown(). If it stays owned by us
224 * and/or unreadable by others, then it isn't too bad... */
226 backup = strappenda(x, "-");
228 /* Copy over the access mask */
229 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
230 log_warning_errno(errno, "Failed to change mode on %s: %m", backup);
232 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
233 log_warning_errno(errno, "Failed to change ownership of %s: %m", backup);
237 if (futimens(fileno(dst), ts) < 0)
238 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
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 putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
306 a = hashmap_get(members, sg->sg_namp);
308 _cleanup_strv_free_ char **l = NULL;
312 l = strv_copy(sg->sg_mem);
317 if (strv_find(l, *i))
320 if (strv_extend(&l, *i) < 0)
336 if (putsgent(&t, gshadow) != 0)
337 return errno ? -errno : -EIO;
344 if (putsgent(sg, gshadow) != 0)
345 return errno ? -errno : -EIO;
350 static int sync_rights(FILE *from, FILE *to) {
353 if (fstat(fileno(from), &st) < 0)
356 if (fchmod(fileno(to), st.st_mode & 07777) < 0)
359 if (fchown(fileno(to), st.st_uid, st.st_gid) < 0)
365 static int write_files(void) {
367 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
368 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
369 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
370 bool group_changed = false;
375 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
376 _cleanup_fclose_ FILE *original = NULL;
378 /* First we update the actual group list file */
379 group_path = fix_root("/etc/group");
380 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
384 original = fopen(group_path, "re");
388 r = sync_rights(original, group);
393 while ((gr = fgetgrent(original))) {
394 /* Safety checks against name and GID
395 * collisions. Normally, this should
396 * be unnecessary, but given that we
397 * look at the entries anyway here,
398 * let's make an extra verification
399 * step that we don't generate
400 * duplicate entries. */
402 i = hashmap_get(groups, gr->gr_name);
403 if (i && i->todo_group) {
408 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
413 r = putgrent_with_members(gr, group);
417 group_changed = true;
421 if (!IN_SET(errno, 0, ENOENT)) {
426 } else if (errno != ENOENT) {
429 } else if (fchmod(fileno(group), 0644) < 0) {
434 HASHMAP_FOREACH(i, todo_gids, iterator) {
438 .gr_passwd = (char*) "x",
441 r = putgrent_with_members(&n, group);
445 group_changed = true;
448 r = fflush_and_check(group);
457 /* OK, now also update the shadow file for the group list */
458 gshadow_path = fix_root("/etc/gshadow");
459 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
463 original = fopen(gshadow_path, "re");
467 r = sync_rights(original, gshadow);
472 while ((sg = fgetsgent(original))) {
474 i = hashmap_get(groups, sg->sg_namp);
475 if (i && i->todo_group) {
480 r = putsgent_with_members(sg, gshadow);
484 group_changed = true;
488 if (!IN_SET(errno, 0, ENOENT)) {
493 } else if (errno != ENOENT) {
496 } else if (fchmod(fileno(gshadow), 0000) < 0) {
501 HASHMAP_FOREACH(i, todo_gids, iterator) {
504 .sg_passwd = (char*) "!!",
507 r = putsgent_with_members(&n, gshadow);
511 group_changed = true;
514 r = fflush_and_check(gshadow);
519 if (hashmap_size(todo_uids) > 0) {
520 _cleanup_fclose_ FILE *original = NULL;
523 /* First we update the user database itself */
524 passwd_path = fix_root("/etc/passwd");
525 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
529 original = fopen(passwd_path, "re");
533 r = sync_rights(original, passwd);
538 while ((pw = fgetpwent(original))) {
540 i = hashmap_get(users, pw->pw_name);
541 if (i && i->todo_user) {
546 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
552 if (putpwent(pw, passwd) < 0) {
553 r = errno ? -errno : -EIO;
559 if (!IN_SET(errno, 0, ENOENT)) {
564 } else if (errno != ENOENT) {
567 } else if (fchmod(fileno(passwd), 0644) < 0) {
572 HASHMAP_FOREACH(i, todo_uids, iterator) {
577 .pw_gecos = i->description,
579 /* "x" means the password is stored in
581 .pw_passwd = (char*) "x",
583 /* We default to the root directory as home */
584 .pw_dir = i->home ? i->home : (char*) "/",
586 /* Initialize the shell to nologin,
587 * with one exception: for root we
588 * patch in something special */
589 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
593 if (putpwent(&n, passwd) != 0) {
594 r = errno ? -errno : -EIO;
599 r = fflush_and_check(passwd);
608 /* The we update the shadow database */
609 shadow_path = fix_root("/etc/shadow");
610 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
614 original = fopen(shadow_path, "re");
618 r = sync_rights(original, shadow);
623 while ((sp = fgetspent(original))) {
625 i = hashmap_get(users, sp->sp_namp);
626 if (i && i->todo_user) {
632 if (putspent(sp, shadow) < 0) {
633 r = errno ? -errno : -EIO;
639 if (!IN_SET(errno, 0, ENOENT)) {
643 } else if (errno != ENOENT) {
646 } else if (fchmod(fileno(shadow), 0000) < 0) {
651 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
652 HASHMAP_FOREACH(i, todo_uids, iterator) {
655 .sp_pwdp = (char*) "!!",
662 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
666 if (putspent(&n, shadow) != 0) {
667 r = errno ? -errno : -EIO;
672 r = fflush_and_check(shadow);
677 /* Make a backup of the old files */
680 r = make_backup("/etc/group", group_path);
685 r = make_backup("/etc/gshadow", gshadow_path);
692 r = make_backup("/etc/passwd", passwd_path);
697 r = make_backup("/etc/shadow", shadow_path);
702 /* And make the new files count */
705 if (rename(group_tmp, group_path) < 0) {
714 if (rename(gshadow_tmp, gshadow_path) < 0) {
725 if (rename(passwd_tmp, passwd_path) < 0) {
734 if (rename(shadow_tmp, shadow_path) < 0) {
758 static int uid_is_ok(uid_t uid, const char *name) {
764 /* Let's see if we already have assigned the UID a second time */
765 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
768 /* Try to avoid using uids that are already used by a group
769 * that doesn't have the same name as our new user. */
770 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
771 if (i && !streq(i->name, name))
774 /* Let's check the files directly */
775 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
778 n = hashmap_get(database_gid, GID_TO_PTR(uid));
779 if (n && !streq(n, name))
782 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
788 if (!IN_SET(errno, 0, ENOENT))
792 g = getgrgid((gid_t) uid);
794 if (!streq(g->gr_name, name))
796 } else if (!IN_SET(errno, 0, ENOENT))
803 static int root_stat(const char *p, struct stat *st) {
807 if (stat(fix, st) < 0)
813 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
815 bool found_uid = false, found_gid = false;
821 /* First, try to get the gid directly */
822 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
827 /* Then, try to get the uid directly */
828 if ((_uid || (_gid && !found_gid))
830 && root_stat(i->uid_path, &st) >= 0) {
835 /* If we need the gid, but had no success yet, also derive it from the uid path */
836 if (_gid && !found_gid) {
842 /* If that didn't work yet, then let's reuse the gid as uid */
843 if (_uid && !found_uid && i->gid_path) {
848 } else if (root_stat(i->gid_path, &st) >= 0) {
849 uid = (uid_t) st.st_gid;
871 static int add_user(Item *i) {
877 /* Check the database directly */
878 z = hashmap_get(database_user, i->name);
880 log_debug("User %s already exists.", i->name);
881 i->uid = PTR_TO_UID(z);
892 p = getpwnam(i->name);
894 log_debug("User %s already exists.", i->name);
898 free(i->description);
899 i->description = strdup(p->pw_gecos);
902 if (!IN_SET(errno, 0, ENOENT))
903 return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
905 /* And shadow too, just to be sure */
907 sp = getspnam(i->name);
909 log_error("User %s already exists in shadow database, but not in user database.", i->name);
912 if (!IN_SET(errno, 0, ENOENT))
913 return log_error_errno(errno, "Failed to check if user %s already exists in shadow database: %m", i->name);
916 /* Try to use the suggested numeric uid */
918 r = uid_is_ok(i->uid, i->name);
920 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
922 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
927 /* If that didn't work, try to read it from the specified path */
931 if (read_id_from_file(i, &c, NULL) > 0) {
933 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
934 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
936 r = uid_is_ok(c, i->name);
938 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
943 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
948 /* Otherwise try to reuse the group ID */
949 if (!i->uid_set && i->gid_set) {
950 r = uid_is_ok((uid_t) i->gid, i->name);
952 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
954 i->uid = (uid_t) i->gid;
959 /* And if that didn't work either, let's try to find a free one */
962 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
964 log_error("No free user ID available for %s.", i->name);
968 r = uid_is_ok(search_uid, i->name);
970 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
979 r = hashmap_ensure_allocated(&todo_uids, NULL);
983 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
988 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
993 static int gid_is_ok(gid_t gid) {
997 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
1000 /* Avoid reusing gids that are already used by a different user */
1001 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
1004 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1007 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1015 if (!IN_SET(errno, 0, ENOENT))
1019 p = getpwuid((uid_t) gid);
1022 if (!IN_SET(errno, 0, ENOENT))
1029 static int add_group(Item *i) {
1035 /* Check the database directly */
1036 z = hashmap_get(database_group, i->name);
1038 log_debug("Group %s already exists.", i->name);
1039 i->gid = PTR_TO_GID(z);
1044 /* Also check NSS */
1049 g = getgrnam(i->name);
1051 log_debug("Group %s already exists.", i->name);
1056 if (!IN_SET(errno, 0, ENOENT))
1057 return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
1060 /* Try to use the suggested numeric gid */
1062 r = gid_is_ok(i->gid);
1064 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1066 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1071 /* Try to reuse the numeric uid, if there's one */
1072 if (!i->gid_set && i->uid_set) {
1073 r = gid_is_ok((gid_t) i->uid);
1075 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1077 i->gid = (gid_t) i->uid;
1082 /* If that didn't work, try to read it from the specified path */
1086 if (read_id_from_file(i, NULL, &c) > 0) {
1088 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1089 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1093 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
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 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1121 i->gid = search_uid;
1124 r = hashmap_ensure_allocated(&todo_gids, NULL);
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_ops);
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_ops);
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, *resolved_id = 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, ADD_RANGE)) {
1421 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1426 if (isempty(name) || streq(name, "-")) {
1432 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1434 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1438 if (!valid_user_group_name(resolved_name)) {
1439 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1445 if (isempty(id) || streq(id, "-")) {
1451 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1453 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1458 /* Verify description */
1459 if (isempty(description) || streq(description, "-")) {
1465 if (!valid_gecos(description)) {
1466 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1472 if (isempty(home) || streq(home, "-")) {
1478 if (!valid_home(home)) {
1479 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1484 switch (action[0]) {
1487 if (resolved_name) {
1488 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1493 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1498 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1503 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1507 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1509 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1518 /* Try to extend an existing member or group item */
1520 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1525 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1529 if (!valid_user_group_name(resolved_id)) {
1530 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1535 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1540 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1544 r = hashmap_ensure_allocated(&members, &string_hash_ops);
1548 l = hashmap_get(members, resolved_id);
1550 /* A list for this group name already exists, let's append to it */
1551 r = strv_push(&l, resolved_name);
1555 resolved_name = NULL;
1557 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1559 /* No list for this group name exists yet, create one */
1561 l = new0(char *, 2);
1565 l[0] = resolved_name;
1568 r = hashmap_put(members, resolved_id, l);
1574 resolved_id = resolved_name = NULL;
1582 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1586 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1595 if (path_is_absolute(resolved_id)) {
1596 i->uid_path = resolved_id;
1599 path_kill_slashes(i->uid_path);
1601 r = parse_uid(resolved_id, &i->uid);
1603 log_error("Failed to parse UID: %s", id);
1611 i->description = description;
1622 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1627 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1632 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1636 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1645 if (path_is_absolute(resolved_id)) {
1646 i->gid_path = resolved_id;
1649 path_kill_slashes(i->gid_path);
1651 r = parse_gid(resolved_id, &i->gid);
1653 log_error("Failed to parse GID: %s", id);
1668 i->type = action[0];
1669 i->name = resolved_name;
1670 resolved_name = NULL;
1672 existing = hashmap_get(h, i->name);
1675 /* Two identical items are fine */
1676 if (!item_equal(existing, i))
1677 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1682 r = hashmap_put(h, i->name, i);
1690 static int read_config_file(const char *fn, bool ignore_enoent) {
1691 _cleanup_fclose_ FILE *rf = NULL;
1693 char line[LINE_MAX];
1702 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1704 if (ignore_enoent && r == -ENOENT)
1707 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1713 FOREACH_LINE(line, f, break) {
1720 if (*l == '#' || *l == 0)
1723 k = parse_line(fn, v, l);
1724 if (k < 0 && r == 0)
1729 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1737 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1741 name = hashmap_first(by_id);
1745 hashmap_remove(by_name, name);
1747 hashmap_steal_first_key(by_id);
1751 while ((name = hashmap_steal_first_key(by_name)))
1754 hashmap_free(by_name);
1755 hashmap_free(by_id);
1758 static void help(void) {
1759 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1760 "Creates system user accounts.\n\n"
1761 " -h --help Show this help\n"
1762 " --version Show package version\n"
1763 " --root=PATH Operate on an alternate filesystem root\n"
1764 , program_invocation_short_name);
1767 static int parse_argv(int argc, char *argv[]) {
1770 ARG_VERSION = 0x100,
1774 static const struct option options[] = {
1775 { "help", no_argument, NULL, 'h' },
1776 { "version", no_argument, NULL, ARG_VERSION },
1777 { "root", required_argument, NULL, ARG_ROOT },
1786 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1795 puts(PACKAGE_STRING);
1796 puts(SYSTEMD_FEATURES);
1801 arg_root = path_make_absolute_cwd(optarg);
1805 path_kill_slashes(arg_root);
1812 assert_not_reached("Unhandled option");
1818 int main(int argc, char *argv[]) {
1820 _cleanup_close_ int lock = -1;
1826 r = parse_argv(argc, argv);
1830 log_set_target(LOG_TARGET_AUTO);
1831 log_parse_environment();
1836 r = mac_selinux_init(NULL);
1838 log_error_errno(r, "SELinux setup failed: %m");
1842 if (optind < argc) {
1845 for (j = optind; j < argc; j++) {
1846 k = read_config_file(argv[j], false);
1847 if (k < 0 && r == 0)
1851 _cleanup_strv_free_ char **files = NULL;
1854 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1856 log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
1860 STRV_FOREACH(f, files) {
1861 k = read_config_file(*f, true);
1862 if (k < 0 && r == 0)
1868 /* Default to default range of 1..SYSTEMD_UID_MAX */
1869 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1880 lock = take_password_lock(arg_root);
1882 log_error_errno(lock, "Failed to take lock: %m");
1886 r = load_user_database();
1888 log_error_errno(r, "Failed to load user database: %m");
1892 r = load_group_database();
1894 log_error_errno(r, "Failed to read group database: %m");
1898 HASHMAP_FOREACH(i, groups, iterator)
1901 HASHMAP_FOREACH(i, users, iterator)
1906 log_error_errno(r, "Failed to write files: %m");
1909 while ((i = hashmap_steal_first(groups)))
1912 while ((i = hashmap_steal_first(users)))
1915 while ((n = hashmap_first_key(members))) {
1916 strv_free(hashmap_steal_first(members));
1920 hashmap_free(groups);
1921 hashmap_free(users);
1922 hashmap_free(members);
1923 hashmap_free(todo_uids);
1924 hashmap_free(todo_gids);
1926 free_database(database_user, database_uid);
1927 free_database(database_group, database_gid);
1931 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;