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 "smack-util.h"
44 typedef enum ItemType {
69 static char *arg_root = NULL;
71 static const char conf_file_dirs[] =
74 "/usr/local/lib/sysusers.d\0"
75 "/usr/lib/sysusers.d\0"
81 static Hashmap *users = NULL, *groups = NULL;
82 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
83 static Hashmap *members = NULL;
85 static Hashmap *database_uid = NULL, *database_user = NULL;
86 static Hashmap *database_gid = NULL, *database_group = NULL;
88 static uid_t search_uid = (uid_t) -1;
89 static UidRange *uid_range = NULL;
90 static unsigned n_uid_range = 0;
92 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
93 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
95 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
96 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
98 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
100 static int load_user_database(void) {
101 _cleanup_fclose_ FILE *f = NULL;
102 const char *passwd_path;
106 passwd_path = fix_root("/etc/passwd");
107 f = fopen(passwd_path, "re");
109 return errno == ENOENT ? 0 : -errno;
111 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
115 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
120 while ((pw = fgetpwent(f))) {
124 n = strdup(pw->pw_name);
128 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
129 if (k < 0 && k != -EEXIST) {
134 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
135 if (q < 0 && q != -EEXIST) {
146 if (!IN_SET(errno, 0, ENOENT))
152 static int load_group_database(void) {
153 _cleanup_fclose_ FILE *f = NULL;
154 const char *group_path;
158 group_path = fix_root("/etc/group");
159 f = fopen(group_path, "re");
161 return errno == ENOENT ? 0 : -errno;
163 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
167 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
172 while ((gr = fgetgrent(f))) {
176 n = strdup(gr->gr_name);
180 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
181 if (k < 0 && k != -EEXIST) {
186 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
187 if (q < 0 && q != -EEXIST) {
198 if (!IN_SET(errno, 0, ENOENT))
204 static int make_backup(const char *target, const char *x) {
205 _cleanup_close_ int src = -1;
206 _cleanup_fclose_ FILE *dst = NULL;
208 struct timespec ts[2];
212 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
214 if (errno == ENOENT) /* No backup necessary... */
220 if (fstat(src, &st) < 0)
223 r = fopen_temporary_label(target, x, &dst, &temp);
227 r = copy_bytes(src, fileno(dst), (off_t) -1);
231 /* Don't fail on chmod() or chown(). If it stays owned by us
232 * and/or unreadable by others, then it isn't too bad... */
234 backup = strappenda(x, "-");
236 /* Copy over the access mask */
237 if (fchmod(fileno(dst), st.st_mode & 07777) < 0)
238 log_warning("Failed to change mode on %s: %m", backup);
240 if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
241 log_warning("Failed to change ownership of %s: %m", backup);
245 futimens(fileno(dst), ts);
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 write_files(void) {
359 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
360 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
361 const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
362 _cleanup_free_ char *passwd_label = NULL, *group_label = NULL, *shadow_label = NULL, *gshadow_label = NULL;
363 bool group_changed = false;
368 if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
369 _cleanup_fclose_ FILE *original = NULL;
371 /* First we update the actual group list file */
372 group_path = fix_root("/etc/group");
373 r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
377 if (fchmod(fileno(group), 0644) < 0) {
382 original = fopen(group_path, "re");
386 r = smack_label_get_path(group_path, &group_label);
388 log_warning("Failed to get smack label from %s", group_path);
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) {
430 HASHMAP_FOREACH(i, todo_gids, iterator) {
434 .gr_passwd = (char*) "x",
437 r = putgrent_with_members(&n, group);
441 group_changed = true;
444 r = fflush_and_check(group);
453 /* OK, now also update the shadow file for the group list */
454 gshadow_path = fix_root("/etc/gshadow");
455 r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
459 if (fchmod(fileno(gshadow), 0000) < 0) {
464 original = fopen(gshadow_path, "re");
468 r = smack_label_get_path(gshadow_path, &gshadow_label);
470 log_warning("Failed to get smack label from %s", gshadow_path);
474 while ((sg = fgetsgent(original))) {
476 i = hashmap_get(groups, sg->sg_namp);
477 if (i && i->todo_group) {
482 r = putsgent_with_members(sg, gshadow);
486 group_changed = true;
490 if (!IN_SET(errno, 0, ENOENT)) {
495 } else if (errno != ENOENT) {
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 if (fchmod(fileno(passwd), 0644) < 0) {
533 original = fopen(passwd_path, "re");
537 r = smack_label_get_path(passwd_path, &passwd_label);
539 log_warning("Failed to get smack label from %s", passwd_path);
543 while ((pw = fgetpwent(original))) {
545 i = hashmap_get(users, pw->pw_name);
546 if (i && i->todo_user) {
551 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
557 if (putpwent(pw, passwd) < 0) {
558 r = errno ? -errno : -EIO;
564 if (!IN_SET(errno, 0, ENOENT)) {
569 } else if (errno != ENOENT) {
574 HASHMAP_FOREACH(i, todo_uids, iterator) {
579 .pw_gecos = i->description,
581 /* "x" means the password is stored in
583 .pw_passwd = (char*) "x",
585 /* We default to the root directory as home */
586 .pw_dir = i->home ? i->home : (char*) "/",
588 /* Initialize the shell to nologin,
589 * with one exception: for root we
590 * patch in something special */
591 .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
595 if (putpwent(&n, passwd) != 0) {
596 r = errno ? -errno : -EIO;
601 r = fflush_and_check(passwd);
610 /* The we update the shadow database */
611 shadow_path = fix_root("/etc/shadow");
612 r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
616 if (fchmod(fileno(shadow), 0000) < 0) {
621 original = fopen(shadow_path, "re");
625 r = smack_label_get_path(shadow_path, &shadow_label);
627 log_warning("Failed to get smack label from %s", passwd_path);
631 while ((sp = fgetspent(original))) {
633 i = hashmap_get(users, sp->sp_namp);
634 if (i && i->todo_user) {
640 if (putspent(sp, shadow) < 0) {
641 r = errno ? -errno : -EIO;
647 if (!IN_SET(errno, 0, ENOENT)) {
651 } else if (errno != ENOENT) {
656 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
657 HASHMAP_FOREACH(i, todo_uids, iterator) {
660 .sp_pwdp = (char*) "!!",
667 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
671 if (putspent(&n, shadow) != 0) {
672 r = errno ? -errno : -EIO;
677 r = fflush_and_check(shadow);
682 /* Make a backup of the old files */
685 r = make_backup("/etc/group", group_path);
690 r = make_backup("/etc/gshadow", gshadow_path);
697 r = make_backup("/etc/passwd", passwd_path);
702 r = make_backup("/etc/shadow", shadow_path);
707 /* And make the new files count */
710 if (rename(group_tmp, group_path) < 0) {
716 smack_label_path(group_path, group_label);
723 if (rename(gshadow_tmp, gshadow_path) < 0) {
729 smack_label_path(gshadow_path, gshadow_label);
738 if (rename(passwd_tmp, passwd_path) < 0) {
744 smack_label_path(passwd_path, passwd_label);
751 if (rename(shadow_tmp, shadow_path) < 0) {
757 smack_label_path(shadow_path, shadow_label);
779 static int uid_is_ok(uid_t uid, const char *name) {
785 /* Let's see if we already have assigned the UID a second time */
786 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
789 /* Try to avoid using uids that are already used by a group
790 * that doesn't have the same name as our new user. */
791 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
792 if (i && !streq(i->name, name))
795 /* Let's check the files directly */
796 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
799 n = hashmap_get(database_gid, GID_TO_PTR(uid));
800 if (n && !streq(n, name))
803 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
809 if (!IN_SET(errno, 0, ENOENT))
813 g = getgrgid((gid_t) uid);
815 if (!streq(g->gr_name, name))
817 } else if (!IN_SET(errno, 0, ENOENT))
824 static int root_stat(const char *p, struct stat *st) {
828 if (stat(fix, st) < 0)
834 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
836 bool found_uid = false, found_gid = false;
842 /* First, try to get the gid directly */
843 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
848 /* Then, try to get the uid directly */
849 if ((_uid || (_gid && !found_gid))
851 && root_stat(i->uid_path, &st) >= 0) {
856 /* If we need the gid, but had no success yet, also derive it from the uid path */
857 if (_gid && !found_gid) {
863 /* If that didn't work yet, then let's reuse the gid as uid */
864 if (_uid && !found_uid && i->gid_path) {
869 } else if (root_stat(i->gid_path, &st) >= 0) {
870 uid = (uid_t) st.st_gid;
892 static int add_user(Item *i) {
898 /* Check the database directly */
899 z = hashmap_get(database_user, i->name);
901 log_debug("User %s already exists.", i->name);
902 i->uid = PTR_TO_UID(z);
913 p = getpwnam(i->name);
915 log_debug("User %s already exists.", i->name);
919 free(i->description);
920 i->description = strdup(p->pw_gecos);
923 if (!IN_SET(errno, 0, ENOENT)) {
924 log_error("Failed to check if user %s already exists: %m", i->name);
928 /* And shadow too, just to be sure */
930 sp = getspnam(i->name);
932 log_error("User %s already exists in shadow database, but not in user database.", i->name);
935 if (!IN_SET(errno, 0, ENOENT)) {
936 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
941 /* Try to use the suggested numeric uid */
943 r = uid_is_ok(i->uid, i->name);
945 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
949 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
954 /* If that didn't work, try to read it from the specified path */
958 if (read_id_from_file(i, &c, NULL) > 0) {
960 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
961 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
963 r = uid_is_ok(c, i->name);
965 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
971 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
976 /* Otherwise try to reuse the group ID */
977 if (!i->uid_set && i->gid_set) {
978 r = uid_is_ok((uid_t) i->gid, i->name);
980 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
984 i->uid = (uid_t) i->gid;
989 /* And if that didn't work either, let's try to find a free one */
992 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
994 log_error("No free user ID available for %s.", i->name);
998 r = uid_is_ok(search_uid, i->name);
1000 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
1007 i->uid = search_uid;
1010 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
1014 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
1018 i->todo_user = true;
1019 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
1024 static int gid_is_ok(gid_t gid) {
1028 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
1031 /* Avoid reusing gids that are already used by a different user */
1032 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
1035 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
1038 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
1046 if (!IN_SET(errno, 0, ENOENT))
1050 p = getpwuid((uid_t) gid);
1053 if (!IN_SET(errno, 0, ENOENT))
1060 static int add_group(Item *i) {
1066 /* Check the database directly */
1067 z = hashmap_get(database_group, i->name);
1069 log_debug("Group %s already exists.", i->name);
1070 i->gid = PTR_TO_GID(z);
1075 /* Also check NSS */
1080 g = getgrnam(i->name);
1082 log_debug("Group %s already exists.", i->name);
1087 if (!IN_SET(errno, 0, ENOENT)) {
1088 log_error("Failed to check if group %s already exists: %m", i->name);
1093 /* Try to use the suggested numeric gid */
1095 r = gid_is_ok(i->gid);
1097 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1101 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1106 /* Try to reuse the numeric uid, if there's one */
1107 if (!i->gid_set && i->uid_set) {
1108 r = gid_is_ok((gid_t) i->uid);
1110 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1114 i->gid = (gid_t) i->uid;
1119 /* If that didn't work, try to read it from the specified path */
1123 if (read_id_from_file(i, NULL, &c) > 0) {
1125 if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
1126 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1130 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1136 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1141 /* And if that didn't work either, let's try to find a free one */
1144 /* We look for new GIDs in the UID pool! */
1145 r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
1147 log_error("No free group ID available for %s.", i->name);
1151 r = gid_is_ok(search_uid);
1153 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1160 i->gid = search_uid;
1163 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
1167 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1171 i->todo_group = true;
1172 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1177 static int process_item(Item *i) {
1194 j = hashmap_get(users, i->name);
1196 /* There's already user to be created for this
1197 * name, let's process that in one step */
1206 j->gid_path = strdup(i->gid_path);
1214 return add_group(i);
1218 assert_not_reached("Unknown item type");
1222 static void item_free(Item *i) {
1230 free(i->description);
1234 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1236 static int add_implicit(void) {
1241 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1243 HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1247 i = hashmap_get(groups, g);
1249 _cleanup_(item_freep) Item *j = NULL;
1251 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1259 j->type = ADD_GROUP;
1260 j->name = strdup(g);
1264 r = hashmap_put(groups, j->name, j);
1268 log_debug("Adding implicit group '%s' due to m line", j->name);
1272 STRV_FOREACH(m, l) {
1274 i = hashmap_get(users, *m);
1276 _cleanup_(item_freep) Item *j = NULL;
1278 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1287 j->name = strdup(*m);
1291 r = hashmap_put(users, j->name, j);
1295 log_debug("Adding implicit user '%s' due to m line", j->name);
1304 static bool item_equal(Item *a, Item *b) {
1308 if (a->type != b->type)
1311 if (!streq_ptr(a->name, b->name))
1314 if (!streq_ptr(a->uid_path, b->uid_path))
1317 if (!streq_ptr(a->gid_path, b->gid_path))
1320 if (!streq_ptr(a->description, b->description))
1323 if (a->uid_set != b->uid_set)
1326 if (a->uid_set && a->uid != b->uid)
1329 if (a->gid_set != b->gid_set)
1332 if (a->gid_set && a->gid != b->gid)
1335 if (!streq_ptr(a->home, b->home))
1341 static bool valid_user_group_name(const char *u) {
1348 if (!(u[0] >= 'a' && u[0] <= 'z') &&
1349 !(u[0] >= 'A' && u[0] <= 'Z') &&
1353 for (i = u+1; *i; i++) {
1354 if (!(*i >= 'a' && *i <= 'z') &&
1355 !(*i >= 'A' && *i <= 'Z') &&
1356 !(*i >= '0' && *i <= '9') &&
1362 sz = sysconf(_SC_LOGIN_NAME_MAX);
1365 if ((size_t) (i-u) > (size_t) sz)
1368 if ((size_t) (i-u) > UT_NAMESIZE - 1)
1374 static bool valid_gecos(const char *d) {
1379 if (!utf8_is_valid(d))
1382 if (string_has_cc(d, NULL))
1385 /* Colons are used as field separators, and hence not OK */
1392 static bool valid_home(const char *p) {
1397 if (!utf8_is_valid(p))
1400 if (string_has_cc(p, NULL))
1403 if (!path_is_absolute(p))
1406 if (!path_is_safe(p))
1409 /* Colons are used as field separators, and hence not OK */
1416 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1418 static const Specifier specifier_table[] = {
1419 { 'm', specifier_machine_id, NULL },
1420 { 'b', specifier_boot_id, NULL },
1421 { 'H', specifier_host_name, NULL },
1422 { 'v', specifier_kernel_release, NULL },
1426 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
1427 _cleanup_(item_freep) Item *i = NULL;
1439 r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
1441 log_error("[%s:%u] Syntax error.", fname, line);
1445 log_error("[%s:%u] Missing action and name columns.", fname, line);
1449 log_error("[%s:%u] Trailing garbage.", fname, line);
1454 if (strlen(action) != 1) {
1455 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1459 if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
1460 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1465 if (isempty(name) || streq(name, "-")) {
1471 r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1473 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1477 if (!valid_user_group_name(resolved_name)) {
1478 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
1484 if (isempty(id) || streq(id, "-")) {
1490 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1492 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1497 /* Verify description */
1498 if (isempty(description) || streq(description, "-")) {
1504 if (!valid_gecos(description)) {
1505 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
1511 if (isempty(home) || streq(home, "-")) {
1517 if (!valid_home(home)) {
1518 log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
1523 switch (action[0]) {
1526 if (resolved_name) {
1527 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
1532 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
1537 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
1542 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
1546 r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
1548 log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
1557 /* Try to extend an existing member or group item */
1559 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
1564 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1568 if (!valid_user_group_name(resolved_id)) {
1569 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id);
1574 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1579 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
1583 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1587 l = hashmap_get(members, resolved_id);
1589 /* A list for this group name already exists, let's append to it */
1590 r = strv_push(&l, resolved_name);
1594 resolved_name = NULL;
1596 assert_se(hashmap_update(members, resolved_id, l) >= 0);
1598 /* No list for this group name exists yet, create one */
1600 l = new0(char *, 2);
1604 l[0] = resolved_name;
1607 r = hashmap_put(members, resolved_id, l);
1613 resolved_id = resolved_name = NULL;
1621 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
1625 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1634 if (path_is_absolute(resolved_id)) {
1635 i->uid_path = resolved_id;
1638 path_kill_slashes(i->uid_path);
1640 r = parse_uid(resolved_id, &i->uid);
1642 log_error("Failed to parse UID: %s", id);
1650 i->description = description;
1661 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
1666 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1671 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line);
1675 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1684 if (path_is_absolute(resolved_id)) {
1685 i->gid_path = resolved_id;
1688 path_kill_slashes(i->gid_path);
1690 r = parse_gid(resolved_id, &i->gid);
1692 log_error("Failed to parse GID: %s", id);
1707 i->type = action[0];
1708 i->name = resolved_name;
1709 resolved_name = NULL;
1711 existing = hashmap_get(h, i->name);
1714 /* Two identical items are fine */
1715 if (!item_equal(existing, i))
1716 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1721 r = hashmap_put(h, i->name, i);
1729 static int read_config_file(const char *fn, bool ignore_enoent) {
1730 _cleanup_fclose_ FILE *rf = NULL;
1732 char line[LINE_MAX];
1741 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1743 if (ignore_enoent && r == -ENOENT)
1746 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1753 FOREACH_LINE(line, f, break) {
1760 if (*l == '#' || *l == 0)
1763 k = parse_line(fn, v, l);
1764 if (k < 0 && r == 0)
1769 log_error("Failed to read from file %s: %m", fn);
1777 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1781 name = hashmap_first(by_id);
1785 hashmap_remove(by_name, name);
1787 hashmap_steal_first_key(by_id);
1791 while ((name = hashmap_steal_first_key(by_name)))
1794 hashmap_free(by_name);
1795 hashmap_free(by_id);
1798 static void help(void) {
1799 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1800 "Creates system user accounts.\n\n"
1801 " -h --help Show this help\n"
1802 " --version Show package version\n"
1803 " --root=PATH Operate on an alternate filesystem root\n"
1804 , program_invocation_short_name);
1807 static int parse_argv(int argc, char *argv[]) {
1810 ARG_VERSION = 0x100,
1814 static const struct option options[] = {
1815 { "help", no_argument, NULL, 'h' },
1816 { "version", no_argument, NULL, ARG_VERSION },
1817 { "root", required_argument, NULL, ARG_ROOT },
1826 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1835 puts(PACKAGE_STRING);
1836 puts(SYSTEMD_FEATURES);
1841 arg_root = path_make_absolute_cwd(optarg);
1845 path_kill_slashes(arg_root);
1852 assert_not_reached("Unhandled option");
1858 int main(int argc, char *argv[]) {
1860 _cleanup_close_ int lock = -1;
1866 r = parse_argv(argc, argv);
1870 log_set_target(LOG_TARGET_AUTO);
1871 log_parse_environment();
1876 r = label_init(NULL);
1878 log_error("SELinux setup failed: %s", strerror(-r));
1882 if (optind < argc) {
1885 for (j = optind; j < argc; j++) {
1886 k = read_config_file(argv[j], false);
1887 if (k < 0 && r == 0)
1891 _cleanup_strv_free_ char **files = NULL;
1894 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1896 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1900 STRV_FOREACH(f, files) {
1901 k = read_config_file(*f, true);
1902 if (k < 0 && r == 0)
1908 /* Default to default range of 1..SYSTEMD_UID_MAX */
1909 r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
1920 lock = take_password_lock(arg_root);
1922 log_error("Failed to take lock: %s", strerror(-lock));
1926 r = load_user_database();
1928 log_error("Failed to load user database: %s", strerror(-r));
1932 r = load_group_database();
1934 log_error("Failed to read group database: %s", strerror(-r));
1938 HASHMAP_FOREACH(i, groups, iterator)
1941 HASHMAP_FOREACH(i, users, iterator)
1946 log_error("Failed to write files: %s", strerror(-r));
1949 while ((i = hashmap_steal_first(groups)))
1952 while ((i = hashmap_steal_first(users)))
1955 while ((n = hashmap_first_key(members))) {
1956 strv_free(hashmap_steal_first(members));
1960 hashmap_free(groups);
1961 hashmap_free(users);
1962 hashmap_free(members);
1963 hashmap_free(todo_uids);
1964 hashmap_free(todo_gids);
1966 free_database(database_user, database_uid);
1967 free_database(database_group, database_gid);
1971 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;