1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/types.h>
32 #include "specifier.h"
33 #include "path-util.h"
36 #include "conf-files.h"
40 #include "fileio-label.h"
41 #include "uid-range.h"
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_t
) -1;
80 static UidRange
*uid_range
= NULL
;
81 static unsigned n_uid_range
= 0;
83 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
84 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
86 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
87 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
89 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
91 static int load_user_database(void) {
92 _cleanup_fclose_
FILE *f
= NULL
;
93 const char *passwd_path
;
97 passwd_path
= fix_root("/etc/passwd");
98 f
= fopen(passwd_path
, "re");
100 return errno
== ENOENT
? 0 : -errno
;
102 r
= hashmap_ensure_allocated(&database_user
, &string_hash_ops
);
106 r
= hashmap_ensure_allocated(&database_uid
, NULL
);
111 while ((pw
= fgetpwent(f
))) {
115 n
= strdup(pw
->pw_name
);
119 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
120 if (k
< 0 && k
!= -EEXIST
) {
125 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
126 if (q
< 0 && q
!= -EEXIST
) {
137 if (!IN_SET(errno
, 0, ENOENT
))
143 static int load_group_database(void) {
144 _cleanup_fclose_
FILE *f
= NULL
;
145 const char *group_path
;
149 group_path
= fix_root("/etc/group");
150 f
= fopen(group_path
, "re");
152 return errno
== ENOENT
? 0 : -errno
;
154 r
= hashmap_ensure_allocated(&database_group
, &string_hash_ops
);
158 r
= hashmap_ensure_allocated(&database_gid
, NULL
);
163 while ((gr
= fgetgrent(f
))) {
167 n
= strdup(gr
->gr_name
);
171 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
172 if (k
< 0 && k
!= -EEXIST
) {
177 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
178 if (q
< 0 && q
!= -EEXIST
) {
189 if (!IN_SET(errno
, 0, ENOENT
))
195 static int make_backup(const char *target
, const char *x
) {
196 _cleanup_close_
int src
= -1;
197 _cleanup_fclose_
FILE *dst
= NULL
;
199 struct timespec ts
[2];
203 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
205 if (errno
== ENOENT
) /* No backup necessary... */
211 if (fstat(src
, &st
) < 0)
214 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
218 r
= copy_bytes(src
, fileno(dst
), (off_t
) -1);
222 /* Don't fail on chmod() or chown(). If it stays owned by us
223 * and/or unreadable by others, then it isn't too bad... */
225 backup
= strappenda(x
, "-");
227 /* Copy over the access mask */
228 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
229 log_warning("Failed to change mode on %s: %m", backup
);
231 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
232 log_warning("Failed to change ownership of %s: %m", backup
);
236 if (futimens(fileno(dst
), ts
) < 0)
237 log_warning("Failed to fix access and modification time of %s: %m", backup
);
239 if (rename(temp
, backup
) < 0)
249 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
255 a
= hashmap_get(members
, gr
->gr_name
);
257 _cleanup_strv_free_
char **l
= NULL
;
261 l
= strv_copy(gr
->gr_mem
);
266 if (strv_find(l
, *i
))
269 if (strv_extend(&l
, *i
) < 0)
285 if (putgrent(&t
, group
) != 0)
286 return errno
? -errno
: -EIO
;
293 if (putgrent(gr
, group
) != 0)
294 return errno
? -errno
: -EIO
;
299 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
305 a
= hashmap_get(members
, sg
->sg_namp
);
307 _cleanup_strv_free_
char **l
= NULL
;
311 l
= strv_copy(sg
->sg_mem
);
316 if (strv_find(l
, *i
))
319 if (strv_extend(&l
, *i
) < 0)
335 if (putsgent(&t
, gshadow
) != 0)
336 return errno
? -errno
: -EIO
;
343 if (putsgent(sg
, gshadow
) != 0)
344 return errno
? -errno
: -EIO
;
349 static int sync_rights(FILE *from
, FILE *to
) {
352 if (fstat(fileno(from
), &st
) < 0)
355 if (fchmod(fileno(to
), st
.st_mode
& 07777) < 0)
358 if (fchown(fileno(to
), st
.st_uid
, st
.st_gid
) < 0)
364 static int write_files(void) {
366 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
367 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
368 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
369 bool group_changed
= false;
374 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
375 _cleanup_fclose_
FILE *original
= NULL
;
377 /* First we update the actual group list file */
378 group_path
= fix_root("/etc/group");
379 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
383 original
= fopen(group_path
, "re");
387 r
= sync_rights(original
, group
);
392 while ((gr
= fgetgrent(original
))) {
393 /* Safety checks against name and GID
394 * collisions. Normally, this should
395 * be unnecessary, but given that we
396 * look at the entries anyway here,
397 * let's make an extra verification
398 * step that we don't generate
399 * duplicate entries. */
401 i
= hashmap_get(groups
, gr
->gr_name
);
402 if (i
&& i
->todo_group
) {
407 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
412 r
= putgrent_with_members(gr
, group
);
416 group_changed
= true;
420 if (!IN_SET(errno
, 0, ENOENT
)) {
425 } else if (errno
!= ENOENT
) {
428 } else if (fchmod(fileno(group
), 0644) < 0) {
433 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
437 .gr_passwd
= (char*) "x",
440 r
= putgrent_with_members(&n
, group
);
444 group_changed
= true;
447 r
= fflush_and_check(group
);
456 /* OK, now also update the shadow file for the group list */
457 gshadow_path
= fix_root("/etc/gshadow");
458 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
462 original
= fopen(gshadow_path
, "re");
466 r
= sync_rights(original
, gshadow
);
471 while ((sg
= fgetsgent(original
))) {
473 i
= hashmap_get(groups
, sg
->sg_namp
);
474 if (i
&& i
->todo_group
) {
479 r
= putsgent_with_members(sg
, gshadow
);
483 group_changed
= true;
487 if (!IN_SET(errno
, 0, ENOENT
)) {
492 } else if (errno
!= ENOENT
) {
495 } else if (fchmod(fileno(gshadow
), 0000) < 0) {
500 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
503 .sg_passwd
= (char*) "!!",
506 r
= putsgent_with_members(&n
, gshadow
);
510 group_changed
= true;
513 r
= fflush_and_check(gshadow
);
518 if (hashmap_size(todo_uids
) > 0) {
519 _cleanup_fclose_
FILE *original
= NULL
;
522 /* First we update the user database itself */
523 passwd_path
= fix_root("/etc/passwd");
524 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
528 original
= fopen(passwd_path
, "re");
532 r
= sync_rights(original
, passwd
);
537 while ((pw
= fgetpwent(original
))) {
539 i
= hashmap_get(users
, pw
->pw_name
);
540 if (i
&& i
->todo_user
) {
545 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
551 if (putpwent(pw
, passwd
) < 0) {
552 r
= errno
? -errno
: -EIO
;
558 if (!IN_SET(errno
, 0, ENOENT
)) {
563 } else if (errno
!= ENOENT
) {
566 } else if (fchmod(fileno(passwd
), 0644) < 0) {
571 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
576 .pw_gecos
= i
->description
,
578 /* "x" means the password is stored in
580 .pw_passwd
= (char*) "x",
582 /* We default to the root directory as home */
583 .pw_dir
= i
->home
? i
->home
: (char*) "/",
585 /* Initialize the shell to nologin,
586 * with one exception: for root we
587 * patch in something special */
588 .pw_shell
= i
->uid
== 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
592 if (putpwent(&n
, passwd
) != 0) {
593 r
= errno
? -errno
: -EIO
;
598 r
= fflush_and_check(passwd
);
607 /* The we update the shadow database */
608 shadow_path
= fix_root("/etc/shadow");
609 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
613 original
= fopen(shadow_path
, "re");
617 r
= sync_rights(original
, shadow
);
622 while ((sp
= fgetspent(original
))) {
624 i
= hashmap_get(users
, sp
->sp_namp
);
625 if (i
&& i
->todo_user
) {
631 if (putspent(sp
, shadow
) < 0) {
632 r
= errno
? -errno
: -EIO
;
638 if (!IN_SET(errno
, 0, ENOENT
)) {
642 } else if (errno
!= ENOENT
) {
645 } else if (fchmod(fileno(shadow
), 0000) < 0) {
650 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
651 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
654 .sp_pwdp
= (char*) "!!",
661 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
665 if (putspent(&n
, shadow
) != 0) {
666 r
= errno
? -errno
: -EIO
;
671 r
= fflush_and_check(shadow
);
676 /* Make a backup of the old files */
679 r
= make_backup("/etc/group", group_path
);
684 r
= make_backup("/etc/gshadow", gshadow_path
);
691 r
= make_backup("/etc/passwd", passwd_path
);
696 r
= make_backup("/etc/shadow", shadow_path
);
701 /* And make the new files count */
704 if (rename(group_tmp
, group_path
) < 0) {
713 if (rename(gshadow_tmp
, gshadow_path
) < 0) {
724 if (rename(passwd_tmp
, passwd_path
) < 0) {
733 if (rename(shadow_tmp
, shadow_path
) < 0) {
757 static int uid_is_ok(uid_t uid
, const char *name
) {
763 /* Let's see if we already have assigned the UID a second time */
764 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
767 /* Try to avoid using uids that are already used by a group
768 * that doesn't have the same name as our new user. */
769 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
770 if (i
&& !streq(i
->name
, name
))
773 /* Let's check the files directly */
774 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
777 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
778 if (n
&& !streq(n
, name
))
781 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
787 if (!IN_SET(errno
, 0, ENOENT
))
791 g
= getgrgid((gid_t
) uid
);
793 if (!streq(g
->gr_name
, name
))
795 } else if (!IN_SET(errno
, 0, ENOENT
))
802 static int root_stat(const char *p
, struct stat
*st
) {
806 if (stat(fix
, st
) < 0)
812 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
814 bool found_uid
= false, found_gid
= false;
820 /* First, try to get the gid directly */
821 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
826 /* Then, try to get the uid directly */
827 if ((_uid
|| (_gid
&& !found_gid
))
829 && root_stat(i
->uid_path
, &st
) >= 0) {
834 /* If we need the gid, but had no success yet, also derive it from the uid path */
835 if (_gid
&& !found_gid
) {
841 /* If that didn't work yet, then let's reuse the gid as uid */
842 if (_uid
&& !found_uid
&& i
->gid_path
) {
847 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
848 uid
= (uid_t
) st
.st_gid
;
870 static int add_user(Item
*i
) {
876 /* Check the database directly */
877 z
= hashmap_get(database_user
, i
->name
);
879 log_debug("User %s already exists.", i
->name
);
880 i
->uid
= PTR_TO_UID(z
);
891 p
= getpwnam(i
->name
);
893 log_debug("User %s already exists.", i
->name
);
897 free(i
->description
);
898 i
->description
= strdup(p
->pw_gecos
);
901 if (!IN_SET(errno
, 0, ENOENT
)) {
902 log_error("Failed to check if user %s already exists: %m", i
->name
);
906 /* And shadow too, just to be sure */
908 sp
= getspnam(i
->name
);
910 log_error("User %s already exists in shadow database, but not in user database.", i
->name
);
913 if (!IN_SET(errno
, 0, ENOENT
)) {
914 log_error("Failed to check if user %s already exists in shadow database: %m", i
->name
);
919 /* Try to use the suggested numeric uid */
921 r
= uid_is_ok(i
->uid
, i
->name
);
923 log_error_errno(-r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
927 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
932 /* If that didn't work, try to read it from the specified path */
936 if (read_id_from_file(i
, &c
, NULL
) > 0) {
938 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
939 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
941 r
= uid_is_ok(c
, i
->name
);
943 log_error_errno(-r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
949 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
954 /* Otherwise try to reuse the group ID */
955 if (!i
->uid_set
&& i
->gid_set
) {
956 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
958 log_error_errno(-r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
962 i
->uid
= (uid_t
) i
->gid
;
967 /* And if that didn't work either, let's try to find a free one */
970 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
972 log_error("No free user ID available for %s.", i
->name
);
976 r
= uid_is_ok(search_uid
, i
->name
);
978 log_error_errno(-r
, "Failed to verify uid " UID_FMT
": %m", i
->uid
);
988 r
= hashmap_ensure_allocated(&todo_uids
, NULL
);
992 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
997 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
1002 static int gid_is_ok(gid_t gid
) {
1006 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
1009 /* Avoid reusing gids that are already used by a different user */
1010 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
1013 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
1016 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1024 if (!IN_SET(errno
, 0, ENOENT
))
1028 p
= getpwuid((uid_t
) gid
);
1031 if (!IN_SET(errno
, 0, ENOENT
))
1038 static int add_group(Item
*i
) {
1044 /* Check the database directly */
1045 z
= hashmap_get(database_group
, i
->name
);
1047 log_debug("Group %s already exists.", i
->name
);
1048 i
->gid
= PTR_TO_GID(z
);
1053 /* Also check NSS */
1058 g
= getgrnam(i
->name
);
1060 log_debug("Group %s already exists.", i
->name
);
1065 if (!IN_SET(errno
, 0, ENOENT
)) {
1066 log_error("Failed to check if group %s already exists: %m", i
->name
);
1071 /* Try to use the suggested numeric gid */
1073 r
= gid_is_ok(i
->gid
);
1075 log_error_errno(-r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1079 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1084 /* Try to reuse the numeric uid, if there's one */
1085 if (!i
->gid_set
&& i
->uid_set
) {
1086 r
= gid_is_ok((gid_t
) i
->uid
);
1088 log_error_errno(-r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1092 i
->gid
= (gid_t
) i
->uid
;
1097 /* If that didn't work, try to read it from the specified path */
1101 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1103 if (c
<= 0 || !uid_range_contains(uid_range
, n_uid_range
, c
))
1104 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1108 log_error_errno(-r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1114 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1119 /* And if that didn't work either, let's try to find a free one */
1122 /* We look for new GIDs in the UID pool! */
1123 r
= uid_range_next_lower(uid_range
, n_uid_range
, &search_uid
);
1125 log_error("No free group ID available for %s.", i
->name
);
1129 r
= gid_is_ok(search_uid
);
1131 log_error_errno(-r
, "Failed to verify gid " GID_FMT
": %m", i
->gid
);
1138 i
->gid
= search_uid
;
1141 r
= hashmap_ensure_allocated(&todo_gids
, NULL
);
1145 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1149 i
->todo_group
= true;
1150 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1155 static int process_item(Item
*i
) {
1172 j
= hashmap_get(users
, i
->name
);
1174 /* There's already user to be created for this
1175 * name, let's process that in one step */
1184 j
->gid_path
= strdup(i
->gid_path
);
1192 return add_group(i
);
1196 assert_not_reached("Unknown item type");
1200 static void item_free(Item
*i
) {
1208 free(i
->description
);
1212 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1214 static int add_implicit(void) {
1219 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1221 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1225 i
= hashmap_get(groups
, g
);
1227 _cleanup_(item_freep
) Item
*j
= NULL
;
1229 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1237 j
->type
= ADD_GROUP
;
1238 j
->name
= strdup(g
);
1242 r
= hashmap_put(groups
, j
->name
, j
);
1246 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1250 STRV_FOREACH(m
, l
) {
1252 i
= hashmap_get(users
, *m
);
1254 _cleanup_(item_freep
) Item
*j
= NULL
;
1256 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1265 j
->name
= strdup(*m
);
1269 r
= hashmap_put(users
, j
->name
, j
);
1273 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1282 static bool item_equal(Item
*a
, Item
*b
) {
1286 if (a
->type
!= b
->type
)
1289 if (!streq_ptr(a
->name
, b
->name
))
1292 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1295 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1298 if (!streq_ptr(a
->description
, b
->description
))
1301 if (a
->uid_set
!= b
->uid_set
)
1304 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1307 if (a
->gid_set
!= b
->gid_set
)
1310 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1313 if (!streq_ptr(a
->home
, b
->home
))
1319 static bool valid_user_group_name(const char *u
) {
1326 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1327 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1331 for (i
= u
+1; *i
; i
++) {
1332 if (!(*i
>= 'a' && *i
<= 'z') &&
1333 !(*i
>= 'A' && *i
<= 'Z') &&
1334 !(*i
>= '0' && *i
<= '9') &&
1340 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1343 if ((size_t) (i
-u
) > (size_t) sz
)
1346 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1352 static bool valid_gecos(const char *d
) {
1357 if (!utf8_is_valid(d
))
1360 if (string_has_cc(d
, NULL
))
1363 /* Colons are used as field separators, and hence not OK */
1370 static bool valid_home(const char *p
) {
1375 if (!utf8_is_valid(p
))
1378 if (string_has_cc(p
, NULL
))
1381 if (!path_is_absolute(p
))
1384 if (!path_is_safe(p
))
1387 /* Colons are used as field separators, and hence not OK */
1394 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1396 static const Specifier specifier_table
[] = {
1397 { 'm', specifier_machine_id
, NULL
},
1398 { 'b', specifier_boot_id
, NULL
},
1399 { 'H', specifier_host_name
, NULL
},
1400 { 'v', specifier_kernel_release
, NULL
},
1404 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
, *resolved_id
= NULL
, *description
= NULL
, *home
= NULL
;
1405 _cleanup_(item_freep
) Item
*i
= NULL
;
1417 r
= unquote_many_words(&p
, &action
, &name
, &id
, &description
, &home
, NULL
);
1419 log_error("[%s:%u] Syntax error.", fname
, line
);
1423 log_error("[%s:%u] Missing action and name columns.", fname
, line
);
1427 log_error("[%s:%u] Trailing garbage.", fname
, line
);
1432 if (strlen(action
) != 1) {
1433 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1437 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
, ADD_RANGE
)) {
1438 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1443 if (isempty(name
) || streq(name
, "-")) {
1449 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1451 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1455 if (!valid_user_group_name(resolved_name
)) {
1456 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1462 if (isempty(id
) || streq(id
, "-")) {
1468 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1470 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1475 /* Verify description */
1476 if (isempty(description
) || streq(description
, "-")) {
1482 if (!valid_gecos(description
)) {
1483 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, description
);
1489 if (isempty(home
) || streq(home
, "-")) {
1495 if (!valid_home(home
)) {
1496 log_error("[%s:%u] '%s' is not a valid home directory field.", fname
, line
, home
);
1501 switch (action
[0]) {
1504 if (resolved_name
) {
1505 log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname
, line
);
1510 log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname
, line
);
1515 log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname
, line
);
1520 log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname
, line
);
1524 r
= uid_range_add_str(&uid_range
, &n_uid_range
, resolved_id
);
1526 log_error("[%s:%u] Invalid UID range %s.", fname
, line
, resolved_id
);
1535 /* Try to extend an existing member or group item */
1537 log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname
, line
);
1542 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1546 if (!valid_user_group_name(resolved_id
)) {
1547 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1552 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1557 log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname
, line
);
1561 r
= hashmap_ensure_allocated(&members
, &string_hash_ops
);
1565 l
= hashmap_get(members
, resolved_id
);
1567 /* A list for this group name already exists, let's append to it */
1568 r
= strv_push(&l
, resolved_name
);
1572 resolved_name
= NULL
;
1574 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1576 /* No list for this group name exists yet, create one */
1578 l
= new0(char *, 2);
1582 l
[0] = resolved_name
;
1585 r
= hashmap_put(members
, resolved_id
, l
);
1591 resolved_id
= resolved_name
= NULL
;
1599 log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname
, line
);
1603 r
= hashmap_ensure_allocated(&users
, &string_hash_ops
);
1612 if (path_is_absolute(resolved_id
)) {
1613 i
->uid_path
= resolved_id
;
1616 path_kill_slashes(i
->uid_path
);
1618 r
= parse_uid(resolved_id
, &i
->uid
);
1620 log_error("Failed to parse UID: %s", id
);
1628 i
->description
= description
;
1639 log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname
, line
);
1644 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1649 log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname
, line
);
1653 r
= hashmap_ensure_allocated(&groups
, &string_hash_ops
);
1662 if (path_is_absolute(resolved_id
)) {
1663 i
->gid_path
= resolved_id
;
1666 path_kill_slashes(i
->gid_path
);
1668 r
= parse_gid(resolved_id
, &i
->gid
);
1670 log_error("Failed to parse GID: %s", id
);
1685 i
->type
= action
[0];
1686 i
->name
= resolved_name
;
1687 resolved_name
= NULL
;
1689 existing
= hashmap_get(h
, i
->name
);
1692 /* Two identical items are fine */
1693 if (!item_equal(existing
, i
))
1694 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1699 r
= hashmap_put(h
, i
->name
, i
);
1707 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1708 _cleanup_fclose_
FILE *rf
= NULL
;
1710 char line
[LINE_MAX
];
1719 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1721 if (ignore_enoent
&& r
== -ENOENT
)
1724 log_error_errno(-r
, "Failed to open '%s', ignoring: %m", fn
);
1731 FOREACH_LINE(line
, f
, break) {
1738 if (*l
== '#' || *l
== 0)
1741 k
= parse_line(fn
, v
, l
);
1742 if (k
< 0 && r
== 0)
1747 log_error("Failed to read from file %s: %m", fn
);
1755 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1759 name
= hashmap_first(by_id
);
1763 hashmap_remove(by_name
, name
);
1765 hashmap_steal_first_key(by_id
);
1769 while ((name
= hashmap_steal_first_key(by_name
)))
1772 hashmap_free(by_name
);
1773 hashmap_free(by_id
);
1776 static void help(void) {
1777 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1778 "Creates system user accounts.\n\n"
1779 " -h --help Show this help\n"
1780 " --version Show package version\n"
1781 " --root=PATH Operate on an alternate filesystem root\n"
1782 , program_invocation_short_name
);
1785 static int parse_argv(int argc
, char *argv
[]) {
1788 ARG_VERSION
= 0x100,
1792 static const struct option options
[] = {
1793 { "help", no_argument
, NULL
, 'h' },
1794 { "version", no_argument
, NULL
, ARG_VERSION
},
1795 { "root", required_argument
, NULL
, ARG_ROOT
},
1804 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1813 puts(PACKAGE_STRING
);
1814 puts(SYSTEMD_FEATURES
);
1819 arg_root
= path_make_absolute_cwd(optarg
);
1823 path_kill_slashes(arg_root
);
1830 assert_not_reached("Unhandled option");
1836 int main(int argc
, char *argv
[]) {
1838 _cleanup_close_
int lock
= -1;
1844 r
= parse_argv(argc
, argv
);
1848 log_set_target(LOG_TARGET_AUTO
);
1849 log_parse_environment();
1854 r
= mac_selinux_init(NULL
);
1856 log_error_errno(-r
, "SELinux setup failed: %m");
1860 if (optind
< argc
) {
1863 for (j
= optind
; j
< argc
; j
++) {
1864 k
= read_config_file(argv
[j
], false);
1865 if (k
< 0 && r
== 0)
1869 _cleanup_strv_free_
char **files
= NULL
;
1872 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1874 log_error_errno(-r
, "Failed to enumerate sysusers.d files: %m");
1878 STRV_FOREACH(f
, files
) {
1879 k
= read_config_file(*f
, true);
1880 if (k
< 0 && r
== 0)
1886 /* Default to default range of 1..SYSTEMD_UID_MAX */
1887 r
= uid_range_add(&uid_range
, &n_uid_range
, 1, SYSTEM_UID_MAX
);
1898 lock
= take_password_lock(arg_root
);
1900 log_error_errno(-lock
, "Failed to take lock: %m");
1904 r
= load_user_database();
1906 log_error_errno(-r
, "Failed to load user database: %m");
1910 r
= load_group_database();
1912 log_error_errno(-r
, "Failed to read group database: %m");
1916 HASHMAP_FOREACH(i
, groups
, iterator
)
1919 HASHMAP_FOREACH(i
, users
, iterator
)
1924 log_error_errno(-r
, "Failed to write files: %m");
1927 while ((i
= hashmap_steal_first(groups
)))
1930 while ((i
= hashmap_steal_first(users
)))
1933 while ((n
= hashmap_first_key(members
))) {
1934 strv_free(hashmap_steal_first(members
));
1938 hashmap_free(groups
);
1939 hashmap_free(users
);
1940 hashmap_free(members
);
1941 hashmap_free(todo_uids
);
1942 hashmap_free(todo_gids
);
1944 free_database(database_user
, database_uid
);
1945 free_database(database_group
, database_gid
);
1949 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;