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[] = CONF_DIRS_NULSTR("sysusers");
72 static Hashmap *users = NULL, *groups = NULL;
73 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
74 static Hashmap *members = NULL;
76 static Hashmap *database_uid = NULL, *database_user = NULL;
77 static Hashmap *database_gid = NULL, *database_group = NULL;
79 static uid_t search_uid = (uid_t) -1;
80 static UidRange *uid_range = NULL;
81 static unsigned n_uid_range = 0;
83 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
84 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
86 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
87 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
89 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
91 static int load_user_database(void) {
92 _cleanup_fclose_ FILE *f = NULL;
93 const char *passwd_path;
97 passwd_path = fix_root("/etc/passwd");
98 f = fopen(passwd_path, "re");
100 return errno == ENOENT ? 0 : -errno;
102 r = hashmap_ensure_allocated(&database_user, &string_hash_ops);
106 r = hashmap_ensure_allocated(&database_uid, NULL);
111 while ((pw = fgetpwent(f))) {
115 n = strdup(pw->pw_name);
119 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
120 if (k < 0 && k != -EEXIST) {
125 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
126 if (q < 0 && q != -EEXIST) {
137 if (!IN_SET(errno, 0, ENOENT))
143 static int load_group_database(void) {
144 _cleanup_fclose_ FILE *f = NULL;
145 const char *group_path;
149 group_path = fix_root("/etc/group");
150 f = fopen(group_path, "re");
152 return errno == ENOENT ? 0 : -errno;
154 r = hashmap_ensure_allocated(&database_group, &string_hash_ops);
158 r = hashmap_ensure_allocated(&database_gid, NULL);
163 while ((gr = fgetgrent(f))) {
167 n = strdup(gr->gr_name);
171 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
172 if (k < 0 && k != -EEXIST) {
177 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
178 if (q < 0 && q != -EEXIST) {
189 if (!IN_SET(errno, 0, ENOENT))
195 static int make_backup(const char *target, const char *x) {
196 _cleanup_close_ int src = -1;
197 _cleanup_fclose_ FILE *dst = NULL;
199 struct timespec ts[2];
203 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
205 if (errno == ENOENT) /* No backup necessary... */
211 if (fstat(src, &st) < 0)
214 r = fopen_temporary_label(target, x, &dst, &temp);
218 r = copy_bytes(src, fileno(dst), (off_t) -1);
222 /* Don't fail on chmod() or chown(). If it stays owned by us
223 * and/or unreadable by others, then it isn't too bad... */
225 backup = strappenda(x, "-");
227 /* Copy over the access mask */
228 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
229 log_warning_errno(errno, "Failed to change mode on %s: %m", backup);
231 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
232 log_warning_errno(errno, "Failed to change ownership of %s: %m", backup);
236 if (futimens(fileno(dst), ts) < 0)
237 log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup);
239 if (rename(temp, backup) < 0)
249 static int putgrent_with_members(const struct group *gr, FILE *group) {
255 a = hashmap_get(members, gr->gr_name);
257 _cleanup_strv_free_ char **l = NULL;
261 l = strv_copy(gr->gr_mem);
266 if (strv_find(l, *i))
269 if (strv_extend(&l, *i) < 0)
285 if (putgrent(&t, group) != 0)
286 return errno ? -errno : -EIO;
293 if (putgrent(gr, group) != 0)
294 return errno ? -errno : -EIO;
299 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
305 a = hashmap_get(members, sg->sg_namp);
307 _cleanup_strv_free_ char **l = NULL;
311 l = strv_copy(sg->sg_mem);
316 if (strv_find(l, *i))
319 if (strv_extend(&l, *i) < 0)
335 if (putsgent(&t, gshadow) != 0)
336 return errno ? -errno : -EIO;
343 if (putsgent(sg, gshadow) != 0)
344 return errno ? -errno : -EIO;
349 static int sync_rights(FILE *from, FILE *to) {
352 if (fstat(fileno(from), &st) < 0)
355 if (fchmod(fileno(to), st.st_mode & 07777) < 0)
358 if (fchown(fileno(to), st.st_uid, st.st_gid) < 0)
364 static int write_files(void) {
366 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
367 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
368 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
369 bool group_changed = false;
374 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
375 _cleanup_fclose_ FILE *original = NULL;
377 /* First we update the actual group list file */
378 group_path = fix_root("/etc/group");
379 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
383 original = fopen(group_path, "re");
387 r = sync_rights(original, group);
392 while ((gr = fgetgrent(original))) {
393 /* Safety checks against name and GID
394 * collisions. Normally, this should
395 * be unnecessary, but given that we
396 * look at the entries anyway here,
397 * let's make an extra verification
398 * step that we don't generate
399 * duplicate entries. */
401 i = hashmap_get(groups, gr->gr_name);
402 if (i && i->todo_group) {
407 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
412 r = putgrent_with_members(gr, group);
416 group_changed = true;
420 if (!IN_SET(errno, 0, ENOENT)) {
425 } else if (errno != ENOENT) {
428 } else if (fchmod(fileno(group), 0644) < 0) {
433 HASHMAP_FOREACH(i, todo_gids, iterator) {
437 .gr_passwd = (char*) "x",
440 r = putgrent_with_members(&n, group);
444 group_changed = true;
447 r = fflush_and_check(group);
456 /* OK, now also update the shadow file for the group list */
457 gshadow_path = fix_root("/etc/gshadow");
458 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
462 original = fopen(gshadow_path, "re");
466 r = sync_rights(original, gshadow);
471 while ((sg = fgetsgent(original))) {
473 i = hashmap_get(groups, sg->sg_namp);
474 if (i && i->todo_group) {
479 r = putsgent_with_members(sg, gshadow);
483 group_changed = true;
487 if (!IN_SET(errno, 0, ENOENT)) {
492 } else if (errno != ENOENT) {
495 } else if (fchmod(fileno(gshadow), 0000) < 0) {
500 HASHMAP_FOREACH(i, todo_gids, iterator) {
503 .sg_passwd = (char*) "!!",
506 r = putsgent_with_members(&n, gshadow);
510 group_changed = true;
513 r = fflush_and_check(gshadow);
518 if (hashmap_size(todo_uids) > 0) {
519 _cleanup_fclose_ FILE *original = NULL;
522 /* First we update the user database itself */
523 passwd_path = fix_root("/etc/passwd");
524 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
528 original = fopen(passwd_path, "re");
532 r = sync_rights(original, passwd);
537 while ((pw = fgetpwent(original))) {
539 i = hashmap_get(users, pw->pw_name);
540 if (i && i->todo_user) {
545 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
551 if (putpwent(pw, passwd) < 0) {
552 r = errno ? -errno : -EIO;
558 if (!IN_SET(errno, 0, ENOENT)) {
563 } else if (errno != ENOENT) {
566 } else if (fchmod(fileno(passwd), 0644) < 0) {
571 HASHMAP_FOREACH(i, todo_uids, iterator) {
576 .pw_gecos = i->description,
578 /* "x" means the password is stored in
580 .pw_passwd = (char*) "x",
582 /* We default to the root directory as home */
583 .pw_dir = i->home ? i->home : (char*) "/",
585 /* Initialize the shell to nologin,
586 * with one exception: for root we
587 * patch in something special */
588 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
592 if (putpwent(&n, passwd) != 0) {
593 r = errno ? -errno : -EIO;
598 r = fflush_and_check(passwd);
607 /* The we update the shadow database */
608 shadow_path = fix_root("/etc/shadow");
609 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
613 original = fopen(shadow_path, "re");
617 r = sync_rights(original, shadow);
622 while ((sp = fgetspent(original))) {
624 i = hashmap_get(users, sp->sp_namp);
625 if (i && i->todo_user) {
631 if (putspent(sp, shadow) < 0) {
632 r = errno ? -errno : -EIO;
638 if (!IN_SET(errno, 0, ENOENT)) {
642 } else if (errno != ENOENT) {
645 } else if (fchmod(fileno(shadow), 0000) < 0) {
650 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
651 HASHMAP_FOREACH(i, todo_uids, iterator) {
654 .sp_pwdp = (char*) "!!",
661 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
665 if (putspent(&n, shadow) != 0) {
666 r = errno ? -errno : -EIO;
671 r = fflush_and_check(shadow);
676 /* Make a backup of the old files */
679 r = make_backup("/etc/group", group_path);
684 r = make_backup("/etc/gshadow", gshadow_path);
691 r = make_backup("/etc/passwd", passwd_path);
696 r = make_backup("/etc/shadow", shadow_path);
701 /* And make the new files count */
704 if (rename(group_tmp, group_path) < 0) {
713 if (rename(gshadow_tmp, gshadow_path) < 0) {
724 if (rename(passwd_tmp, passwd_path) < 0) {
733 if (rename(shadow_tmp, shadow_path) < 0) {
757 static int uid_is_ok(uid_t uid, const char *name) {
763 /* Let's see if we already have assigned the UID a second time */
764 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
767 /* Try to avoid using uids that are already used by a group
768 * that doesn't have the same name as our new user. */
769 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
770 if (i && !streq(i->name, name))
773 /* Let's check the files directly */
774 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
777 n = hashmap_get(database_gid, GID_TO_PTR(uid));
778 if (n && !streq(n, name))
781 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
787 if (!IN_SET(errno, 0, ENOENT))
791 g = getgrgid((gid_t) uid);
793 if (!streq(g->gr_name, name))
795 } else if (!IN_SET(errno, 0, ENOENT))
802 static int root_stat(const char *p, struct stat *st) {
806 if (stat(fix, st) < 0)
812 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
814 bool found_uid = false, found_gid = false;
820 /* First, try to get the gid directly */
821 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
826 /* Then, try to get the uid directly */
827 if ((_uid || (_gid && !found_gid))
829 && root_stat(i->uid_path, &st) >= 0) {
834 /* If we need the gid, but had no success yet, also derive it from the uid path */
835 if (_gid && !found_gid) {
841 /* If that didn't work yet, then let's reuse the gid as uid */
842 if (_uid && !found_uid && i->gid_path) {
847 } else if (root_stat(i->gid_path, &st) >= 0) {
848 uid = (uid_t) st.st_gid;
870 static int add_user(Item *i) {
876 /* Check the database directly */
877 z = hashmap_get(database_user, i->name);
879 log_debug("User %s already exists.", i->name);
880 i->uid = PTR_TO_UID(z);
891 p = getpwnam(i->name);
893 log_debug("User %s already exists.", i->name);
897 free(i->description);
898 i->description = strdup(p->pw_gecos);
901 if (!IN_SET(errno, 0, ENOENT)) {
902 log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
906 /* And shadow too, just to be sure */
908 sp = getspnam(i->name);
910 log_error("User %s already exists in shadow database, but not in user database.", i->name);
913 if (!IN_SET(errno, 0, ENOENT)) {
914 log_error_errno(errno, "Failed to check if user %s already exists in shadow database: %m", i->name);
919 /* Try to use the suggested numeric uid */
921 r = uid_is_ok(i->uid, i->name);
923 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
925 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
930 /* If that didn't work, try to read it from the specified path */
934 if (read_id_from_file(i, &c, NULL) > 0) {
936 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
937 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
939 r = uid_is_ok(c, i->name);
941 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
946 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
951 /* Otherwise try to reuse the group ID */
952 if (!i->uid_set && i->gid_set) {
953 r = uid_is_ok((uid_t) i->gid, i->name);
955 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
957 i->uid = (uid_t) i->gid;
962 /* And if that didn't work either, let's try to find a free one */
965 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
967 log_error("No free user ID available for %s.", i->name);
971 r = uid_is_ok(search_uid, i->name);
973 return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid);
982 r = hashmap_ensure_allocated(&todo_uids, NULL);
986 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
991 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
996 static int gid_is_ok(gid_t gid) {
1000 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
1003 /* Avoid reusing gids that are already used by a different user */
1004 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
1007 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1010 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1018 if (!IN_SET(errno, 0, ENOENT))
1022 p = getpwuid((uid_t) gid);
1025 if (!IN_SET(errno, 0, ENOENT))
1032 static int add_group(Item *i) {
1038 /* Check the database directly */
1039 z = hashmap_get(database_group, i->name);
1041 log_debug("Group %s already exists.", i->name);
1042 i->gid = PTR_TO_GID(z);
1047 /* Also check NSS */
1052 g = getgrnam(i->name);
1054 log_debug("Group %s already exists.", i->name);
1059 if (!IN_SET(errno, 0, ENOENT)) {
1060 log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
1065 /* Try to use the suggested numeric gid */
1067 r = gid_is_ok(i->gid);
1069 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1071 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1076 /* Try to reuse the numeric uid, if there's one */
1077 if (!i->gid_set && i->uid_set) {
1078 r = gid_is_ok((gid_t) i->uid);
1080 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1082 i->gid = (gid_t) i->uid;
1087 /* If that didn't work, try to read it from the specified path */
1091 if (read_id_from_file(i, NULL, &c) > 0) {
1093 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1094 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1098 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1103 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1108 /* And if that didn't work either, let's try to find a free one */
1111 /* We look for new GIDs in the UID pool! */
1112 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1114 log_error("No free group ID available for %s.", i->name);
1118 r = gid_is_ok(search_uid);
1120 return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid);
1126 i->gid = search_uid;
1129 r = hashmap_ensure_allocated(&todo_gids, NULL);
1133 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1137 i->todo_group = true;
1138 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1143 static int process_item(Item *i) {
1160 j = hashmap_get(users, i->name);
1162 /* There's already user to be created for this
1163 * name, let's process that in one step */
1172 j->gid_path = strdup(i->gid_path);
1180 return add_group(i);
1184 assert_not_reached("Unknown item type");
1188 static void item_free(Item *i) {
1196 free(i->description);
1200 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1202 static int add_implicit(void) {
1207 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1209 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1213 i = hashmap_get(groups, g);
1215 _cleanup_(item_freep) Item *j = NULL;
1217 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1225 j->type = ADD_GROUP;
1226 j->name = strdup(g);
1230 r = hashmap_put(groups, j->name, j);
1234 log_debug("Adding implicit group '%s' due to m line", j->name);
1238 STRV_FOREACH(m, l) {
1240 i = hashmap_get(users, *m);
1242 _cleanup_(item_freep) Item *j = NULL;
1244 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1253 j->name = strdup(*m);
1257 r = hashmap_put(users, j->name, j);
1261 log_debug("Adding implicit user '%s' due to m line", j->name);
1270 static bool item_equal(Item *a, Item *b) {
1274 if (a->type != b->type)
1277 if (!streq_ptr(a->name, b->name))
1280 if (!streq_ptr(a->uid_path, b->uid_path))
1283 if (!streq_ptr(a->gid_path, b->gid_path))
1286 if (!streq_ptr(a->description, b->description))
1289 if (a->uid_set != b->uid_set)
1292 if (a->uid_set && a->uid != b->uid)
1295 if (a->gid_set != b->gid_set)
1298 if (a->gid_set && a->gid != b->gid)
1301 if (!streq_ptr(a->home, b->home))
1307 static bool valid_user_group_name(const char *u) {
1314 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1315 !(u[0] >= 'A' && u[0] <= 'Z') &&
1319 for (i = u+1; *i; i++) {
1320 if (!(*i >= 'a' && *i <= 'z') &&
1321 !(*i >= 'A' && *i <= 'Z') &&
1322 !(*i >= '0' && *i <= '9') &&
1328 sz = sysconf(_SC_LOGIN_NAME_MAX);
1331 if ((size_t) (i-u) > (size_t) sz)
1334 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1340 static bool valid_gecos(const char *d) {
1345 if (!utf8_is_valid(d))
1348 if (string_has_cc(d, NULL))
1351 /* Colons are used as field separators, and hence not OK */
1358 static bool valid_home(const char *p) {
1363 if (!utf8_is_valid(p))
1366 if (string_has_cc(p, NULL))
1369 if (!path_is_absolute(p))
1372 if (!path_is_safe(p))
1375 /* Colons are used as field separators, and hence not OK */
1382 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1384 static const Specifier specifier_table[] = {
1385 { 'm', specifier_machine_id, NULL },
1386 { 'b', specifier_boot_id, NULL },
1387 { 'H', specifier_host_name, NULL },
1388 { 'v', specifier_kernel_release, NULL },
1392 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
1393 _cleanup_(item_freep) Item *i = NULL;
1405 r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
1407 log_error("[%s:%u] Syntax error.", fname, line);
1411 log_error("[%s:%u] Missing action and name columns.", fname, line);
1415 log_error("[%s:%u] Trailing garbage.", fname, line);
1420 if (strlen(action) != 1) {
1421 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1425 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1426 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1431 if (isempty(name) || streq(name, "-")) {
1437 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1439 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1443 if (!valid_user_group_name(resolved_name)) {
1444 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1450 if (isempty(id) || streq(id, "-")) {
1456 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1458 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1463 /* Verify description */
1464 if (isempty(description) || streq(description, "-")) {
1470 if (!valid_gecos(description)) {
1471 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1477 if (isempty(home) || streq(home, "-")) {
1483 if (!valid_home(home)) {
1484 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1489 switch (action[0]) {
1492 if (resolved_name) {
1493 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1498 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1503 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1508 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1512 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1514 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1523 /* Try to extend an existing member or group item */
1525 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1530 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1534 if (!valid_user_group_name(resolved_id)) {
1535 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1540 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1545 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1549 r = hashmap_ensure_allocated(&members, &string_hash_ops);
1553 l = hashmap_get(members, resolved_id);
1555 /* A list for this group name already exists, let's append to it */
1556 r = strv_push(&l, resolved_name);
1560 resolved_name = NULL;
1562 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1564 /* No list for this group name exists yet, create one */
1566 l = new0(char *, 2);
1570 l[0] = resolved_name;
1573 r = hashmap_put(members, resolved_id, l);
1579 resolved_id = resolved_name = NULL;
1587 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1591 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1600 if (path_is_absolute(resolved_id)) {
1601 i->uid_path = resolved_id;
1604 path_kill_slashes(i->uid_path);
1606 r = parse_uid(resolved_id, &i->uid);
1608 log_error("Failed to parse UID: %s", id);
1616 i->description = description;
1627 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1632 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1637 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1641 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1650 if (path_is_absolute(resolved_id)) {
1651 i->gid_path = resolved_id;
1654 path_kill_slashes(i->gid_path);
1656 r = parse_gid(resolved_id, &i->gid);
1658 log_error("Failed to parse GID: %s", id);
1673 i->type = action[0];
1674 i->name = resolved_name;
1675 resolved_name = NULL;
1677 existing = hashmap_get(h, i->name);
1680 /* Two identical items are fine */
1681 if (!item_equal(existing, i))
1682 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1687 r = hashmap_put(h, i->name, i);
1695 static int read_config_file(const char *fn, bool ignore_enoent) {
1696 _cleanup_fclose_ FILE *rf = NULL;
1698 char line[LINE_MAX];
1707 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1709 if (ignore_enoent && r == -ENOENT)
1712 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1718 FOREACH_LINE(line, f, break) {
1725 if (*l == '#' || *l == 0)
1728 k = parse_line(fn, v, l);
1729 if (k < 0 && r == 0)
1734 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1742 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1746 name = hashmap_first(by_id);
1750 hashmap_remove(by_name, name);
1752 hashmap_steal_first_key(by_id);
1756 while ((name = hashmap_steal_first_key(by_name)))
1759 hashmap_free(by_name);
1760 hashmap_free(by_id);
1763 static void help(void) {
1764 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1765 "Creates system user accounts.\n\n"
1766 " -h --help Show this help\n"
1767 " --version Show package version\n"
1768 " --root=PATH Operate on an alternate filesystem root\n"
1769 , program_invocation_short_name);
1772 static int parse_argv(int argc, char *argv[]) {
1775 ARG_VERSION = 0x100,
1779 static const struct option options[] = {
1780 { "help", no_argument, NULL, 'h' },
1781 { "version", no_argument, NULL, ARG_VERSION },
1782 { "root", required_argument, NULL, ARG_ROOT },
1791 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1800 puts(PACKAGE_STRING);
1801 puts(SYSTEMD_FEATURES);
1806 arg_root = path_make_absolute_cwd(optarg);
1810 path_kill_slashes(arg_root);
1817 assert_not_reached("Unhandled option");
1823 int main(int argc, char *argv[]) {
1825 _cleanup_close_ int lock = -1;
1831 r = parse_argv(argc, argv);
1835 log_set_target(LOG_TARGET_AUTO);
1836 log_parse_environment();
1841 r = mac_selinux_init(NULL);
1843 log_error_errno(r, "SELinux setup failed: %m");
1847 if (optind < argc) {
1850 for (j = optind; j < argc; j++) {
1851 k = read_config_file(argv[j], false);
1852 if (k < 0 && r == 0)
1856 _cleanup_strv_free_ char **files = NULL;
1859 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1861 log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
1865 STRV_FOREACH(f, files) {
1866 k = read_config_file(*f, true);
1867 if (k < 0 && r == 0)
1873 /* Default to default range of 1..SYSTEMD_UID_MAX */
1874 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1885 lock = take_password_lock(arg_root);
1887 log_error_errno(lock, "Failed to take lock: %m");
1891 r = load_user_database();
1893 log_error_errno(r, "Failed to load user database: %m");
1897 r = load_group_database();
1899 log_error_errno(r, "Failed to read group database: %m");
1903 HASHMAP_FOREACH(i, groups, iterator)
1906 HASHMAP_FOREACH(i, users, iterator)
1911 log_error_errno(r, "Failed to write files: %m");
1914 while ((i = hashmap_steal_first(groups)))
1917 while ((i = hashmap_steal_first(users)))
1920 while ((n = hashmap_first_key(members))) {
1921 strv_free(hashmap_steal_first(members));
1925 hashmap_free(groups);
1926 hashmap_free(users);
1927 hashmap_free(members);
1928 hashmap_free(todo_uids);
1929 hashmap_free(todo_gids);
1931 free_database(database_user, database_uid);
1932 free_database(database_group, database_gid);
1936 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;