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 "format-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
;
193 _cleanup_free_
char *temp
= NULL
;
195 struct timespec ts
[2];
199 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
201 if (errno
== ENOENT
) /* No backup necessary... */
207 if (fstat(src
, &st
) < 0)
210 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
214 r
= copy_bytes(src
, fileno(dst
), (uint64_t) -1, COPY_REFLINK
);
218 /* Don't fail on chmod() or chown(). If it stays owned by us
219 * and/or unreadable by others, then it isn't too bad... */
221 backup
= strjoina(x
, "-");
223 /* Copy over the access mask */
224 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
225 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
227 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
228 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
232 if (futimens(fileno(dst
), ts
) < 0)
233 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
235 if (rename(temp
, backup
) < 0)
245 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
251 a
= hashmap_get(members
, gr
->gr_name
);
253 _cleanup_strv_free_
char **l
= NULL
;
257 l
= strv_copy(gr
->gr_mem
);
262 if (strv_find(l
, *i
))
265 if (strv_extend(&l
, *i
) < 0)
281 if (putgrent(&t
, group
) != 0)
282 return errno
> 0 ? -errno
: -EIO
;
289 if (putgrent(gr
, group
) != 0)
290 return errno
> 0 ? -errno
: -EIO
;
295 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
301 a
= hashmap_get(members
, sg
->sg_namp
);
303 _cleanup_strv_free_
char **l
= NULL
;
307 l
= strv_copy(sg
->sg_mem
);
312 if (strv_find(l
, *i
))
315 if (strv_extend(&l
, *i
) < 0)
331 if (putsgent(&t
, gshadow
) != 0)
332 return errno
> 0 ? -errno
: -EIO
;
339 if (putsgent(sg
, gshadow
) != 0)
340 return errno
> 0 ? -errno
: -EIO
;
345 static int sync_rights(FILE *from
, FILE *to
) {
348 if (fstat(fileno(from
), &st
) < 0)
351 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
354 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
360 static int rename_and_apply_smack(const char *temp_path
, const char *dest_path
) {
362 if (rename(temp_path
, dest_path
) < 0)
365 #ifdef SMACK_RUN_LABEL
366 r
= mac_smack_apply(dest_path
, SMACK_ATTR_ACCESS
, SMACK_FLOOR_LABEL
);
373 static int write_files(void) {
375 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
376 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
377 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
378 bool group_changed
= false;
383 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
384 _cleanup_fclose_
FILE *original
= NULL
;
386 /* First we update the actual group list file */
387 group_path
= prefix_roota(arg_root
, "/etc/group");
388 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
392 original
= fopen(group_path
, "re");
396 r
= sync_rights(original
, group
);
401 while ((gr
= fgetgrent(original
))) {
402 /* Safety checks against name and GID
403 * collisions. Normally, this should
404 * be unnecessary, but given that we
405 * look at the entries anyway here,
406 * let's make an extra verification
407 * step that we don't generate
408 * duplicate entries. */
410 i
= hashmap_get(groups
, gr
->gr_name
);
411 if (i
&& i
->todo_group
) {
412 log_error("%s: Group \"%s\" already exists.", group_path
, gr
->gr_name
);
417 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
418 log_error("%s: Detected collision for GID " GID_FMT
".", group_path
, gr
->gr_gid
);
423 r
= putgrent_with_members(gr
, group
);
427 group_changed
= true;
431 if (!IN_SET(errno
, 0, ENOENT
)) {
436 } else if (errno
!= ENOENT
) {
439 } else if (fchmod(fileno(group
), 0644) < 0) {
444 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
448 .gr_passwd
= (char*) "x",
451 r
= putgrent_with_members(&n
, group
);
455 group_changed
= true;
458 r
= fflush_and_check(group
);
467 /* OK, now also update the shadow file for the group list */
468 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
469 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
473 original
= fopen(gshadow_path
, "re");
477 r
= sync_rights(original
, gshadow
);
482 while ((sg
= fgetsgent(original
))) {
484 i
= hashmap_get(groups
, sg
->sg_namp
);
485 if (i
&& i
->todo_group
) {
486 log_error("%s: Group \"%s\" already exists.", gshadow_path
, sg
->sg_namp
);
491 r
= putsgent_with_members(sg
, gshadow
);
495 group_changed
= true;
499 if (!IN_SET(errno
, 0, ENOENT
)) {
504 } else if (errno
!= ENOENT
) {
507 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
512 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
515 .sg_passwd
= (char*) "!!",
518 r
= putsgent_with_members(&n
, gshadow
);
522 group_changed
= true;
525 r
= fflush_and_check(gshadow
);
530 if (hashmap_size(todo_uids
) > 0) {
531 _cleanup_fclose_
FILE *original
= NULL
;
534 /* First we update the user database itself */
535 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
536 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
540 original
= fopen(passwd_path
, "re");
544 r
= sync_rights(original
, passwd
);
549 while ((pw
= fgetpwent(original
))) {
551 i
= hashmap_get(users
, pw
->pw_name
);
552 if (i
&& i
->todo_user
) {
553 log_error("%s: User \"%s\" already exists.", passwd_path
, pw
->pw_name
);
558 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
559 log_error("%s: Detected collision for UID " UID_FMT
".", passwd_path
, pw
->pw_uid
);
565 if (putpwent(pw
, passwd
) < 0) {
566 r
= errno
? -errno
: -EIO
;
572 if (!IN_SET(errno
, 0, ENOENT
)) {
577 } else if (errno
!= ENOENT
) {
580 } else if (fchmod(fileno(passwd
), 0644) < 0) {
585 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
590 .pw_gecos
= i
->description
,
592 /* "x" means the password is stored in
594 .pw_passwd
= (char*) "x",
596 /* We default to the root directory as home */
597 .pw_dir
= i
->home
? i
->home
: (char*) "/",
599 /* Initialize the shell to nologin,
600 * with one exception: for root we
601 * patch in something special */
602 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
606 if (putpwent(&n
, passwd
) != 0) {
607 r
= errno
? -errno
: -EIO
;
612 r
= fflush_and_check(passwd
);
621 /* The we update the shadow database */
622 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
623 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
627 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
629 original
= fopen(shadow_path
, "re");
633 r
= sync_rights(original
, shadow
);
638 while ((sp
= fgetspent(original
))) {
640 i
= hashmap_get(users
, sp
->sp_namp
);
641 if (i
&& i
->todo_user
) {
642 /* we will update the existing entry */
643 sp
->sp_lstchg
= lstchg
;
645 /* only the /etc/shadow stage is left, so we can
646 * safely remove the item from the todo set */
647 i
->todo_user
= false;
648 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
652 if (putspent(sp
, shadow
) < 0) {
653 r
= errno
? -errno
: -EIO
;
659 if (!IN_SET(errno
, 0, ENOENT
)) {
663 } else if (errno
!= ENOENT
) {
666 } else if (fchmod(fileno(shadow
), 0000) < 0) {
671 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
674 .sp_pwdp
= (char*) "!!",
681 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
685 if (putspent(&n
, shadow
) != 0) {
686 r
= errno
? -errno
: -EIO
;
691 r
= fflush_and_check(shadow
);
696 /* Make a backup of the old files */
699 r
= make_backup("/etc/group", group_path
);
704 r
= make_backup("/etc/gshadow", gshadow_path
);
711 r
= make_backup("/etc/passwd", passwd_path
);
716 r
= make_backup("/etc/shadow", shadow_path
);
721 /* And make the new files count */
724 r
= rename_and_apply_smack(group_tmp
, group_path
);
728 group_tmp
= mfree(group_tmp
);
731 r
= rename_and_apply_smack(gshadow_tmp
, gshadow_path
);
735 gshadow_tmp
= mfree(gshadow_tmp
);
740 r
= rename_and_apply_smack(passwd_tmp
, passwd_path
);
744 passwd_tmp
= mfree(passwd_tmp
);
747 r
= rename_and_apply_smack(shadow_tmp
, shadow_path
);
751 shadow_tmp
= mfree(shadow_tmp
);
769 static int uid_is_ok(uid_t uid
, const char *name
) {
775 /* Let's see if we already have assigned the UID a second time */
776 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
779 /* Try to avoid using uids that are already used by a group
780 * that doesn't have the same name as our new user. */
781 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
782 if (i
&& !streq(i
->name
, name
))
785 /* Let's check the files directly */
786 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
789 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
790 if (n
&& !streq(n
, name
))
793 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
799 if (!IN_SET(errno
, 0, ENOENT
))
803 g
= getgrgid((gid_t
) uid
);
805 if (!streq(g
->gr_name
, name
))
807 } else if (!IN_SET(errno
, 0, ENOENT
))
814 static int root_stat(const char *p
, struct stat
*st
) {
817 fix
= prefix_roota(arg_root
, p
);
818 if (stat(fix
, st
) < 0)
824 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
826 bool found_uid
= false, found_gid
= false;
832 /* First, try to get the gid directly */
833 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
838 /* Then, try to get the uid directly */
839 if ((_uid
|| (_gid
&& !found_gid
))
841 && root_stat(i
->uid_path
, &st
) >= 0) {
846 /* If we need the gid, but had no success yet, also derive it from the uid path */
847 if (_gid
&& !found_gid
) {
853 /* If that didn't work yet, then let's reuse the gid as uid */
854 if (_uid
&& !found_uid
&& i
->gid_path
) {
859 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
860 uid
= (uid_t
) st
.st_gid
;
882 static int add_user(Item
*i
) {
888 /* Check the database directly */
889 z
= hashmap_get(database_user
, i
->name
);
891 log_debug("User %s already exists.", i
->name
);
892 i
->uid
= PTR_TO_UID(z
);
902 p
= getpwnam(i
->name
);
904 log_debug("User %s already exists.", i
->name
);
908 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
914 if (!IN_SET(errno
, 0, ENOENT
))
915 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
918 /* Try to use the suggested numeric uid */
920 r
= uid_is_ok(i
->uid
, i
->name
);
922 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
924 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
929 /* If that didn't work, try to read it from the specified path */
933 if (read_id_from_file(i
, &c
, NULL
) > 0) {
935 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
936 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
938 r
= uid_is_ok(c
, i
->name
);
940 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
945 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
950 /* Otherwise, try to reuse the group ID */
951 if (!i
->uid_set
&& i
->gid_set
) {
952 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
954 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
956 i
->uid
= (uid_t
) i
->gid
;
961 /* And if that didn't work either, let's try to find a free one */
964 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
966 log_error("No free user ID available for %s.", i
->name
);
970 r
= uid_is_ok(search_uid
, i
->name
);
972 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
981 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
985 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
990 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
995 static int gid_is_ok(gid_t gid
) {
999 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1002 /* Avoid reusing gids that are already used by a different user */
1003 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1006 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1009 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1017 if (!IN_SET(errno
, 0, ENOENT
))
1021 p
= getpwuid((uid_t
) gid
);
1024 if (!IN_SET(errno
, 0, ENOENT
))
1031 static int add_group(Item
*i
) {
1037 /* Check the database directly */
1038 z
= hashmap_get(database_group
, i
->name
);
1040 log_debug("Group %s already exists.", i
->name
);
1041 i
->gid
= PTR_TO_GID(z
);
1046 /* Also check NSS */
1051 g
= getgrnam(i
->name
);
1053 log_debug("Group %s already exists.", i
->name
);
1058 if (!IN_SET(errno
, 0, ENOENT
))
1059 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1062 /* Try to use the suggested numeric gid */
1064 r
= gid_is_ok(i
->gid
);
1066 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1068 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1073 /* Try to reuse the numeric uid, if there's one */
1074 if (!i
->gid_set
&& i
->uid_set
) {
1075 r
= gid_is_ok((gid_t
) i
->uid
);
1077 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1079 i
->gid
= (gid_t
) i
->uid
;
1084 /* If that didn't work, try to read it from the specified path */
1088 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1090 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1091 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1095 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1100 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1105 /* And if that didn't work either, let's try to find a free one */
1108 /* We look for new GIDs in the UID pool! */
1109 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1111 log_error("No free group ID available for %s.", i
->name
);
1115 r
= gid_is_ok(search_uid
);
1117 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1123 i
->gid
= search_uid
;
1126 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1130 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1134 i
->todo_group
= true;
1135 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1140 static int process_item(Item
*i
) {
1157 j
= hashmap_get(users
, i
->name
);
1159 /* There's already user to be created for this
1160 * name, let's process that in one step */
1168 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1176 return add_group(i
);
1180 assert_not_reached("Unknown item type");
1184 static void item_free(Item
*i
) {
1192 free(i
->description
);
1197 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1199 static int add_implicit(void) {
1204 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1206 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1210 i
= hashmap_get(groups
, g
);
1212 _cleanup_(item_freep
) Item
*j
= NULL
;
1214 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1222 j
->type
= ADD_GROUP
;
1223 j
->name
= strdup(g
);
1227 r
= hashmap_put(groups
, j
->name
, j
);
1231 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1235 STRV_FOREACH(m
, l
) {
1237 i
= hashmap_get(users
, *m
);
1239 _cleanup_(item_freep
) Item
*j
= NULL
;
1241 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1250 j
->name
= strdup(*m
);
1254 r
= hashmap_put(users
, j
->name
, j
);
1258 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1267 static bool item_equal(Item
*a
, Item
*b
) {
1271 if (a
->type
!= b
->type
)
1274 if (!streq_ptr(a
->name
, b
->name
))
1277 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1280 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1283 if (!streq_ptr(a
->description
, b
->description
))
1286 if (a
->uid_set
!= b
->uid_set
)
1289 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1292 if (a
->gid_set
!= b
->gid_set
)
1295 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1298 if (!streq_ptr(a
->home
, b
->home
))
1304 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1306 static const Specifier specifier_table
[] = {
1307 { 'm', specifier_machine_id
, NULL
},
1308 { 'b', specifier_boot_id
, NULL
},
1309 { 'H', specifier_host_name
, NULL
},
1310 { 'v', specifier_kernel_release
, NULL
},
1314 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1315 _cleanup_(item_freep
) Item
*i
= NULL
;
1327 r
= extract_many_words(&p
, NULL
, EXTRACT_QUOTES
, &action
, &name
, &id
, &description
, &home
, NULL
);
1329 log_error("[%s:%u] Syntax error.", fname
, line
);
1333 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1337 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1342 if (strlen(action
) != 1) {
1343 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1347 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1348 log_error("[%s:%u] Unknown command type '%c'.", fname
, line
, action
[0]);
1353 if (isempty(name
) || streq(name
, "-"))
1357 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1359 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1363 if (!valid_user_group_name(resolved_name
)) {
1364 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1370 if (isempty(id
) || streq(id
, "-"))
1374 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1376 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1381 /* Verify description */
1382 if (isempty(description
) || streq(description
, "-"))
1383 description
= mfree(description
);
1386 if (!valid_gecos(description
)) {
1387 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1393 if (isempty(home
) || streq(home
, "-"))
1397 if (!valid_home(home
)) {
1398 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1403 switch (action
[0]) {
1406 if (resolved_name
) {
1407 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1412 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1417 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1422 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1426 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1428 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1437 /* Try to extend an existing member or group item */
1439 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1444 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1448 if (!valid_user_group_name(resolved_id
)) {
1449 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1454 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1459 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1463 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1467 l
= hashmap_get(members
, resolved_id
);
1469 /* A list for this group name already exists, let's append to it */
1470 r
= strv_push(&l
, resolved_name
);
1474 resolved_name
= NULL
;
1476 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1478 /* No list for this group name exists yet, create one */
1480 l
= new0(char *, 2);
1484 l
[0] = resolved_name
;
1487 r
= hashmap_put(members
, resolved_id
, l
);
1493 resolved_id
= resolved_name
= NULL
;
1501 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1505 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1514 if (path_is_absolute(resolved_id
)) {
1515 i
->uid_path
= resolved_id
;
1518 path_kill_slashes(i
->uid_path
);
1520 r
= parse_uid(resolved_id
, &i
->uid
);
1522 log_error("Failed to parse UID: %s", id
);
1530 i
->description
= description
;
1541 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1546 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1551 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1555 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1564 if (path_is_absolute(resolved_id
)) {
1565 i
->gid_path
= resolved_id
;
1568 path_kill_slashes(i
->gid_path
);
1570 r
= parse_gid(resolved_id
, &i
->gid
);
1572 log_error("Failed to parse GID: %s", id
);
1587 i
->type
= action
[0];
1588 i
->name
= resolved_name
;
1589 resolved_name
= NULL
;
1591 existing
= hashmap_get(h
, i
->name
);
1594 /* Two identical items are fine */
1595 if (!item_equal(existing
, i
))
1596 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1601 r
= hashmap_put(h
, i
->name
, i
);
1609 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1610 _cleanup_fclose_
FILE *rf
= NULL
;
1612 char line
[LINE_MAX
];
1621 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1623 if (ignore_enoent
&& r
== -ENOENT
)
1626 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1632 FOREACH_LINE(line
, f
, break) {
1639 if (*l
== '#' || *l
== 0)
1642 k
= parse_line(fn
, v
, l
);
1643 if (k
< 0 && r
== 0)
1648 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1656 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1660 name
= hashmap_first(by_id
);
1664 hashmap_remove(by_name
, name
);
1666 hashmap_steal_first_key(by_id
);
1670 while ((name
= hashmap_steal_first_key(by_name
)))
1673 hashmap_free(by_name
);
1674 hashmap_free(by_id
);
1677 static void help(void) {
1678 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1679 "Creates system user accounts.\n\n"
1680 " -h --help Show this help\n"
1681 " --version Show package version\n"
1682 " --root=PATH Operate on an alternate filesystem root\n"
1683 , program_invocation_short_name
);
1686 static int parse_argv(int argc
, char *argv
[]) {
1689 ARG_VERSION
= 0x100,
1693 static const struct option options
[] = {
1694 { "help", no_argument
, NULL
, 'h' },
1695 { "version", no_argument
, NULL
, ARG_VERSION
},
1696 { "root", required_argument
, NULL
, ARG_ROOT
},
1705 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1717 r
= parse_path_argument_and_warn(optarg
, true, &arg_root
);
1726 assert_not_reached("Unhandled option");
1732 int main(int argc
, char *argv
[]) {
1734 _cleanup_close_
int lock
= -1;
1740 r
= parse_argv(argc
, argv
);
1744 log_set_target(LOG_TARGET_AUTO
);
1745 log_parse_environment();
1750 r
= mac_selinux_init();
1752 log_error_errno(r
, "SELinux setup failed: %m");
1756 if (optind
< argc
) {
1759 for (j
= optind
; j
< argc
; j
++) {
1760 k
= read_config_file(argv
[j
], false);
1761 if (k
< 0 && r
== 0)
1765 _cleanup_strv_free_
char **files
= NULL
;
1768 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1770 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1774 STRV_FOREACH(f
, files
) {
1775 k
= read_config_file(*f
, true);
1776 if (k
< 0 && r
== 0)
1782 /* Default to default range of 1..SYSTEMD_UID_MAX */
1783 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1794 lock
= take_etc_passwd_lock(arg_root
);
1796 log_error_errno(lock
, "Failed to take lock: %m");
1800 r
= load_user_database();
1802 log_error_errno(r
, "Failed to load user database: %m");
1806 r
= load_group_database();
1808 log_error_errno(r
, "Failed to read group database: %m");
1812 HASHMAP_FOREACH(i
, groups
, iterator
)
1815 HASHMAP_FOREACH(i
, users
, iterator
)
1820 log_error_errno(r
, "Failed to write files: %m");
1823 while ((i
= hashmap_steal_first(groups
)))
1826 while ((i
= hashmap_steal_first(users
)))
1829 while ((n
= hashmap_first_key(members
))) {
1830 strv_free(hashmap_steal_first(members
));
1834 hashmap_free(groups
);
1835 hashmap_free(users
);
1836 hashmap_free(members
);
1837 hashmap_free(todo_uids
);
1838 hashmap_free(todo_gids
);
1840 free_database(database_user
, database_uid
);
1841 free_database(database_group
, database_gid
);
1845 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;