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"
33 #include "fileio-label.h"
34 #include "format-util.h"
36 #include "path-util.h"
37 #include "selinux-util.h"
38 #include "smack-util.h"
39 #include "specifier.h"
40 #include "string-util.h"
42 #include "uid-range.h"
43 #include "user-util.h"
47 typedef enum ItemType
{
72 static char *arg_root
= NULL
;
74 static const char conf_file_dirs
[] = CONF_PATHS_NULSTR("sysusers.d");
76 static Hashmap
*users
= NULL
, *groups
= NULL
;
77 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
78 static Hashmap
*members
= NULL
;
80 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
81 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
83 static uid_t search_uid
= UID_INVALID
;
84 static UidRange
*uid_range
= NULL
;
85 static unsigned n_uid_range
= 0;
87 static int load_user_database(void) {
88 _cleanup_fclose_
FILE *f
= NULL
;
89 const char *passwd_path
;
93 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
94 f
= fopen(passwd_path
, "re");
96 return errno
== ENOENT
? 0 : -errno
;
98 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
102 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
107 while ((pw
= fgetpwent(f
))) {
111 n
= strdup(pw
->pw_name
);
115 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
116 if (k
< 0 && k
!= -EEXIST
) {
121 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
122 if (q
< 0 && q
!= -EEXIST
) {
133 if (!IN_SET(errno
, 0, ENOENT
))
139 static int load_group_database(void) {
140 _cleanup_fclose_
FILE *f
= NULL
;
141 const char *group_path
;
145 group_path
= prefix_roota(arg_root
, "/etc/group");
146 f
= fopen(group_path
, "re");
148 return errno
== ENOENT
? 0 : -errno
;
150 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
154 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
159 while ((gr
= fgetgrent(f
))) {
163 n
= strdup(gr
->gr_name
);
167 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
168 if (k
< 0 && k
!= -EEXIST
) {
173 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
174 if (q
< 0 && q
!= -EEXIST
) {
185 if (!IN_SET(errno
, 0, ENOENT
))
191 static int make_backup(const char *target
, const char *x
) {
192 _cleanup_close_
int src
= -1;
193 _cleanup_fclose_
FILE *dst
= NULL
;
194 _cleanup_free_
char *temp
= NULL
;
196 struct timespec ts
[2];
200 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
202 if (errno
== ENOENT
) /* No backup necessary... */
208 if (fstat(src
, &st
) < 0)
211 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
215 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, COPY_REFLINK
);
219 /* Don't fail on chmod() or chown(). If it stays owned by us
220 * and/or unreadable by others, then it isn't too bad... */
222 backup
= strjoina(x
, "-");
224 /* Copy over the access mask */
225 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
226 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
228 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
229 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
233 if (futimens(fileno(dst
), ts
) < 0)
234 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
236 if (rename(temp
, backup
) < 0)
246 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
252 a
= hashmap_get(members
, gr
->gr_name
);
254 _cleanup_strv_free_
char **l
= NULL
;
258 l
= strv_copy(gr
->gr_mem
);
263 if (strv_find(l
, *i
))
266 if (strv_extend(&l
, *i
) < 0)
282 if (putgrent(&t
, group
) != 0)
283 return errno
> 0 ? -errno
: -EIO
;
290 if (putgrent(gr
, group
) != 0)
291 return errno
> 0 ? -errno
: -EIO
;
296 #ifdef ENABLE_GSHADOW
297 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
303 a
= hashmap_get(members
, sg
->sg_namp
);
305 _cleanup_strv_free_
char **l
= NULL
;
309 l
= strv_copy(sg
->sg_mem
);
314 if (strv_find(l
, *i
))
317 if (strv_extend(&l
, *i
) < 0)
333 if (putsgent(&t
, gshadow
) != 0)
334 return errno
> 0 ? -errno
: -EIO
;
341 if (putsgent(sg
, gshadow
) != 0)
342 return errno
> 0 ? -errno
: -EIO
;
348 static int sync_rights(FILE *from
, FILE *to
) {
351 if (fstat(fileno(from
), &st
) < 0)
354 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
357 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
363 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
365 if (rename(temp_path
, dest_path
) < 0)
368 #ifdef SMACK_RUN_LABEL
369 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
376 static int write_temporary_passwd(const char *passwd_path
, FILE **tmpfile
, char **tmpfile_path
) {
377 _cleanup_fclose_
FILE *original
= NULL
, *passwd
= NULL
;
378 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
;
383 if (hashmap_size(todo_uids
) == 0)
386 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
390 original
= fopen(passwd_path
, "re");
394 r
= sync_rights(original
, passwd
);
399 while ((pw
= fgetpwent(original
))) {
401 i
= hashmap_get(users
, pw
->pw_name
);
402 if (i
&& i
->todo_user
) {
403 log_error("%s: User \"%s\" already exists.", passwd_path
, pw
->pw_name
);
407 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
408 log_error("%s: Detected collision for UID " UID_FMT
".", passwd_path
, pw
->pw_uid
);
413 if (putpwent(pw
, passwd
) < 0)
414 return errno
? -errno
: -EIO
;
418 if (!IN_SET(errno
, 0, ENOENT
))
424 if (fchmod(fileno(passwd
), 0644) < 0)
428 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
433 .pw_gecos
= i
->description
,
435 /* "x" means the password is stored in the shadow file */
436 .pw_passwd
= (char*) "x",
438 /* We default to the root directory as home */
439 .pw_dir
= i
->home
? i
->home
: (char*) "/",
441 /* Initialize the shell to nologin, with one exception:
442 * for root we patch in something special */
443 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
447 if (putpwent(&n
, passwd
) != 0)
448 return errno
? -errno
: -EIO
;
451 r
= fflush_and_check(passwd
);
456 *tmpfile_path
= passwd_tmp
;
462 static int write_temporary_shadow(const char *shadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
463 _cleanup_fclose_
FILE *original
= NULL
, *shadow
= NULL
;
464 _cleanup_(unlink_and_freep
) char *shadow_tmp
= NULL
;
470 if (hashmap_size(todo_uids
) == 0)
473 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
477 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
479 original
= fopen(shadow_path
, "re");
483 r
= sync_rights(original
, shadow
);
488 while ((sp
= fgetspent(original
))) {
490 i
= hashmap_get(users
, sp
->sp_namp
);
491 if (i
&& i
->todo_user
) {
492 /* we will update the existing entry */
493 sp
->sp_lstchg
= lstchg
;
495 /* only the /etc/shadow stage is left, so we can
496 * safely remove the item from the todo set */
497 i
->todo_user
= false;
498 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
502 if (putspent(sp
, shadow
) < 0)
503 return errno
? -errno
: -EIO
;
507 if (!IN_SET(errno
, 0, ENOENT
))
513 if (fchmod(fileno(shadow
), 0000) < 0)
517 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
520 .sp_pwdp
= (char*) "!!",
527 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
531 if (putspent(&n
, shadow
) != 0)
532 return errno
? -errno
: -EIO
;
535 r
= fflush_and_check(shadow
);
540 *tmpfile_path
= shadow_tmp
;
546 static int write_temporary_group(const char *group_path
, FILE **tmpfile
, char **tmpfile_path
) {
547 _cleanup_fclose_
FILE *original
= NULL
, *group
= NULL
;
548 _cleanup_(unlink_and_freep
) char *group_tmp
= NULL
;
549 bool group_changed
= false;
554 if (hashmap_size(todo_gids
) == 0 && hashmap_size(members
) == 0)
557 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
561 original
= fopen(group_path
, "re");
565 r
= sync_rights(original
, group
);
570 while ((gr
= fgetgrent(original
))) {
571 /* Safety checks against name and GID collisions. Normally,
572 * this should be unnecessary, but given that we look at the
573 * entries anyway here, let's make an extra verification
574 * step that we don't generate duplicate entries. */
576 i
= hashmap_get(groups
, gr
->gr_name
);
577 if (i
&& i
->todo_group
) {
578 log_error("%s: Group \"%s\" already exists.", group_path
, gr
->gr_name
);
582 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
583 log_error("%s: Detected collision for GID " GID_FMT
".", group_path
, gr
->gr_gid
);
587 r
= putgrent_with_members(gr
, group
);
591 group_changed
= true;
595 if (!IN_SET(errno
, 0, ENOENT
))
601 if (fchmod(fileno(group
), 0644) < 0)
605 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
609 .gr_passwd
= (char*) "x",
612 r
= putgrent_with_members(&n
, group
);
616 group_changed
= true;
619 r
= fflush_and_check(group
);
625 *tmpfile_path
= group_tmp
;
632 static int write_temporary_gshadow(const char * gshadow_path
, FILE **tmpfile
, char **tmpfile_path
) {
633 #ifdef ENABLE_GSHADOW
634 _cleanup_fclose_
FILE *original
= NULL
, *gshadow
= NULL
;
635 _cleanup_(unlink_and_freep
) char *gshadow_tmp
= NULL
;
636 bool group_changed
= false;
641 if (hashmap_size(todo_gids
) == 0 && hashmap_size(members
) == 0)
644 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
648 original
= fopen(gshadow_path
, "re");
652 r
= sync_rights(original
, gshadow
);
657 while ((sg
= fgetsgent(original
))) {
659 i
= hashmap_get(groups
, sg
->sg_namp
);
660 if (i
&& i
->todo_group
) {
661 log_error("%s: Group \"%s\" already exists.", gshadow_path
, sg
->sg_namp
);
665 r
= putsgent_with_members(sg
, gshadow
);
669 group_changed
= true;
673 if (!IN_SET(errno
, 0, ENOENT
))
679 if (fchmod(fileno(gshadow
), 0000) < 0)
683 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
686 .sg_passwd
= (char*) "!!",
689 r
= putsgent_with_members(&n
, gshadow
);
693 group_changed
= true;
696 r
= fflush_and_check(gshadow
);
702 *tmpfile_path
= gshadow_tmp
;
712 static int write_files(void) {
713 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
714 _cleanup_(unlink_and_freep
) char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
715 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
718 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
719 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
720 group_path
= prefix_roota(arg_root
, "/etc/group");
721 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
723 r
= write_temporary_group(group_path
, &group
, &group_tmp
);
727 r
= write_temporary_gshadow(gshadow_path
, &gshadow
, &gshadow_tmp
);
731 r
= write_temporary_passwd(passwd_path
, &passwd
, &passwd_tmp
);
735 r
= write_temporary_shadow(shadow_path
, &shadow
, &shadow_tmp
);
739 /* Make a backup of the old files */
741 r
= make_backup("/etc/group", group_path
);
746 r
= make_backup("/etc/gshadow", gshadow_path
);
752 r
= make_backup("/etc/passwd", passwd_path
);
757 r
= make_backup("/etc/shadow", shadow_path
);
762 /* And make the new files count */
764 r
= rename_and_apply_smack(group_tmp
, group_path
);
768 group_tmp
= mfree(group_tmp
);
771 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
775 gshadow_tmp
= mfree(gshadow_tmp
);
779 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
783 passwd_tmp
= mfree(passwd_tmp
);
786 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
790 shadow_tmp
= mfree(shadow_tmp
);
796 static int uid_is_ok(uid_t uid
, const char *name
) {
802 /* Let's see if we already have assigned the UID a second time */
803 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
806 /* Try to avoid using uids that are already used by a group
807 * that doesn't have the same name as our new user. */
808 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
809 if (i
&& !streq(i
->name
, name
))
812 /* Let's check the files directly */
813 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
816 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
817 if (n
&& !streq(n
, name
))
820 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
826 if (!IN_SET(errno
, 0, ENOENT
))
830 g
= getgrgid((gid_t
) uid
);
832 if (!streq(g
->gr_name
, name
))
834 } else if (!IN_SET(errno
, 0, ENOENT
))
841 static int root_stat(const char *p
, struct stat
*st
) {
844 fix
= prefix_roota(arg_root
, p
);
845 if (stat(fix
, st
) < 0)
851 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
853 bool found_uid
= false, found_gid
= false;
859 /* First, try to get the gid directly */
860 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
865 /* Then, try to get the uid directly */
866 if ((_uid
|| (_gid
&& !found_gid
))
868 && root_stat(i
->uid_path
, &st
) >= 0) {
873 /* If we need the gid, but had no success yet, also derive it from the uid path */
874 if (_gid
&& !found_gid
) {
880 /* If that didn't work yet, then let's reuse the gid as uid */
881 if (_uid
&& !found_uid
&& i
->gid_path
) {
886 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
887 uid
= (uid_t
) st
.st_gid
;
909 static int add_user(Item
*i
) {
915 /* Check the database directly */
916 z
= hashmap_get(database_user
, i
->name
);
918 log_debug("User %s already exists.", i
->name
);
919 i
->uid
= PTR_TO_UID(z
);
929 p
= getpwnam(i
->name
);
931 log_debug("User %s already exists.", i
->name
);
935 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
941 if (!IN_SET(errno
, 0, ENOENT
))
942 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
945 /* Try to use the suggested numeric uid */
947 r
= uid_is_ok(i
->uid
, i
->name
);
949 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
951 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
956 /* If that didn't work, try to read it from the specified path */
960 if (read_id_from_file(i
, &c
, NULL
) > 0) {
962 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
963 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
965 r
= uid_is_ok(c
, i
->name
);
967 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
972 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
977 /* Otherwise, try to reuse the group ID */
978 if (!i
->uid_set
&& i
->gid_set
) {
979 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
981 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
983 i
->uid
= (uid_t
) i
->gid
;
988 /* And if that didn't work either, let's try to find a free one */
991 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
993 log_error("No free user ID available for %s.", i
->name
);
997 r
= uid_is_ok(search_uid
, i
->name
);
999 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
1005 i
->uid
= search_uid
;
1008 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
1012 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
1016 i
->todo_user
= true;
1017 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
1022 static int gid_is_ok(gid_t gid
) {
1026 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1029 /* Avoid reusing gids that are already used by a different user */
1030 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1033 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1036 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1044 if (!IN_SET(errno
, 0, ENOENT
))
1048 p
= getpwuid((uid_t
) gid
);
1051 if (!IN_SET(errno
, 0, ENOENT
))
1058 static int add_group(Item
*i
) {
1064 /* Check the database directly */
1065 z
= hashmap_get(database_group
, i
->name
);
1067 log_debug("Group %s already exists.", i
->name
);
1068 i
->gid
= PTR_TO_GID(z
);
1073 /* Also check NSS */
1078 g
= getgrnam(i
->name
);
1080 log_debug("Group %s already exists.", i
->name
);
1085 if (!IN_SET(errno
, 0, ENOENT
))
1086 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1089 /* Try to use the suggested numeric gid */
1091 r
= gid_is_ok(i
->gid
);
1093 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1095 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1100 /* Try to reuse the numeric uid, if there's one */
1101 if (!i
->gid_set
&& i
->uid_set
) {
1102 r
= gid_is_ok((gid_t
) i
->uid
);
1104 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1106 i
->gid
= (gid_t
) i
->uid
;
1111 /* If that didn't work, try to read it from the specified path */
1115 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1117 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1118 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1122 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1127 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1132 /* And if that didn't work either, let's try to find a free one */
1135 /* We look for new GIDs in the UID pool! */
1136 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1138 log_error("No free group ID available for %s.", i
->name
);
1142 r
= gid_is_ok(search_uid
);
1144 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1150 i
->gid
= search_uid
;
1153 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1157 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1161 i
->todo_group
= true;
1162 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1167 static int process_item(Item
*i
) {
1184 j
= hashmap_get(users
, i
->name
);
1186 /* There's already user to be created for this
1187 * name, let's process that in one step */
1195 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1203 return add_group(i
);
1207 assert_not_reached("Unknown item type");
1211 static void item_free(Item
*i
) {
1219 free(i
->description
);
1224 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1226 static int add_implicit(void) {
1231 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1233 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1237 i
= hashmap_get(groups
, g
);
1239 _cleanup_(item_freep
) Item
*j
= NULL
;
1241 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1249 j
->type
= ADD_GROUP
;
1250 j
->name
= strdup(g
);
1254 r
= hashmap_put(groups
, j
->name
, j
);
1258 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1262 STRV_FOREACH(m
, l
) {
1264 i
= hashmap_get(users
, *m
);
1266 _cleanup_(item_freep
) Item
*j
= NULL
;
1268 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1277 j
->name
= strdup(*m
);
1281 r
= hashmap_put(users
, j
->name
, j
);
1285 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1294 static bool item_equal(Item
*a
, Item
*b
) {
1298 if (a
->type
!= b
->type
)
1301 if (!streq_ptr(a
->name
, b
->name
))
1304 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1307 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1310 if (!streq_ptr(a
->description
, b
->description
))
1313 if (a
->uid_set
!= b
->uid_set
)
1316 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1319 if (a
->gid_set
!= b
->gid_set
)
1322 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1325 if (!streq_ptr(a
->home
, b
->home
))
1331 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1333 static const Specifier specifier_table
[] = {
1334 { 'm', specifier_machine_id
, NULL
},
1335 { 'b', specifier_boot_id
, NULL
},
1336 { 'H', specifier_host_name
, NULL
},
1337 { 'v', specifier_kernel_release
, NULL
},
1341 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1342 _cleanup_(item_freep
) Item
*i
= NULL
;
1354 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1356 log_error("[%s:%u] Syntax error.", fname
, line
);
1360 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1364 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1369 if (strlen(action
) != 1) {
1370 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1374 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1375 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, action
[0]);
1380 if (isempty(name
) || streq(name
, "-"))
1384 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1386 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1390 if (!valid_user_group_name(resolved_name
)) {
1391 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1397 if (isempty(id
) || streq(id
, "-"))
1401 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1403 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1408 /* Verify description */
1409 if (isempty(description
) || streq(description
, "-"))
1410 description
= mfree(description
);
1413 if (!valid_gecos(description
)) {
1414 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1420 if (isempty(home
) || streq(home
, "-"))
1424 if (!valid_home(home
)) {
1425 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1430 switch (action
[0]) {
1433 if (resolved_name
) {
1434 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1439 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1444 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1449 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1453 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1455 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1464 /* Try to extend an existing member or group item */
1466 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1471 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1475 if (!valid_user_group_name(resolved_id
)) {
1476 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1481 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1486 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1490 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1494 l
= hashmap_get(members
, resolved_id
);
1496 /* A list for this group name already exists, let's append to it */
1497 r
= strv_push(&l
, resolved_name
);
1501 resolved_name
= NULL
;
1503 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1505 /* No list for this group name exists yet, create one */
1507 l
= new0(char *, 2);
1511 l
[0] = resolved_name
;
1514 r
= hashmap_put(members
, resolved_id
, l
);
1520 resolved_id
= resolved_name
= NULL
;
1528 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1532 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1541 if (path_is_absolute(resolved_id
)) {
1542 i
->uid_path
= resolved_id
;
1545 path_kill_slashes(i
->uid_path
);
1547 r
= parse_uid(resolved_id
, &i
->uid
);
1549 log_error("Failed to parse UID: %s", id
);
1557 i
->description
= description
;
1568 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1573 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1578 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1582 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1591 if (path_is_absolute(resolved_id
)) {
1592 i
->gid_path
= resolved_id
;
1595 path_kill_slashes(i
->gid_path
);
1597 r
= parse_gid(resolved_id
, &i
->gid
);
1599 log_error("Failed to parse GID: %s", id
);
1614 i
->type
= action
[0];
1615 i
->name
= resolved_name
;
1616 resolved_name
= NULL
;
1618 existing
= hashmap_get(h
, i
->name
);
1621 /* Two identical items are fine */
1622 if (!item_equal(existing
, i
))
1623 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1628 r
= hashmap_put(h
, i
->name
, i
);
1636 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1637 _cleanup_fclose_
FILE *rf
= NULL
;
1639 char line
[LINE_MAX
];
1648 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1650 if (ignore_enoent
&& r
== -ENOENT
)
1653 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1659 FOREACH_LINE(line
, f
, break) {
1666 if (*l
== '#' || *l
== 0)
1669 k
= parse_line(fn
, v
, l
);
1670 if (k
< 0 && r
== 0)
1675 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1683 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1687 name
= hashmap_first(by_id
);
1691 hashmap_remove(by_name
, name
);
1693 hashmap_steal_first_key(by_id
);
1697 while ((name
= hashmap_steal_first_key(by_name
)))
1700 hashmap_free(by_name
);
1701 hashmap_free(by_id
);
1704 static void help(void) {
1705 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1706 "Creates system user accounts.\n\n"
1707 " -h --help Show this help\n"
1708 " --version Show package version\n"
1709 " --root=PATH Operate on an alternate filesystem root\n"
1710 , program_invocation_short_name
);
1713 static int parse_argv(int argc
, char *argv
[]) {
1716 ARG_VERSION
= 0x100,
1720 static const struct option options
[] = {
1721 { "help", no_argument
, NULL
, 'h' },
1722 { "version", no_argument
, NULL
, ARG_VERSION
},
1723 { "root", required_argument
, NULL
, ARG_ROOT
},
1732 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1744 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1753 assert_not_reached("Unhandled option");
1759 int main(int argc
, char *argv
[]) {
1761 _cleanup_close_
int lock
= -1;
1767 r
= parse_argv(argc
, argv
);
1771 log_set_target(LOG_TARGET_AUTO
);
1772 log_parse_environment();
1777 r
= mac_selinux_init();
1779 log_error_errno(r
, "SELinux setup failed: %m");
1783 if (optind
< argc
) {
1786 for (j
= optind
; j
< argc
; j
++) {
1787 k
= read_config_file(argv
[j
], false);
1788 if (k
< 0 && r
== 0)
1792 _cleanup_strv_free_
char **files
= NULL
;
1795 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1797 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1801 STRV_FOREACH(f
, files
) {
1802 k
= read_config_file(*f
, true);
1803 if (k
< 0 && r
== 0)
1809 /* Default to default range of 1..SYSTEMD_UID_MAX */
1810 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1821 lock
= take_etc_passwd_lock(arg_root
);
1823 log_error_errno(lock
, "Failed to take lock: %m");
1827 r
= load_user_database();
1829 log_error_errno(r
, "Failed to load user database: %m");
1833 r
= load_group_database();
1835 log_error_errno(r
, "Failed to read group database: %m");
1839 HASHMAP_FOREACH(i
, groups
, iterator
)
1842 HASHMAP_FOREACH(i
, users
, iterator
)
1847 log_error_errno(r
, "Failed to write files: %m");
1850 while ((i
= hashmap_steal_first(groups
)))
1853 while ((i
= hashmap_steal_first(users
)))
1856 while ((n
= hashmap_first_key(members
))) {
1857 strv_free(hashmap_steal_first(members
));
1861 hashmap_free(groups
);
1862 hashmap_free(users
);
1863 hashmap_free(members
);
1864 hashmap_free(todo_uids
);
1865 hashmap_free(todo_gids
);
1867 free_database(database_user
, database_uid
);
1868 free_database(database_group
, database_gid
);
1872 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;