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/>.
31 #include "specifier.h"
32 #include "path-util.h"
35 #include "conf-files.h"
38 #include "fileio-label.h"
39 #include "uid-range.h"
40 #include "selinux-util.h"
41 #include "formats-util.h"
43 typedef enum ItemType
{
68 static char *arg_root
= NULL
;
70 static const char conf_file_dirs
[] = CONF_DIRS_NULSTR("sysusers");
72 static Hashmap
*users
= NULL
, *groups
= NULL
;
73 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
74 static Hashmap
*members
= NULL
;
76 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
77 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
79 static uid_t search_uid
= UID_INVALID
;
80 static UidRange
*uid_range
= NULL
;
81 static unsigned n_uid_range
= 0;
83 static int load_user_database(void) {
84 _cleanup_fclose_
FILE *f
= NULL
;
85 const char *passwd_path
;
89 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
90 f
= fopen(passwd_path
, "re");
92 return errno
== ENOENT
? 0 : -errno
;
94 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
98 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
103 while ((pw
= fgetpwent(f
))) {
107 n
= strdup(pw
->pw_name
);
111 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
112 if (k
< 0 && k
!= -EEXIST
) {
117 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
118 if (q
< 0 && q
!= -EEXIST
) {
129 if (!IN_SET(errno
, 0, ENOENT
))
135 static int load_group_database(void) {
136 _cleanup_fclose_
FILE *f
= NULL
;
137 const char *group_path
;
141 group_path
= prefix_roota(arg_root
, "/etc/group");
142 f
= fopen(group_path
, "re");
144 return errno
== ENOENT
? 0 : -errno
;
146 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
150 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
155 while ((gr
= fgetgrent(f
))) {
159 n
= strdup(gr
->gr_name
);
163 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
164 if (k
< 0 && k
!= -EEXIST
) {
169 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
170 if (q
< 0 && q
!= -EEXIST
) {
181 if (!IN_SET(errno
, 0, ENOENT
))
187 static int make_backup(const char *target
, const char *x
) {
188 _cleanup_close_
int src
= -1;
189 _cleanup_fclose_
FILE *dst
= NULL
;
191 struct timespec ts
[2];
195 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
197 if (errno
== ENOENT
) /* No backup necessary... */
203 if (fstat(src
, &st
) < 0)
206 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
210 r
= copy_bytes(src
, fileno(dst
), (off_t
) -1, true);
214 /* Don't fail on chmod() or chown(). If it stays owned by us
215 * and/or unreadable by others, then it isn't too bad... */
217 backup
= strjoina(x
, "-");
219 /* Copy over the access mask */
220 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
221 log_warning_errno(errno
, "Failed to change mode on %s: %m", backup
);
223 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
224 log_warning_errno(errno
, "Failed to change ownership of %s: %m", backup
);
228 if (futimens(fileno(dst
), ts
) < 0)
229 log_warning_errno(errno
, "Failed to fix access and modification time of %s: %m", backup
);
231 if (rename(temp
, backup
) < 0)
241 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
247 a
= hashmap_get(members
, gr
->gr_name
);
249 _cleanup_strv_free_
char **l
= NULL
;
253 l
= strv_copy(gr
->gr_mem
);
258 if (strv_find(l
, *i
))
261 if (strv_extend(&l
, *i
) < 0)
277 if (putgrent(&t
, group
) != 0)
278 return errno
? -errno
: -EIO
;
285 if (putgrent(gr
, group
) != 0)
286 return errno
? -errno
: -EIO
;
291 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
297 a
= hashmap_get(members
, sg
->sg_namp
);
299 _cleanup_strv_free_
char **l
= NULL
;
303 l
= strv_copy(sg
->sg_mem
);
308 if (strv_find(l
, *i
))
311 if (strv_extend(&l
, *i
) < 0)
327 if (putsgent(&t
, gshadow
) != 0)
328 return errno
? -errno
: -EIO
;
335 if (putsgent(sg
, gshadow
) != 0)
336 return errno
? -errno
: -EIO
;
341 static int sync_rights(FILE *from
, FILE *to
) {
344 if (fstat(fileno(from
), &st
) < 0)
347 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
350 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
356 static int write_files(void) {
358 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
359 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
360 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
361 bool group_changed
= false;
366 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
367 _cleanup_fclose_
FILE *original
= NULL
;
369 /* First we update the actual group list file */
370 group_path
= prefix_roota(arg_root
, "/etc/group");
371 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
375 original
= fopen(group_path
, "re");
379 r
= sync_rights(original
, group
);
384 while ((gr
= fgetgrent(original
))) {
385 /* Safety checks against name and GID
386 * collisions. Normally, this should
387 * be unnecessary, but given that we
388 * look at the entries anyway here,
389 * let's make an extra verification
390 * step that we don't generate
391 * duplicate entries. */
393 i
= hashmap_get(groups
, gr
->gr_name
);
394 if (i
&& i
->todo_group
) {
399 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
404 r
= putgrent_with_members(gr
, group
);
408 group_changed
= true;
412 if (!IN_SET(errno
, 0, ENOENT
)) {
417 } else if (errno
!= ENOENT
) {
420 } else if (fchmod(fileno(group
), 0644) < 0) {
425 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
429 .gr_passwd
= (char*) "x",
432 r
= putgrent_with_members(&n
, group
);
436 group_changed
= true;
439 r
= fflush_and_check(group
);
448 /* OK, now also update the shadow file for the group list */
449 gshadow_path
= prefix_roota(arg_root
, "/etc/gshadow");
450 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
454 original
= fopen(gshadow_path
, "re");
458 r
= sync_rights(original
, gshadow
);
463 while ((sg
= fgetsgent(original
))) {
465 i
= hashmap_get(groups
, sg
->sg_namp
);
466 if (i
&& i
->todo_group
) {
471 r
= putsgent_with_members(sg
, gshadow
);
475 group_changed
= true;
479 if (!IN_SET(errno
, 0, ENOENT
)) {
484 } else if (errno
!= ENOENT
) {
487 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
492 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
495 .sg_passwd
= (char*) "!!",
498 r
= putsgent_with_members(&n
, gshadow
);
502 group_changed
= true;
505 r
= fflush_and_check(gshadow
);
510 if (hashmap_size(todo_uids
) > 0) {
511 _cleanup_fclose_
FILE *original
= NULL
;
514 /* First we update the user database itself */
515 passwd_path
= prefix_roota(arg_root
, "/etc/passwd");
516 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
520 original
= fopen(passwd_path
, "re");
524 r
= sync_rights(original
, passwd
);
529 while ((pw
= fgetpwent(original
))) {
531 i
= hashmap_get(users
, pw
->pw_name
);
532 if (i
&& i
->todo_user
) {
537 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
543 if (putpwent(pw
, passwd
) < 0) {
544 r
= errno
? -errno
: -EIO
;
550 if (!IN_SET(errno
, 0, ENOENT
)) {
555 } else if (errno
!= ENOENT
) {
558 } else if (fchmod(fileno(passwd
), 0644) < 0) {
563 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
568 .pw_gecos
= i
->description
,
570 /* "x" means the password is stored in
572 .pw_passwd
= (char*) "x",
574 /* We default to the root directory as home */
575 .pw_dir
= i
->home
? i
->home
: (char*) "/",
577 /* Initialize the shell to nologin,
578 * with one exception: for root we
579 * patch in something special */
580 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
584 if (putpwent(&n
, passwd
) != 0) {
585 r
= errno
? -errno
: -EIO
;
590 r
= fflush_and_check(passwd
);
599 /* The we update the shadow database */
600 shadow_path
= prefix_roota(arg_root
, "/etc/shadow");
601 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
605 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
607 original
= fopen(shadow_path
, "re");
611 r
= sync_rights(original
, shadow
);
616 while ((sp
= fgetspent(original
))) {
618 i
= hashmap_get(users
, sp
->sp_namp
);
619 if (i
&& i
->todo_user
) {
620 /* we will update the existing entry */
621 sp
->sp_lstchg
= lstchg
;
623 /* only the /etc/shadow stage is left, so we can
624 * safely remove the item from the todo set */
625 i
->todo_user
= false;
626 hashmap_remove(todo_uids
, UID_TO_PTR(i
->uid
));
630 if (putspent(sp
, shadow
) < 0) {
631 r
= errno
? -errno
: -EIO
;
637 if (!IN_SET(errno
, 0, ENOENT
)) {
641 } else if (errno
!= ENOENT
) {
644 } else if (fchmod(fileno(shadow
), 0000) < 0) {
649 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
652 .sp_pwdp
= (char*) "!!",
659 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
663 if (putspent(&n
, shadow
) != 0) {
664 r
= errno
? -errno
: -EIO
;
669 r
= fflush_and_check(shadow
);
674 /* Make a backup of the old files */
677 r
= make_backup("/etc/group", group_path
);
682 r
= make_backup("/etc/gshadow", gshadow_path
);
689 r
= make_backup("/etc/passwd", passwd_path
);
694 r
= make_backup("/etc/shadow", shadow_path
);
699 /* And make the new files count */
702 if (rename(group_tmp
, group_path
) < 0) {
707 group_tmp
= mfree(group_tmp
);
710 if (rename(gshadow_tmp
, gshadow_path
) < 0) {
715 gshadow_tmp
= mfree(gshadow_tmp
);
720 if (rename(passwd_tmp
, passwd_path
) < 0) {
725 passwd_tmp
= mfree(passwd_tmp
);
728 if (rename(shadow_tmp
, shadow_path
) < 0) {
733 shadow_tmp
= mfree(shadow_tmp
);
751 static int uid_is_ok(uid_t uid
, const char *name
) {
757 /* Let's see if we already have assigned the UID a second time */
758 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
761 /* Try to avoid using uids that are already used by a group
762 * that doesn't have the same name as our new user. */
763 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
764 if (i
&& !streq(i
->name
, name
))
767 /* Let's check the files directly */
768 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
771 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
772 if (n
&& !streq(n
, name
))
775 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
781 if (!IN_SET(errno
, 0, ENOENT
))
785 g
= getgrgid((gid_t
) uid
);
787 if (!streq(g
->gr_name
, name
))
789 } else if (!IN_SET(errno
, 0, ENOENT
))
796 static int root_stat(const char *p
, struct stat
*st
) {
799 fix
= prefix_roota(arg_root
, p
);
800 if (stat(fix
, st
) < 0)
806 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
808 bool found_uid
= false, found_gid
= false;
814 /* First, try to get the gid directly */
815 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
820 /* Then, try to get the uid directly */
821 if ((_uid
|| (_gid
&& !found_gid
))
823 && root_stat(i
->uid_path
, &st
) >= 0) {
828 /* If we need the gid, but had no success yet, also derive it from the uid path */
829 if (_gid
&& !found_gid
) {
835 /* If that didn't work yet, then let's reuse the gid as uid */
836 if (_uid
&& !found_uid
&& i
->gid_path
) {
841 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
842 uid
= (uid_t
) st
.st_gid
;
864 static int add_user(Item
*i
) {
870 /* Check the database directly */
871 z
= hashmap_get(database_user
, i
->name
);
873 log_debug("User %s already exists.", i
->name
);
874 i
->uid
= PTR_TO_UID(z
);
884 p
= getpwnam(i
->name
);
886 log_debug("User %s already exists.", i
->name
);
890 r
= free_and_strdup(&i
->description
, p
->pw_gecos
);
896 if (!IN_SET(errno
, 0, ENOENT
))
897 return log_error_errno(errno
, "Failed to check if user %s already exists: %m", i
->name
);
900 /* Try to use the suggested numeric uid */
902 r
= uid_is_ok(i
->uid
, i
->name
);
904 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
906 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
911 /* If that didn't work, try to read it from the specified path */
915 if (read_id_from_file(i
, &c
, NULL
) > 0) {
917 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
918 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
920 r
= uid_is_ok(c
, i
->name
);
922 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
927 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
932 /* Otherwise try to reuse the group ID */
933 if (!i
->uid_set
&& i
->gid_set
) {
934 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
936 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
938 i
->uid
= (uid_t
) i
->gid
;
943 /* And if that didn't work either, let's try to find a free one */
946 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
948 log_error("No free user ID available for %s.", i
->name
);
952 r
= uid_is_ok(search_uid
, i
->name
);
954 return log_error_errno(r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
963 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
967 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
972 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
977 static int gid_is_ok(gid_t gid
) {
981 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
984 /* Avoid reusing gids that are already used by a different user */
985 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
988 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
991 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
999 if (!IN_SET(errno
, 0, ENOENT
))
1003 p
= getpwuid((uid_t
) gid
);
1006 if (!IN_SET(errno
, 0, ENOENT
))
1013 static int add_group(Item
*i
) {
1019 /* Check the database directly */
1020 z
= hashmap_get(database_group
, i
->name
);
1022 log_debug("Group %s already exists.", i
->name
);
1023 i
->gid
= PTR_TO_GID(z
);
1028 /* Also check NSS */
1033 g
= getgrnam(i
->name
);
1035 log_debug("Group %s already exists.", i
->name
);
1040 if (!IN_SET(errno
, 0, ENOENT
))
1041 return log_error_errno(errno
, "Failed to check if group %s already exists: %m", i
->name
);
1044 /* Try to use the suggested numeric gid */
1046 r
= gid_is_ok(i
->gid
);
1048 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1050 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1055 /* Try to reuse the numeric uid, if there's one */
1056 if (!i
->gid_set
&& i
->uid_set
) {
1057 r
= gid_is_ok((gid_t
) i
->uid
);
1059 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1061 i
->gid
= (gid_t
) i
->uid
;
1066 /* If that didn't work, try to read it from the specified path */
1070 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1072 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1073 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1077 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1082 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1087 /* And if that didn't work either, let's try to find a free one */
1090 /* We look for new GIDs in the UID pool! */
1091 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1093 log_error("No free group ID available for %s.", i
->name
);
1097 r
= gid_is_ok(search_uid
);
1099 return log_error_errno(r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1105 i
->gid
= search_uid
;
1108 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1112 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1116 i
->todo_group
= true;
1117 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1122 static int process_item(Item
*i
) {
1139 j
= hashmap_get(users
, i
->name
);
1141 /* There's already user to be created for this
1142 * name, let's process that in one step */
1150 r
= free_and_strdup(&j
->gid_path
, i
->gid_path
);
1158 return add_group(i
);
1162 assert_not_reached("Unknown item type");
1166 static void item_free(Item
*i
) {
1174 free(i
->description
);
1178 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1180 static int add_implicit(void) {
1185 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1187 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1191 i
= hashmap_get(groups
, g
);
1193 _cleanup_(item_freep
) Item
*j
= NULL
;
1195 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1203 j
->type
= ADD_GROUP
;
1204 j
->name
= strdup(g
);
1208 r
= hashmap_put(groups
, j
->name
, j
);
1212 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1216 STRV_FOREACH(m
, l
) {
1218 i
= hashmap_get(users
, *m
);
1220 _cleanup_(item_freep
) Item
*j
= NULL
;
1222 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1231 j
->name
= strdup(*m
);
1235 r
= hashmap_put(users
, j
->name
, j
);
1239 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1248 static bool item_equal(Item
*a
, Item
*b
) {
1252 if (a
->type
!= b
->type
)
1255 if (!streq_ptr(a
->name
, b
->name
))
1258 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1261 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1264 if (!streq_ptr(a
->description
, b
->description
))
1267 if (a
->uid_set
!= b
->uid_set
)
1270 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1273 if (a
->gid_set
!= b
->gid_set
)
1276 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1279 if (!streq_ptr(a
->home
, b
->home
))
1285 static bool valid_user_group_name(const char *u
) {
1292 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1293 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1297 for (i
= u
+1; *i
; i
++) {
1298 if (!(*i
>= 'a' && *i
<= 'z') &&
1299 !(*i
>= 'A' && *i
<= 'Z') &&
1300 !(*i
>= '0' && *i
<= '9') &&
1306 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1309 if ((size_t) (i
-u
) > (size_t) sz
)
1312 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1318 static bool valid_gecos(const char *d
) {
1323 if (!utf8_is_valid(d
))
1326 if (string_has_cc(d
, NULL
))
1329 /* Colons are used as field separators, and hence not OK */
1336 static bool valid_home(const char *p
) {
1341 if (!utf8_is_valid(p
))
1344 if (string_has_cc(p
, NULL
))
1347 if (!path_is_absolute(p
))
1350 if (!path_is_safe(p
))
1353 /* Colons are used as field separators, and hence not OK */
1360 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1362 static const Specifier specifier_table
[] = {
1363 { 'm', specifier_machine_id
, NULL
},
1364 { 'b', specifier_boot_id
, NULL
},
1365 { 'H', specifier_host_name
, NULL
},
1366 { 'v', specifier_kernel_release
, NULL
},
1370 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1371 _cleanup_(item_freep
) Item
*i
= NULL
;
1383 r
= unquote_many_words(&p
, 0, &action
, &name
, &id
, &description
, &home
, NULL
);
1385 log_error("[%s:%u] Syntax error.", fname
, line
);
1389 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1393 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1398 if (strlen(action
) != 1) {
1399 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1403 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1404 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1409 if (isempty(name
) || streq(name
, "-"))
1413 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1415 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1419 if (!valid_user_group_name(resolved_name
)) {
1420 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1426 if (isempty(id
) || streq(id
, "-"))
1430 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1432 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1437 /* Verify description */
1438 if (isempty(description
) || streq(description
, "-"))
1439 description
= mfree(description
);
1442 if (!valid_gecos(description
)) {
1443 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1449 if (isempty(home
) || streq(home
, "-"))
1453 if (!valid_home(home
)) {
1454 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1459 switch (action
[0]) {
1462 if (resolved_name
) {
1463 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1468 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1473 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1478 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1482 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1484 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1493 /* Try to extend an existing member or group item */
1495 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1500 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1504 if (!valid_user_group_name(resolved_id
)) {
1505 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1510 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1515 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1519 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1523 l
= hashmap_get(members
, resolved_id
);
1525 /* A list for this group name already exists, let's append to it */
1526 r
= strv_push(&l
, resolved_name
);
1530 resolved_name
= NULL
;
1532 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1534 /* No list for this group name exists yet, create one */
1536 l
= new0(char *, 2);
1540 l
[0] = resolved_name
;
1543 r
= hashmap_put(members
, resolved_id
, l
);
1549 resolved_id
= resolved_name
= NULL
;
1557 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1561 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1570 if (path_is_absolute(resolved_id
)) {
1571 i
->uid_path
= resolved_id
;
1574 path_kill_slashes(i
->uid_path
);
1576 r
= parse_uid(resolved_id
, &i
->uid
);
1578 log_error("Failed to parse UID: %s", id
);
1586 i
->description
= description
;
1597 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1602 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1607 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1611 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1620 if (path_is_absolute(resolved_id
)) {
1621 i
->gid_path
= resolved_id
;
1624 path_kill_slashes(i
->gid_path
);
1626 r
= parse_gid(resolved_id
, &i
->gid
);
1628 log_error("Failed to parse GID: %s", id
);
1643 i
->type
= action
[0];
1644 i
->name
= resolved_name
;
1645 resolved_name
= NULL
;
1647 existing
= hashmap_get(h
, i
->name
);
1650 /* Two identical items are fine */
1651 if (!item_equal(existing
, i
))
1652 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1657 r
= hashmap_put(h
, i
->name
, i
);
1665 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1666 _cleanup_fclose_
FILE *rf
= NULL
;
1668 char line
[LINE_MAX
];
1677 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1679 if (ignore_enoent
&& r
== -ENOENT
)
1682 return log_error_errno(r
, "Failed to open '%s', ignoring: %m", fn
);
1688 FOREACH_LINE(line
, f
, break) {
1695 if (*l
== '#' || *l
== 0)
1698 k
= parse_line(fn
, v
, l
);
1699 if (k
< 0 && r
== 0)
1704 log_error_errno(errno
, "Failed to read from file %s: %m", fn
);
1712 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1716 name
= hashmap_first(by_id
);
1720 hashmap_remove(by_name
, name
);
1722 hashmap_steal_first_key(by_id
);
1726 while ((name
= hashmap_steal_first_key(by_name
)))
1729 hashmap_free(by_name
);
1730 hashmap_free(by_id
);
1733 static void help(void) {
1734 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1735 "Creates system user accounts.\n\n"
1736 " -h --help Show this help\n"
1737 " --version Show package version\n"
1738 " --root=PATH Operate on an alternate filesystem root\n"
1739 , program_invocation_short_name
);
1742 static int parse_argv(int argc
, char *argv
[]) {
1745 ARG_VERSION
= 0x100,
1749 static const struct option options
[] = {
1750 { "help", no_argument
, NULL
, 'h' },
1751 { "version", no_argument
, NULL
, ARG_VERSION
},
1752 { "root", required_argument
, NULL
, ARG_ROOT
},
1761 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1770 puts(PACKAGE_STRING
);
1771 puts(SYSTEMD_FEATURES
);
1776 arg_root
= path_make_absolute_cwd(optarg
);
1780 path_kill_slashes(arg_root
);
1787 assert_not_reached("Unhandled option");
1793 int main(int argc
, char *argv
[]) {
1795 _cleanup_close_
int lock
= -1;
1801 r
= parse_argv(argc
, argv
);
1805 log_set_target(LOG_TARGET_AUTO
);
1806 log_parse_environment();
1811 r
= mac_selinux_init(NULL
);
1813 log_error_errno(r
, "SELinux setup failed: %m");
1817 if (optind
< argc
) {
1820 for (j
= optind
; j
< argc
; j
++) {
1821 k
= read_config_file(argv
[j
], false);
1822 if (k
< 0 && r
== 0)
1826 _cleanup_strv_free_
char **files
= NULL
;
1829 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1831 log_error_errno(r
, "Failed to enumerate sysusers.d files: %m");
1835 STRV_FOREACH(f
, files
) {
1836 k
= read_config_file(*f
, true);
1837 if (k
< 0 && r
== 0)
1843 /* Default to default range of 1..SYSTEMD_UID_MAX */
1844 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1855 lock
= take_password_lock(arg_root
);
1857 log_error_errno(lock
, "Failed to take lock: %m");
1861 r
= load_user_database();
1863 log_error_errno(r
, "Failed to load user database: %m");
1867 r
= load_group_database();
1869 log_error_errno(r
, "Failed to read group database: %m");
1873 HASHMAP_FOREACH(i
, groups
, iterator
)
1876 HASHMAP_FOREACH(i
, users
, iterator
)
1881 log_error_errno(r
, "Failed to write files: %m");
1884 while ((i
= hashmap_steal_first(groups
)))
1887 while ((i
= hashmap_steal_first(users
)))
1890 while ((n
= hashmap_first_key(members
))) {
1891 strv_free(hashmap_steal_first(members
));
1895 hashmap_free(groups
);
1896 hashmap_free(users
);
1897 hashmap_free(members
);
1898 hashmap_free(todo_uids
);
1899 hashmap_free(todo_gids
);
1901 free_database(database_user
, database_uid
);
1902 free_database(database_group
, database_gid
);
1906 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;