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_ops);
114 r = hashmap_ensure_allocated(&database_uid, NULL);
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_ops);
166 r = hashmap_ensure_allocated(&database_gid, NULL);
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 if (futimens(fileno(dst), ts) < 0)
245 log_warning("Failed to fix access and modification time of %s: %m", backup);
247 if (rename(temp, backup) < 0)
257 static int putgrent_with_members(const struct group *gr, FILE *group) {
263 a = hashmap_get(members, gr->gr_name);
265 _cleanup_strv_free_ char **l = NULL;
269 l = strv_copy(gr->gr_mem);
274 if (strv_find(l, *i))
277 if (strv_extend(&l, *i) < 0)
293 if (putgrent(&t, group) != 0)
294 return errno ? -errno : -EIO;
301 if (putgrent(gr, group) != 0)
302 return errno ? -errno : -EIO;
307 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
313 a = hashmap_get(members, sg->sg_namp);
315 _cleanup_strv_free_ char **l = NULL;
319 l = strv_copy(sg->sg_mem);
324 if (strv_find(l, *i))
327 if (strv_extend(&l, *i) < 0)
343 if (putsgent(&t, gshadow) != 0)
344 return errno ? -errno : -EIO;
351 if (putsgent(sg, gshadow) != 0)
352 return errno ? -errno : -EIO;
357 static int sync_rights(FILE *from, FILE *to) {
360 if (fstat(fileno(from), &st) < 0)
363 if (fchmod(fileno(to), st.st_mode & 07777) < 0)
366 if (fchown(fileno(to), st.st_uid, st.st_gid) < 0)
372 static int write_files(void) {
374 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
375 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
376 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
377 bool group_changed = false;
382 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
383 _cleanup_fclose_ FILE *original = NULL;
385 /* First we update the actual group list file */
386 group_path = fix_root("/etc/group");
387 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
391 original = fopen(group_path, "re");
395 r = sync_rights(original, group);
400 while ((gr = fgetgrent(original))) {
401 /* Safety checks against name and GID
402 * collisions. Normally, this should
403 * be unnecessary, but given that we
404 * look at the entries anyway here,
405 * let's make an extra verification
406 * step that we don't generate
407 * duplicate entries. */
409 i = hashmap_get(groups, gr->gr_name);
410 if (i && i->todo_group) {
415 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
420 r = putgrent_with_members(gr, group);
424 group_changed = true;
428 if (!IN_SET(errno, 0, ENOENT)) {
433 } else if (errno != ENOENT) {
436 } else if (fchmod(fileno(group), 0644) < 0) {
441 HASHMAP_FOREACH(i, todo_gids, iterator) {
445 .gr_passwd = (char*) "x",
448 r = putgrent_with_members(&n, group);
452 group_changed = true;
455 r = fflush_and_check(group);
464 /* OK, now also update the shadow file for the group list */
465 gshadow_path = fix_root("/etc/gshadow");
466 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
470 original = fopen(gshadow_path, "re");
474 r = sync_rights(original, gshadow);
479 while ((sg = fgetsgent(original))) {
481 i = hashmap_get(groups, sg->sg_namp);
482 if (i && i->todo_group) {
487 r = putsgent_with_members(sg, gshadow);
491 group_changed = true;
495 if (!IN_SET(errno, 0, ENOENT)) {
500 } else if (errno != ENOENT) {
503 } else if (fchmod(fileno(gshadow), 0000) < 0) {
508 HASHMAP_FOREACH(i, todo_gids, iterator) {
511 .sg_passwd = (char*) "!!",
514 r = putsgent_with_members(&n, gshadow);
518 group_changed = true;
521 r = fflush_and_check(gshadow);
526 if (hashmap_size(todo_uids) > 0) {
527 _cleanup_fclose_ FILE *original = NULL;
530 /* First we update the user database itself */
531 passwd_path = fix_root("/etc/passwd");
532 r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
536 original = fopen(passwd_path, "re");
540 r = sync_rights(original, passwd);
545 while ((pw = fgetpwent(original))) {
547 i = hashmap_get(users, pw->pw_name);
548 if (i && i->todo_user) {
553 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
559 if (putpwent(pw, passwd) < 0) {
560 r = errno ? -errno : -EIO;
566 if (!IN_SET(errno, 0, ENOENT)) {
571 } else if (errno != ENOENT) {
574 } else if (fchmod(fileno(passwd), 0644) < 0) {
579 HASHMAP_FOREACH(i, todo_uids, iterator) {
584 .pw_gecos = i->description,
586 /* "x" means the password is stored in
588 .pw_passwd = (char*) "x",
590 /* We default to the root directory as home */
591 .pw_dir = i->home ? i->home : (char*) "/",
593 /* Initialize the shell to nologin,
594 * with one exception: for root we
595 * patch in something special */
596 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
600 if (putpwent(&n, passwd) != 0) {
601 r = errno ? -errno : -EIO;
606 r = fflush_and_check(passwd);
615 /* The we update the shadow database */
616 shadow_path = fix_root("/etc/shadow");
617 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
621 original = fopen(shadow_path, "re");
625 r = sync_rights(original, shadow);
630 while ((sp = fgetspent(original))) {
632 i = hashmap_get(users, sp->sp_namp);
633 if (i && i->todo_user) {
639 if (putspent(sp, shadow) < 0) {
640 r = errno ? -errno : -EIO;
646 if (!IN_SET(errno, 0, ENOENT)) {
650 } else if (errno != ENOENT) {
653 } else if (fchmod(fileno(shadow), 0000) < 0) {
658 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
659 HASHMAP_FOREACH(i, todo_uids, iterator) {
662 .sp_pwdp = (char*) "!!",
669 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
673 if (putspent(&n, shadow) != 0) {
674 r = errno ? -errno : -EIO;
679 r = fflush_and_check(shadow);
684 /* Make a backup of the old files */
687 r = make_backup("/etc/group", group_path);
692 r = make_backup("/etc/gshadow", gshadow_path);
699 r = make_backup("/etc/passwd", passwd_path);
704 r = make_backup("/etc/shadow", shadow_path);
709 /* And make the new files count */
712 if (rename(group_tmp, group_path) < 0) {
721 if (rename(gshadow_tmp, gshadow_path) < 0) {
732 if (rename(passwd_tmp, passwd_path) < 0) {
741 if (rename(shadow_tmp, shadow_path) < 0) {
765 static int uid_is_ok(uid_t uid, const char *name) {
771 /* Let's see if we already have assigned the UID a second time */
772 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
775 /* Try to avoid using uids that are already used by a group
776 * that doesn't have the same name as our new user. */
777 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
778 if (i && !streq(i->name, name))
781 /* Let's check the files directly */
782 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
785 n = hashmap_get(database_gid, GID_TO_PTR(uid));
786 if (n && !streq(n, name))
789 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
795 if (!IN_SET(errno, 0, ENOENT))
799 g = getgrgid((gid_t) uid);
801 if (!streq(g->gr_name, name))
803 } else if (!IN_SET(errno, 0, ENOENT))
810 static int root_stat(const char *p, struct stat *st) {
814 if (stat(fix, st) < 0)
820 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
822 bool found_uid = false, found_gid = false;
828 /* First, try to get the gid directly */
829 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
834 /* Then, try to get the uid directly */
835 if ((_uid || (_gid && !found_gid))
837 && root_stat(i->uid_path, &st) >= 0) {
842 /* If we need the gid, but had no success yet, also derive it from the uid path */
843 if (_gid && !found_gid) {
849 /* If that didn't work yet, then let's reuse the gid as uid */
850 if (_uid && !found_uid && i->gid_path) {
855 } else if (root_stat(i->gid_path, &st) >= 0) {
856 uid = (uid_t) st.st_gid;
878 static int add_user(Item *i) {
884 /* Check the database directly */
885 z = hashmap_get(database_user, i->name);
887 log_debug("User %s already exists.", i->name);
888 i->uid = PTR_TO_UID(z);
899 p = getpwnam(i->name);
901 log_debug("User %s already exists.", i->name);
905 free(i->description);
906 i->description = strdup(p->pw_gecos);
909 if (!IN_SET(errno, 0, ENOENT)) {
910 log_error("Failed to check if user %s already exists: %m", i->name);
914 /* And shadow too, just to be sure */
916 sp = getspnam(i->name);
918 log_error("User %s already exists in shadow database, but not in user database.", i->name);
921 if (!IN_SET(errno, 0, ENOENT)) {
922 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
927 /* Try to use the suggested numeric uid */
929 r = uid_is_ok(i->uid, i->name);
931 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
935 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
940 /* If that didn't work, try to read it from the specified path */
944 if (read_id_from_file(i, &c, NULL) > 0) {
946 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
947 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
949 r = uid_is_ok(c, i->name);
951 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
957 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
962 /* Otherwise try to reuse the group ID */
963 if (!i->uid_set && i->gid_set) {
964 r = uid_is_ok((uid_t) i->gid, i->name);
966 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
970 i->uid = (uid_t) i->gid;
975 /* And if that didn't work either, let's try to find a free one */
978 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
980 log_error("No free user ID available for %s.", i->name);
984 r = uid_is_ok(search_uid, i->name);
986 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
996 r = hashmap_ensure_allocated(&todo_uids, NULL);
1000 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
1004 i->todo_user = true;
1005 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
1010 static int gid_is_ok(gid_t gid) {
1014 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
1017 /* Avoid reusing gids that are already used by a different user */
1018 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
1021 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1024 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1032 if (!IN_SET(errno, 0, ENOENT))
1036 p = getpwuid((uid_t) gid);
1039 if (!IN_SET(errno, 0, ENOENT))
1046 static int add_group(Item *i) {
1052 /* Check the database directly */
1053 z = hashmap_get(database_group, i->name);
1055 log_debug("Group %s already exists.", i->name);
1056 i->gid = PTR_TO_GID(z);
1061 /* Also check NSS */
1066 g = getgrnam(i->name);
1068 log_debug("Group %s already exists.", i->name);
1073 if (!IN_SET(errno, 0, ENOENT)) {
1074 log_error("Failed to check if group %s already exists: %m", i->name);
1079 /* Try to use the suggested numeric gid */
1081 r = gid_is_ok(i->gid);
1083 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1087 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1092 /* Try to reuse the numeric uid, if there's one */
1093 if (!i->gid_set && i->uid_set) {
1094 r = gid_is_ok((gid_t) i->uid);
1096 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1100 i->gid = (gid_t) i->uid;
1105 /* If that didn't work, try to read it from the specified path */
1109 if (read_id_from_file(i, NULL, &c) > 0) {
1111 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1112 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1116 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1122 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1127 /* And if that didn't work either, let's try to find a free one */
1130 /* We look for new GIDs in the UID pool! */
1131 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1133 log_error("No free group ID available for %s.", i->name);
1137 r = gid_is_ok(search_uid);
1139 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1146 i->gid = search_uid;
1149 r = hashmap_ensure_allocated(&todo_gids, NULL);
1153 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1157 i->todo_group = true;
1158 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1163 static int process_item(Item *i) {
1180 j = hashmap_get(users, i->name);
1182 /* There's already user to be created for this
1183 * name, let's process that in one step */
1192 j->gid_path = strdup(i->gid_path);
1200 return add_group(i);
1204 assert_not_reached("Unknown item type");
1208 static void item_free(Item *i) {
1216 free(i->description);
1220 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1222 static int add_implicit(void) {
1227 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1229 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1233 i = hashmap_get(groups, g);
1235 _cleanup_(item_freep) Item *j = NULL;
1237 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1245 j->type = ADD_GROUP;
1246 j->name = strdup(g);
1250 r = hashmap_put(groups, j->name, j);
1254 log_debug("Adding implicit group '%s' due to m line", j->name);
1258 STRV_FOREACH(m, l) {
1260 i = hashmap_get(users, *m);
1262 _cleanup_(item_freep) Item *j = NULL;
1264 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1273 j->name = strdup(*m);
1277 r = hashmap_put(users, j->name, j);
1281 log_debug("Adding implicit user '%s' due to m line", j->name);
1290 static bool item_equal(Item *a, Item *b) {
1294 if (a->type != b->type)
1297 if (!streq_ptr(a->name, b->name))
1300 if (!streq_ptr(a->uid_path, b->uid_path))
1303 if (!streq_ptr(a->gid_path, b->gid_path))
1306 if (!streq_ptr(a->description, b->description))
1309 if (a->uid_set != b->uid_set)
1312 if (a->uid_set && a->uid != b->uid)
1315 if (a->gid_set != b->gid_set)
1318 if (a->gid_set && a->gid != b->gid)
1321 if (!streq_ptr(a->home, b->home))
1327 static bool valid_user_group_name(const char *u) {
1334 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1335 !(u[0] >= 'A' && u[0] <= 'Z') &&
1339 for (i = u+1; *i; i++) {
1340 if (!(*i >= 'a' && *i <= 'z') &&
1341 !(*i >= 'A' && *i <= 'Z') &&
1342 !(*i >= '0' && *i <= '9') &&
1348 sz = sysconf(_SC_LOGIN_NAME_MAX);
1351 if ((size_t) (i-u) > (size_t) sz)
1354 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1360 static bool valid_gecos(const char *d) {
1365 if (!utf8_is_valid(d))
1368 if (string_has_cc(d, NULL))
1371 /* Colons are used as field separators, and hence not OK */
1378 static bool valid_home(const char *p) {
1383 if (!utf8_is_valid(p))
1386 if (string_has_cc(p, NULL))
1389 if (!path_is_absolute(p))
1392 if (!path_is_safe(p))
1395 /* Colons are used as field separators, and hence not OK */
1402 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1404 static const Specifier specifier_table[] = {
1405 { 'm', specifier_machine_id, NULL },
1406 { 'b', specifier_boot_id, NULL },
1407 { 'H', specifier_host_name, NULL },
1408 { 'v', specifier_kernel_release, NULL },
1412 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
1413 _cleanup_(item_freep) Item *i = NULL;
1425 r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
1427 log_error("[%s:%u] Syntax error.", fname, line);
1431 log_error("[%s:%u] Missing action and name columns.", fname, line);
1435 log_error("[%s:%u] Trailing garbage.", fname, line);
1440 if (strlen(action) != 1) {
1441 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1445 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1446 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1451 if (isempty(name) || streq(name, "-")) {
1457 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1459 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1463 if (!valid_user_group_name(resolved_name)) {
1464 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1470 if (isempty(id) || streq(id, "-")) {
1476 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1478 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1483 /* Verify description */
1484 if (isempty(description) || streq(description, "-")) {
1490 if (!valid_gecos(description)) {
1491 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1497 if (isempty(home) || streq(home, "-")) {
1503 if (!valid_home(home)) {
1504 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1509 switch (action[0]) {
1512 if (resolved_name) {
1513 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1518 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1523 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1528 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1532 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1534 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1543 /* Try to extend an existing member or group item */
1545 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1550 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1554 if (!valid_user_group_name(resolved_id)) {
1555 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1560 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1565 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1569 r = hashmap_ensure_allocated(&members, &string_hash_ops);
1573 l = hashmap_get(members, resolved_id);
1575 /* A list for this group name already exists, let's append to it */
1576 r = strv_push(&l, resolved_name);
1580 resolved_name = NULL;
1582 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1584 /* No list for this group name exists yet, create one */
1586 l = new0(char *, 2);
1590 l[0] = resolved_name;
1593 r = hashmap_put(members, resolved_id, l);
1599 resolved_id = resolved_name = NULL;
1607 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1611 r = hashmap_ensure_allocated(&users, &string_hash_ops);
1620 if (path_is_absolute(resolved_id)) {
1621 i->uid_path = resolved_id;
1624 path_kill_slashes(i->uid_path);
1626 r = parse_uid(resolved_id, &i->uid);
1628 log_error("Failed to parse UID: %s", id);
1636 i->description = description;
1647 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1652 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1657 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1661 r = hashmap_ensure_allocated(&groups, &string_hash_ops);
1670 if (path_is_absolute(resolved_id)) {
1671 i->gid_path = resolved_id;
1674 path_kill_slashes(i->gid_path);
1676 r = parse_gid(resolved_id, &i->gid);
1678 log_error("Failed to parse GID: %s", id);
1693 i->type = action[0];
1694 i->name = resolved_name;
1695 resolved_name = NULL;
1697 existing = hashmap_get(h, i->name);
1700 /* Two identical items are fine */
1701 if (!item_equal(existing, i))
1702 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1707 r = hashmap_put(h, i->name, i);
1715 static int read_config_file(const char *fn, bool ignore_enoent) {
1716 _cleanup_fclose_ FILE *rf = NULL;
1718 char line[LINE_MAX];
1727 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1729 if (ignore_enoent && r == -ENOENT)
1732 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1739 FOREACH_LINE(line, f, break) {
1746 if (*l == '#' || *l == 0)
1749 k = parse_line(fn, v, l);
1750 if (k < 0 && r == 0)
1755 log_error("Failed to read from file %s: %m", fn);
1763 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1767 name = hashmap_first(by_id);
1771 hashmap_remove(by_name, name);
1773 hashmap_steal_first_key(by_id);
1777 while ((name = hashmap_steal_first_key(by_name)))
1780 hashmap_free(by_name);
1781 hashmap_free(by_id);
1784 static void help(void) {
1785 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1786 "Creates system user accounts.\n\n"
1787 " -h --help Show this help\n"
1788 " --version Show package version\n"
1789 " --root=PATH Operate on an alternate filesystem root\n"
1790 , program_invocation_short_name);
1793 static int parse_argv(int argc, char *argv[]) {
1796 ARG_VERSION = 0x100,
1800 static const struct option options[] = {
1801 { "help", no_argument, NULL, 'h' },
1802 { "version", no_argument, NULL, ARG_VERSION },
1803 { "root", required_argument, NULL, ARG_ROOT },
1812 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1821 puts(PACKAGE_STRING);
1822 puts(SYSTEMD_FEATURES);
1827 arg_root = path_make_absolute_cwd(optarg);
1831 path_kill_slashes(arg_root);
1838 assert_not_reached("Unhandled option");
1844 int main(int argc, char *argv[]) {
1846 _cleanup_close_ int lock = -1;
1852 r = parse_argv(argc, argv);
1856 log_set_target(LOG_TARGET_AUTO);
1857 log_parse_environment();
1862 r = mac_selinux_init(NULL);
1864 log_error("SELinux setup failed: %s", strerror(-r));
1868 if (optind < argc) {
1871 for (j = optind; j < argc; j++) {
1872 k = read_config_file(argv[j], false);
1873 if (k < 0 && r == 0)
1877 _cleanup_strv_free_ char **files = NULL;
1880 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1882 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1886 STRV_FOREACH(f, files) {
1887 k = read_config_file(*f, true);
1888 if (k < 0 && r == 0)
1894 /* Default to default range of 1..SYSTEMD_UID_MAX */
1895 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1906 lock = take_password_lock(arg_root);
1908 log_error("Failed to take lock: %s", strerror(-lock));
1912 r = load_user_database();
1914 log_error("Failed to load user database: %s", strerror(-r));
1918 r = load_group_database();
1920 log_error("Failed to read group database: %s", strerror(-r));
1924 HASHMAP_FOREACH(i, groups, iterator)
1927 HASHMAP_FOREACH(i, users, iterator)
1932 log_error("Failed to write files: %s", strerror(-r));
1935 while ((i = hashmap_steal_first(groups)))
1938 while ((i = hashmap_steal_first(users)))
1941 while ((n = hashmap_first_key(members))) {
1942 strv_free(hashmap_steal_first(members));
1946 hashmap_free(groups);
1947 hashmap_free(users);
1948 hashmap_free(members);
1949 hashmap_free(todo_uids);
1950 hashmap_free(todo_gids);
1952 free_database(database_user, database_uid);
1953 free_database(database_group, database_gid);
1957 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;