2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "alloc-util.h"
28 #include "conf-files.h"
32 #include "fileio-label.h"
33 #include "formats-util.h"
35 #include "path-util.h"
36 #include "selinux-util.h"
37 #include "smack-util.h"
38 #include "specifier.h"
39 #include "string-util.h"
41 #include "uid-range.h"
42 #include "user-util.h"
46 typedef enum ItemType
{
71 static char *arg_root
= NULL
;
73 static const char conf_file_dirs
[] = CONF_PATHS_NULSTR("sysusers.d");
75 static Hashmap
*users
= NULL
, *groups
= NULL
;
76 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
77 static Hashmap
*members
= NULL
;
79 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
80 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
82 static uid_t search_uid
= UID_INVALID
;
83 static UidRange
*uid_range
= NULL
;
84 static unsigned n_uid_range
= 0;
86 static int load_user_database(void) {
87 _cleanup_fclose_
FILE *f
= NULL
;
88 const char *passwd_path
;
92 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
93 f
= fopen(passwd_path
, "re");
95 return errno
== ENOENT
? 0 : -errno
;
97 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
101 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
106 while ((pw
= fgetpwent(f
))) {
110 n
= strdup(pw
->pw_name
);
114 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
115 if (k
< 0 && k
!= -EEXIST
) {
120 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
121 if (q
< 0 && q
!= -EEXIST
) {
132 if (!IN_SET(errno
, 0, ENOENT
))
138 static int load_group_database(void) {
139 _cleanup_fclose_
FILE *f
= NULL
;
140 const char *group_path
;
144 group_path
= prefix_roota(arg_root
, "/etc/group");
145 f
= fopen(group_path
, "re");
147 return errno
== ENOENT
? 0 : -errno
;
149 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
153 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
158 while ((gr
= fgetgrent(f
))) {
162 n
= strdup(gr
->gr_name
);
166 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
167 if (k
< 0 && k
!= -EEXIST
) {
172 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
173 if (q
< 0 && q
!= -EEXIST
) {
184 if (!IN_SET(errno
, 0, ENOENT
))
190 static int make_backup(const char *target
, const char *x
) {
191 _cleanup_close_
int src
= -1;
192 _cleanup_fclose_
FILE *dst
= NULL
;
194 struct timespec ts
[2];
198 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
200 if (errno
== ENOENT
) /* No backup necessary... */
206 if (fstat(src
, &st
) < 0)
209 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
213 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, true);
217 /* Don't fail on chmod() or chown(). If it stays owned by us
218 * and/or unreadable by others, then it isn't too bad... */
220 backup
= strjoina(x
, "-");
222 /* Copy over the access mask */
223 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
224 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
226 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
227 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
231 if (futimens(fileno(dst
), ts
) < 0)
232 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
234 if (rename(temp
, backup
) < 0)
244 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
250 a
= hashmap_get(members
, gr
->gr_name
);
252 _cleanup_strv_free_
char **l
= NULL
;
256 l
= strv_copy(gr
->gr_mem
);
261 if (strv_find(l
, *i
))
264 if (strv_extend(&l
, *i
) < 0)
280 if (putgrent(&t
, group
) != 0)
281 return errno
> 0 ? -errno
: -EIO
;
288 if (putgrent(gr
, group
) != 0)
289 return errno
> 0 ? -errno
: -EIO
;
294 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
300 a
= hashmap_get(members
, sg
->sg_namp
);
302 _cleanup_strv_free_
char **l
= NULL
;
306 l
= strv_copy(sg
->sg_mem
);
311 if (strv_find(l
, *i
))
314 if (strv_extend(&l
, *i
) < 0)
330 if (putsgent(&t
, gshadow
) != 0)
331 return errno
> 0 ? -errno
: -EIO
;
338 if (putsgent(sg
, gshadow
) != 0)
339 return errno
> 0 ? -errno
: -EIO
;
344 static int sync_rights(FILE *from
, FILE *to
) {
347 if (fstat(fileno(from
), &st
) < 0)
350 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
353 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
359 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
361 if (rename(temp_path
, dest_path
) < 0)
364 #ifdef SMACK_RUN_LABEL
365 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
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
= prefix_roota(arg_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
) {
411 log_error("%s: Group \"%s\" already exists.", group_path
, gr
->gr_name
);
416 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
417 log_error("%s: Detected collision for GID " GID_FMT
".", group_path
, gr
->gr_gid
);
422 r
= putgrent_with_members(gr
, group
);
426 group_changed
= true;
430 if (!IN_SET(errno
, 0, ENOENT
)) {
435 } else if (errno
!= ENOENT
) {
438 } else if (fchmod(fileno(group
), 0644) < 0) {
443 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
447 .gr_passwd
= (char*) "x",
450 r
= putgrent_with_members(&n
, group
);
454 group_changed
= true;
457 r
= fflush_and_check(group
);
466 /* OK, now also update the shadow file for the group list */
467 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
468 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
472 original
= fopen(gshadow_path
, "re");
476 r
= sync_rights(original
, gshadow
);
481 while ((sg
= fgetsgent(original
))) {
483 i
= hashmap_get(groups
, sg
->sg_namp
);
484 if (i
&& i
->todo_group
) {
485 log_error("%s: Group \"%s\" already exists.", gshadow_path
, sg
->sg_namp
);
490 r
= putsgent_with_members(sg
, gshadow
);
494 group_changed
= true;
498 if (!IN_SET(errno
, 0, ENOENT
)) {
503 } else if (errno
!= ENOENT
) {
506 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
511 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
514 .sg_passwd
= (char*) "!!",
517 r
= putsgent_with_members(&n
, gshadow
);
521 group_changed
= true;
524 r
= fflush_and_check(gshadow
);
529 if (hashmap_size(todo_uids
) > 0) {
530 _cleanup_fclose_
FILE *original
= NULL
;
533 /* First we update the user database itself */
534 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
535 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
539 original
= fopen(passwd_path
, "re");
543 r
= sync_rights(original
, passwd
);
548 while ((pw
= fgetpwent(original
))) {
550 i
= hashmap_get(users
, pw
->pw_name
);
551 if (i
&& i
->todo_user
) {
552 log_error("%s: User \"%s\" already exists.", passwd_path
, pw
->pw_name
);
557 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
558 log_error("%s: Detected collision for UID " UID_FMT
".", passwd_path
, pw
->pw_uid
);
564 if (putpwent(pw
, passwd
) < 0) {
565 r
= errno
? -errno
: -EIO
;
571 if (!IN_SET(errno
, 0, ENOENT
)) {
576 } else if (errno
!= ENOENT
) {
579 } else if (fchmod(fileno(passwd
), 0644) < 0) {
584 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
589 .pw_gecos
= i
->description
,
591 /* "x" means the password is stored in
593 .pw_passwd
= (char*) "x",
595 /* We default to the root directory as home */
596 .pw_dir
= i
->home
? i
->home
: (char*) "/",
598 /* Initialize the shell to nologin,
599 * with one exception: for root we
600 * patch in something special */
601 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
605 if (putpwent(&n
, passwd
) != 0) {
606 r
= errno
? -errno
: -EIO
;
611 r
= fflush_and_check(passwd
);
620 /* The we update the shadow database */
621 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
622 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
626 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
628 original
= fopen(shadow_path
, "re");
632 r
= sync_rights(original
, shadow
);
637 while ((sp
= fgetspent(original
))) {
639 i
= hashmap_get(users
, sp
->sp_namp
);
640 if (i
&& i
->todo_user
) {
641 /* we will update the existing entry */
642 sp
->sp_lstchg
= lstchg
;
644 /* only the /etc/shadow stage is left, so we can
645 * safely remove the item from the todo set */
646 i
->todo_user
= false;
647 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
651 if (putspent(sp
, shadow
) < 0) {
652 r
= errno
? -errno
: -EIO
;
658 if (!IN_SET(errno
, 0, ENOENT
)) {
662 } else if (errno
!= ENOENT
) {
665 } else if (fchmod(fileno(shadow
), 0000) < 0) {
670 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
673 .sp_pwdp
= (char*) "!!",
680 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
684 if (putspent(&n
, shadow
) != 0) {
685 r
= errno
? -errno
: -EIO
;
690 r
= fflush_and_check(shadow
);
695 /* Make a backup of the old files */
698 r
= make_backup("/etc/group", group_path
);
703 r
= make_backup("/etc/gshadow", gshadow_path
);
710 r
= make_backup("/etc/passwd", passwd_path
);
715 r
= make_backup("/etc/shadow", shadow_path
);
720 /* And make the new files count */
723 r
= rename_and_apply_smack(group_tmp
, group_path
);
727 group_tmp
= mfree(group_tmp
);
730 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
734 gshadow_tmp
= mfree(gshadow_tmp
);
739 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
743 passwd_tmp
= mfree(passwd_tmp
);
746 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
750 shadow_tmp
= mfree(shadow_tmp
);
768 static int uid_is_ok(uid_t uid
, const char *name
) {
774 /* Let's see if we already have assigned the UID a second time */
775 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
778 /* Try to avoid using uids that are already used by a group
779 * that doesn't have the same name as our new user. */
780 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
781 if (i
&& !streq(i
->name
, name
))
784 /* Let's check the files directly */
785 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
788 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
789 if (n
&& !streq(n
, name
))
792 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
798 if (!IN_SET(errno
, 0, ENOENT
))
802 g
= getgrgid((gid_t
) uid
);
804 if (!streq(g
->gr_name
, name
))
806 } else if (!IN_SET(errno
, 0, ENOENT
))
813 static int root_stat(const char *p
, struct stat
*st
) {
816 fix
= prefix_roota(arg_root
, p
);
817 if (stat(fix
, st
) < 0)
823 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
825 bool found_uid
= false, found_gid
= false;
831 /* First, try to get the gid directly */
832 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
837 /* Then, try to get the uid directly */
838 if ((_uid
|| (_gid
&& !found_gid
))
840 && root_stat(i
->uid_path
, &st
) >= 0) {
845 /* If we need the gid, but had no success yet, also derive it from the uid path */
846 if (_gid
&& !found_gid
) {
852 /* If that didn't work yet, then let's reuse the gid as uid */
853 if (_uid
&& !found_uid
&& i
->gid_path
) {
858 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
859 uid
= (uid_t
) st
.st_gid
;
881 static int add_user(Item
*i
) {
887 /* Check the database directly */
888 z
= hashmap_get(database_user
, i
->name
);
890 log_debug("User %s already exists.", i
->name
);
891 i
->uid
= PTR_TO_UID(z
);
901 p
= getpwnam(i
->name
);
903 log_debug("User %s already exists.", i
->name
);
907 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
913 if (!IN_SET(errno
, 0, ENOENT
))
914 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
917 /* Try to use the suggested numeric uid */
919 r
= uid_is_ok(i
->uid
, i
->name
);
921 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
923 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
928 /* If that didn't work, try to read it from the specified path */
932 if (read_id_from_file(i
, &c
, NULL
) > 0) {
934 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
935 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
937 r
= uid_is_ok(c
, i
->name
);
939 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
944 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
949 /* Otherwise, try to reuse the group ID */
950 if (!i
->uid_set
&& i
->gid_set
) {
951 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
953 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
955 i
->uid
= (uid_t
) i
->gid
;
960 /* And if that didn't work either, let's try to find a free one */
963 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
965 log_error("No free user ID available for %s.", i
->name
);
969 r
= uid_is_ok(search_uid
, i
->name
);
971 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
980 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
984 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
989 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
994 static int gid_is_ok(gid_t gid
) {
998 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1001 /* Avoid reusing gids that are already used by a different user */
1002 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1005 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1008 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1016 if (!IN_SET(errno
, 0, ENOENT
))
1020 p
= getpwuid((uid_t
) gid
);
1023 if (!IN_SET(errno
, 0, ENOENT
))
1030 static int add_group(Item
*i
) {
1036 /* Check the database directly */
1037 z
= hashmap_get(database_group
, i
->name
);
1039 log_debug("Group %s already exists.", i
->name
);
1040 i
->gid
= PTR_TO_GID(z
);
1045 /* Also check NSS */
1050 g
= getgrnam(i
->name
);
1052 log_debug("Group %s already exists.", i
->name
);
1057 if (!IN_SET(errno
, 0, ENOENT
))
1058 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1061 /* Try to use the suggested numeric gid */
1063 r
= gid_is_ok(i
->gid
);
1065 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1067 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1072 /* Try to reuse the numeric uid, if there's one */
1073 if (!i
->gid_set
&& i
->uid_set
) {
1074 r
= gid_is_ok((gid_t
) i
->uid
);
1076 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1078 i
->gid
= (gid_t
) i
->uid
;
1083 /* If that didn't work, try to read it from the specified path */
1087 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1089 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1090 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1094 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1099 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1104 /* And if that didn't work either, let's try to find a free one */
1107 /* We look for new GIDs in the UID pool! */
1108 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1110 log_error("No free group ID available for %s.", i
->name
);
1114 r
= gid_is_ok(search_uid
);
1116 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1122 i
->gid
= search_uid
;
1125 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1129 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1133 i
->todo_group
= true;
1134 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1139 static int process_item(Item
*i
) {
1156 j
= hashmap_get(users
, i
->name
);
1158 /* There's already user to be created for this
1159 * name, let's process that in one step */
1167 r
= free_and_strdup(&j
->gid_path
, 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 int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1304 static const Specifier specifier_table
[] = {
1305 { 'm', specifier_machine_id
, NULL
},
1306 { 'b', specifier_boot_id
, NULL
},
1307 { 'H', specifier_host_name
, NULL
},
1308 { 'v', specifier_kernel_release
, NULL
},
1312 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1313 _cleanup_(item_freep
) Item
*i
= NULL
;
1325 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1327 log_error("[%s:%u] Syntax error.", fname
, line
);
1331 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1335 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1340 if (strlen(action
) != 1) {
1341 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1345 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1346 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, action
[0]);
1351 if (isempty(name
) || streq(name
, "-"))
1355 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1357 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1361 if (!valid_user_group_name(resolved_name
)) {
1362 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1368 if (isempty(id
) || streq(id
, "-"))
1372 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1374 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1379 /* Verify description */
1380 if (isempty(description
) || streq(description
, "-"))
1381 description
= mfree(description
);
1384 if (!valid_gecos(description
)) {
1385 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1391 if (isempty(home
) || streq(home
, "-"))
1395 if (!valid_home(home
)) {
1396 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1401 switch (action
[0]) {
1404 if (resolved_name
) {
1405 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1410 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1415 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1420 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1424 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1426 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1435 /* Try to extend an existing member or group item */
1437 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1442 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1446 if (!valid_user_group_name(resolved_id
)) {
1447 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1452 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1457 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1461 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1465 l
= hashmap_get(members
, resolved_id
);
1467 /* A list for this group name already exists, let's append to it */
1468 r
= strv_push(&l
, resolved_name
);
1472 resolved_name
= NULL
;
1474 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1476 /* No list for this group name exists yet, create one */
1478 l
= new0(char *, 2);
1482 l
[0] = resolved_name
;
1485 r
= hashmap_put(members
, resolved_id
, l
);
1491 resolved_id
= resolved_name
= NULL
;
1499 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1503 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1512 if (path_is_absolute(resolved_id
)) {
1513 i
->uid_path
= resolved_id
;
1516 path_kill_slashes(i
->uid_path
);
1518 r
= parse_uid(resolved_id
, &i
->uid
);
1520 log_error("Failed to parse UID: %s", id
);
1528 i
->description
= description
;
1539 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1544 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1549 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1553 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1562 if (path_is_absolute(resolved_id
)) {
1563 i
->gid_path
= resolved_id
;
1566 path_kill_slashes(i
->gid_path
);
1568 r
= parse_gid(resolved_id
, &i
->gid
);
1570 log_error("Failed to parse GID: %s", id
);
1585 i
->type
= action
[0];
1586 i
->name
= resolved_name
;
1587 resolved_name
= NULL
;
1589 existing
= hashmap_get(h
, i
->name
);
1592 /* Two identical items are fine */
1593 if (!item_equal(existing
, i
))
1594 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1599 r
= hashmap_put(h
, i
->name
, i
);
1607 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1608 _cleanup_fclose_
FILE *rf
= NULL
;
1610 char line
[LINE_MAX
];
1619 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1621 if (ignore_enoent
&& r
== -ENOENT
)
1624 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1630 FOREACH_LINE(line
, f
, break) {
1637 if (*l
== '#' || *l
== 0)
1640 k
= parse_line(fn
, v
, l
);
1641 if (k
< 0 && r
== 0)
1646 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1654 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1658 name
= hashmap_first(by_id
);
1662 hashmap_remove(by_name
, name
);
1664 hashmap_steal_first_key(by_id
);
1668 while ((name
= hashmap_steal_first_key(by_name
)))
1671 hashmap_free(by_name
);
1672 hashmap_free(by_id
);
1675 static void help(void) {
1676 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1677 "Creates system user accounts.\n\n"
1678 " -h --help Show this help\n"
1679 " --version Show package version\n"
1680 " --root=PATH Operate on an alternate filesystem root\n"
1681 , program_invocation_short_name
);
1684 static int parse_argv(int argc
, char *argv
[]) {
1687 ARG_VERSION
= 0x100,
1691 static const struct option options
[] = {
1692 { "help", no_argument
, NULL
, 'h' },
1693 { "version", no_argument
, NULL
, ARG_VERSION
},
1694 { "root", required_argument
, NULL
, ARG_ROOT
},
1703 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1715 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1724 assert_not_reached("Unhandled option");
1730 int main(int argc
, char *argv
[]) {
1732 _cleanup_close_
int lock
= -1;
1738 r
= parse_argv(argc
, argv
);
1742 log_set_target(LOG_TARGET_AUTO
);
1743 log_parse_environment();
1748 r
= mac_selinux_init();
1750 log_error_errno(r
, "SELinux setup failed: %m");
1754 if (optind
< argc
) {
1757 for (j
= optind
; j
< argc
; j
++) {
1758 k
= read_config_file(argv
[j
], false);
1759 if (k
< 0 && r
== 0)
1763 _cleanup_strv_free_
char **files
= NULL
;
1766 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1768 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1772 STRV_FOREACH(f
, files
) {
1773 k
= read_config_file(*f
, true);
1774 if (k
< 0 && r
== 0)
1780 /* Default to default range of 1..SYSTEMD_UID_MAX */
1781 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1792 lock
= take_etc_passwd_lock(arg_root
);
1794 log_error_errno(lock
, "Failed to take lock: %m");
1798 r
= load_user_database();
1800 log_error_errno(r
, "Failed to load user database: %m");
1804 r
= load_group_database();
1806 log_error_errno(r
, "Failed to read group database: %m");
1810 HASHMAP_FOREACH(i
, groups
, iterator
)
1813 HASHMAP_FOREACH(i
, users
, iterator
)
1818 log_error_errno(r
, "Failed to write files: %m");
1821 while ((i
= hashmap_steal_first(groups
)))
1824 while ((i
= hashmap_steal_first(users
)))
1827 while ((n
= hashmap_first_key(members
))) {
1828 strv_free(hashmap_steal_first(members
));
1832 hashmap_free(groups
);
1833 hashmap_free(users
);
1834 hashmap_free(members
);
1835 hashmap_free(todo_uids
);
1836 hashmap_free(todo_gids
);
1838 free_database(database_user
, database_uid
);
1839 free_database(database_group
, database_gid
);
1843 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;