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"
42 typedef enum ItemType
{
65 static char *arg_root
= NULL
;
67 static const char conf_file_dirs
[] =
70 "/usr/local/lib/sysusers.d\0"
71 "/usr/lib/sysusers.d\0"
77 static Hashmap
*users
= NULL
, *groups
= NULL
;
78 static Hashmap
*todo_uids
= NULL
, *todo_gids
= NULL
;
79 static Hashmap
*members
= NULL
;
81 static Hashmap
*database_uid
= NULL
, *database_user
= NULL
;
82 static Hashmap
*database_gid
= NULL
, *database_group
= NULL
;
84 static uid_t search_uid
= SYSTEM_UID_MAX
;
85 static gid_t search_gid
= SYSTEM_GID_MAX
;
87 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
88 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
90 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
91 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
93 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
95 static int load_user_database(void) {
96 _cleanup_fclose_
FILE *f
= NULL
;
97 const char *passwd_path
;
101 passwd_path
= fix_root("/etc/passwd");
102 f
= fopen(passwd_path
, "re");
104 return errno
== ENOENT
? 0 : -errno
;
106 r
= hashmap_ensure_allocated(&database_user
, string_hash_func
, string_compare_func
);
110 r
= hashmap_ensure_allocated(&database_uid
, trivial_hash_func
, trivial_compare_func
);
115 while ((pw
= fgetpwent(f
))) {
119 n
= strdup(pw
->pw_name
);
123 k
= hashmap_put(database_user
, n
, UID_TO_PTR(pw
->pw_uid
));
124 if (k
< 0 && k
!= -EEXIST
) {
129 q
= hashmap_put(database_uid
, UID_TO_PTR(pw
->pw_uid
), n
);
130 if (q
< 0 && q
!= -EEXIST
) {
141 if (!IN_SET(errno
, 0, ENOENT
))
147 static int load_group_database(void) {
148 _cleanup_fclose_
FILE *f
= NULL
;
149 const char *group_path
;
153 group_path
= fix_root("/etc/group");
154 f
= fopen(group_path
, "re");
156 return errno
== ENOENT
? 0 : -errno
;
158 r
= hashmap_ensure_allocated(&database_group
, string_hash_func
, string_compare_func
);
162 r
= hashmap_ensure_allocated(&database_gid
, trivial_hash_func
, trivial_compare_func
);
167 while ((gr
= fgetgrent(f
))) {
171 n
= strdup(gr
->gr_name
);
175 k
= hashmap_put(database_group
, n
, GID_TO_PTR(gr
->gr_gid
));
176 if (k
< 0 && k
!= -EEXIST
) {
181 q
= hashmap_put(database_gid
, GID_TO_PTR(gr
->gr_gid
), n
);
182 if (q
< 0 && q
!= -EEXIST
) {
193 if (!IN_SET(errno
, 0, ENOENT
))
199 static int make_backup(const char *target
, const char *x
) {
200 _cleanup_close_
int src
= -1;
201 _cleanup_fclose_
FILE *dst
= NULL
;
203 struct timespec ts
[2];
207 src
= open(x
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
209 if (errno
== ENOENT
) /* No backup necessary... */
215 if (fstat(src
, &st
) < 0)
218 r
= fopen_temporary_label(target
, x
, &dst
, &temp
);
222 r
= copy_bytes(src
, fileno(dst
), (off_t
) -1);
226 /* Don't fail on chmod() or chown(). If it stays owned by us
227 * and/or unreadable by others, then it isn't too bad... */
229 backup
= strappenda(x
, "-");
231 /* Copy over the access mask */
232 if (fchmod(fileno(dst
), st
.st_mode
& 07777) < 0)
233 log_warning("Failed to change mode on %s: %m", backup
);
235 if (fchown(fileno(dst
), st
.st_uid
, st
.st_gid
)< 0)
236 log_warning("Failed to change ownership of %s: %m", backup
);
240 futimens(fileno(dst
), ts
);
242 if (rename(temp
, backup
) < 0)
252 static int putgrent_with_members(const struct group
*gr
, FILE *group
) {
258 a
= hashmap_get(members
, gr
->gr_name
);
260 _cleanup_strv_free_
char **l
= NULL
;
264 l
= strv_copy(gr
->gr_mem
);
269 if (strv_find(l
, *i
))
272 if (strv_extend(&l
, *i
) < 0)
288 if (putgrent(&t
, group
) != 0)
289 return errno
? -errno
: -EIO
;
296 if (putgrent(gr
, group
) != 0)
297 return errno
? -errno
: -EIO
;
302 static int putsgent_with_members(const struct sgrp
*sg
, FILE *gshadow
) {
308 a
= hashmap_get(members
, sg
->sg_namp
);
310 _cleanup_strv_free_
char **l
= NULL
;
314 l
= strv_copy(sg
->sg_mem
);
319 if (strv_find(l
, *i
))
322 if (strv_extend(&l
, *i
) < 0)
338 if (putsgent(&t
, gshadow
) != 0)
339 return errno
? -errno
: -EIO
;
346 if (putsgent(sg
, gshadow
) != 0)
347 return errno
? -errno
: -EIO
;
352 static int write_files(void) {
354 _cleanup_fclose_
FILE *passwd
= NULL
, *group
= NULL
, *shadow
= NULL
, *gshadow
= NULL
;
355 _cleanup_free_
char *passwd_tmp
= NULL
, *group_tmp
= NULL
, *shadow_tmp
= NULL
, *gshadow_tmp
= NULL
;
356 const char *passwd_path
= NULL
, *group_path
= NULL
, *shadow_path
= NULL
, *gshadow_path
= NULL
;
357 bool group_changed
= false;
362 if (hashmap_size(todo_gids
) > 0 || hashmap_size(members
) > 0) {
363 _cleanup_fclose_
FILE *original
= NULL
;
365 /* First we update the actual group list file */
366 group_path
= fix_root("/etc/group");
367 r
= fopen_temporary_label("/etc/group", group_path
, &group
, &group_tmp
);
371 if (fchmod(fileno(group
), 0644) < 0) {
376 original
= fopen(group_path
, "re");
381 while ((gr
= fgetgrent(original
))) {
382 /* Safety checks against name and GID
383 * collisions. Normally, this should
384 * be unnecessary, but given that we
385 * look at the entries anyway here,
386 * let's make an extra verification
387 * step that we don't generate
388 * duplicate entries. */
390 i
= hashmap_get(groups
, gr
->gr_name
);
391 if (i
&& i
->todo_group
) {
396 if (hashmap_contains(todo_gids
, GID_TO_PTR(gr
->gr_gid
))) {
401 r
= putgrent_with_members(gr
, group
);
405 group_changed
= true;
409 if (!IN_SET(errno
, 0, ENOENT
)) {
414 } else if (errno
!= ENOENT
) {
419 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
423 .gr_passwd
= (char*) "x",
426 r
= putgrent_with_members(&n
, group
);
430 group_changed
= true;
433 r
= fflush_and_check(group
);
442 /* OK, now also update the shadow file for the group list */
443 gshadow_path
= fix_root("/etc/gshadow");
444 r
= fopen_temporary_label("/etc/gshadow", gshadow_path
, &gshadow
, &gshadow_tmp
);
448 if (fchmod(fileno(gshadow
), 0000) < 0) {
453 original
= fopen(gshadow_path
, "re");
458 while ((sg
= fgetsgent(original
))) {
460 i
= hashmap_get(groups
, sg
->sg_namp
);
461 if (i
&& i
->todo_group
) {
466 r
= putsgent_with_members(sg
, gshadow
);
470 group_changed
= true;
474 if (!IN_SET(errno
, 0, ENOENT
)) {
479 } else if (errno
!= ENOENT
) {
484 HASHMAP_FOREACH(i
, todo_gids
, iterator
) {
487 .sg_passwd
= (char*) "!!",
490 r
= putsgent_with_members(&n
, gshadow
);
494 group_changed
= true;
497 r
= fflush_and_check(gshadow
);
502 if (hashmap_size(todo_uids
) > 0) {
503 _cleanup_fclose_
FILE *original
= NULL
;
506 /* First we update the user database itself */
507 passwd_path
= fix_root("/etc/passwd");
508 r
= fopen_temporary_label("/etc/passwd", passwd_path
, &passwd
, &passwd_tmp
);
512 if (fchmod(fileno(passwd
), 0644) < 0) {
517 original
= fopen(passwd_path
, "re");
522 while ((pw
= fgetpwent(original
))) {
524 i
= hashmap_get(users
, pw
->pw_name
);
525 if (i
&& i
->todo_user
) {
530 if (hashmap_contains(todo_uids
, UID_TO_PTR(pw
->pw_uid
))) {
536 if (putpwent(pw
, passwd
) < 0) {
537 r
= errno
? -errno
: -EIO
;
543 if (!IN_SET(errno
, 0, ENOENT
)) {
548 } else if (errno
!= ENOENT
) {
553 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
558 .pw_gecos
= i
->description
,
559 .pw_passwd
= (char*) "x",
562 /* Initialize the home directory and the shell
563 * to nologin, with one exception: for root we
564 * patch in something special */
566 n
.pw_shell
= (char*) "/bin/sh";
567 n
.pw_dir
= (char*) "/root";
569 n
.pw_shell
= (char*) "/sbin/nologin";
570 n
.pw_dir
= (char*) "/";
574 if (putpwent(&n
, passwd
) != 0) {
575 r
= errno
? -errno
: -EIO
;
580 r
= fflush_and_check(passwd
);
589 /* The we update the shadow database */
590 shadow_path
= fix_root("/etc/shadow");
591 r
= fopen_temporary_label("/etc/shadow", shadow_path
, &shadow
, &shadow_tmp
);
595 if (fchmod(fileno(shadow
), 0000) < 0) {
600 original
= fopen(shadow_path
, "re");
605 while ((sp
= fgetspent(original
))) {
607 i
= hashmap_get(users
, sp
->sp_namp
);
608 if (i
&& i
->todo_user
) {
614 if (putspent(sp
, shadow
) < 0) {
615 r
= errno
? -errno
: -EIO
;
621 if (!IN_SET(errno
, 0, ENOENT
)) {
625 } else if (errno
!= ENOENT
) {
630 lstchg
= (long) (now(CLOCK_REALTIME
) / USEC_PER_DAY
);
631 HASHMAP_FOREACH(i
, todo_uids
, iterator
) {
634 .sp_pwdp
= (char*) "!!",
641 .sp_flag
= (unsigned long) -1, /* this appears to be what everybody does ... */
645 if (putspent(&n
, shadow
) != 0) {
646 r
= errno
? -errno
: -EIO
;
651 r
= fflush_and_check(shadow
);
656 /* Make a backup of the old files */
659 r
= make_backup("/etc/group", group_path
);
664 r
= make_backup("/etc/gshadow", gshadow_path
);
671 r
= make_backup("/etc/passwd", passwd_path
);
676 r
= make_backup("/etc/shadow", shadow_path
);
681 /* And make the new files count */
684 if (rename(group_tmp
, group_path
) < 0) {
693 if (rename(gshadow_tmp
, gshadow_path
) < 0) {
704 if (rename(passwd_tmp
, passwd_path
) < 0) {
713 if (rename(shadow_tmp
, shadow_path
) < 0) {
737 static int uid_is_ok(uid_t uid
, const char *name
) {
743 /* Let's see if we already have assigned the UID a second time */
744 if (hashmap_get(todo_uids
, UID_TO_PTR(uid
)))
747 /* Try to avoid using uids that are already used by a group
748 * that doesn't have the same name as our new user. */
749 i
= hashmap_get(todo_gids
, GID_TO_PTR(uid
));
750 if (i
&& !streq(i
->name
, name
))
753 /* Let's check the files directly */
754 if (hashmap_contains(database_uid
, UID_TO_PTR(uid
)))
757 n
= hashmap_get(database_gid
, GID_TO_PTR(uid
));
758 if (n
&& !streq(n
, name
))
761 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
767 if (!IN_SET(errno
, 0, ENOENT
))
771 g
= getgrgid((gid_t
) uid
);
773 if (!streq(g
->gr_name
, name
))
775 } else if (!IN_SET(errno
, 0, ENOENT
))
782 static int root_stat(const char *p
, struct stat
*st
) {
786 if (stat(fix
, st
) < 0)
792 static int read_id_from_file(Item
*i
, uid_t
*_uid
, gid_t
*_gid
) {
794 bool found_uid
= false, found_gid
= false;
800 /* First, try to get the gid directly */
801 if (_gid
&& i
->gid_path
&& root_stat(i
->gid_path
, &st
) >= 0) {
806 /* Then, try to get the uid directly */
807 if ((_uid
|| (_gid
&& !found_gid
))
809 && root_stat(i
->uid_path
, &st
) >= 0) {
814 /* If we need the gid, but had no success yet, also derive it from the uid path */
815 if (_gid
&& !found_gid
) {
821 /* If that didn't work yet, then let's reuse the gid as uid */
822 if (_uid
&& !found_uid
&& i
->gid_path
) {
827 } else if (root_stat(i
->gid_path
, &st
) >= 0) {
828 uid
= (uid_t
) st
.st_gid
;
850 static int add_user(Item
*i
) {
856 /* Check the database directly */
857 z
= hashmap_get(database_user
, i
->name
);
859 log_debug("User %s already exists.", i
->name
);
860 i
->uid
= PTR_TO_UID(z
);
871 p
= getpwnam(i
->name
);
873 log_debug("User %s already exists.", i
->name
);
877 free(i
->description
);
878 i
->description
= strdup(p
->pw_gecos
);
881 if (!IN_SET(errno
, 0, ENOENT
)) {
882 log_error("Failed to check if user %s already exists: %m", i
->name
);
886 /* And shadow too, just to be sure */
888 sp
= getspnam(i
->name
);
890 log_error("User %s already exists in shadow database, but not in user database.", i
->name
);
893 if (!IN_SET(errno
, 0, ENOENT
)) {
894 log_error("Failed to check if user %s already exists in shadow database: %m", i
->name
);
899 /* Try to use the suggested numeric uid */
901 r
= uid_is_ok(i
->uid
, i
->name
);
903 log_error("Failed to verify uid " UID_FMT
": %s", i
->uid
, strerror(-r
));
907 log_debug("Suggested user ID " UID_FMT
" for %s already used.", i
->uid
, i
->name
);
912 /* If that didn't work, try to read it from the specified path */
916 if (read_id_from_file(i
, &c
, NULL
) > 0) {
918 if (c
<= 0 || c
> SYSTEM_UID_MAX
)
919 log_debug("User ID " UID_FMT
" of file not suitable for %s.", c
, i
->name
);
921 r
= uid_is_ok(c
, i
->name
);
923 log_error("Failed to verify uid " UID_FMT
": %s", i
->uid
, strerror(-r
));
929 log_debug("User ID " UID_FMT
" of file for %s is already used.", c
, i
->name
);
934 /* Otherwise try to reuse the group ID */
935 if (!i
->uid_set
&& i
->gid_set
) {
936 r
= uid_is_ok((uid_t
) i
->gid
, i
->name
);
938 log_error("Failed to verify uid " UID_FMT
": %s", i
->uid
, strerror(-r
));
942 i
->uid
= (uid_t
) i
->gid
;
947 /* And if that didn't work either, let's try to find a free one */
949 for (; search_uid
> 0; search_uid
--) {
951 r
= uid_is_ok(search_uid
, i
->name
);
953 log_error("Failed to verify uid " UID_FMT
": %s", i
->uid
, strerror(-r
));
959 if (search_uid
<= 0) {
960 log_error("No free user ID available for %s.", i
->name
);
970 r
= hashmap_ensure_allocated(&todo_uids
, trivial_hash_func
, trivial_compare_func
);
974 r
= hashmap_put(todo_uids
, UID_TO_PTR(i
->uid
), i
);
979 log_info("Creating user %s (%s) with uid " UID_FMT
" and gid " GID_FMT
".", i
->name
, strna(i
->description
), i
->uid
, i
->gid
);
984 static int gid_is_ok(gid_t gid
) {
988 if (hashmap_get(todo_gids
, GID_TO_PTR(gid
)))
991 /* Avoid reusing gids that are already used by a different user */
992 if (hashmap_get(todo_uids
, UID_TO_PTR(gid
)))
995 if (hashmap_contains(database_gid
, GID_TO_PTR(gid
)))
998 if (hashmap_contains(database_uid
, UID_TO_PTR(gid
)))
1006 if (!IN_SET(errno
, 0, ENOENT
))
1010 p
= getpwuid((uid_t
) gid
);
1013 if (!IN_SET(errno
, 0, ENOENT
))
1020 static int add_group(Item
*i
) {
1026 /* Check the database directly */
1027 z
= hashmap_get(database_group
, i
->name
);
1029 log_debug("Group %s already exists.", i
->name
);
1030 i
->gid
= PTR_TO_GID(z
);
1035 /* Also check NSS */
1040 g
= getgrnam(i
->name
);
1042 log_debug("Group %s already exists.", i
->name
);
1047 if (!IN_SET(errno
, 0, ENOENT
)) {
1048 log_error("Failed to check if group %s already exists: %m", i
->name
);
1053 /* Try to use the suggested numeric gid */
1055 r
= gid_is_ok(i
->gid
);
1057 log_error("Failed to verify gid " GID_FMT
": %s", i
->gid
, strerror(-r
));
1061 log_debug("Suggested group ID " GID_FMT
" for %s already used.", i
->gid
, i
->name
);
1066 /* Try to reuse the numeric uid, if there's one */
1067 if (!i
->gid_set
&& i
->uid_set
) {
1068 r
= gid_is_ok((gid_t
) i
->uid
);
1070 log_error("Failed to verify gid " GID_FMT
": %s", i
->gid
, strerror(-r
));
1074 i
->gid
= (gid_t
) i
->uid
;
1079 /* If that didn't work, try to read it from the specified path */
1083 if (read_id_from_file(i
, NULL
, &c
) > 0) {
1085 if (c
<= 0 || c
> SYSTEM_GID_MAX
)
1086 log_debug("Group ID " GID_FMT
" of file not suitable for %s.", c
, i
->name
);
1090 log_error("Failed to verify gid " GID_FMT
": %s", i
->gid
, strerror(-r
));
1096 log_debug("Group ID " GID_FMT
" of file for %s already used.", c
, i
->name
);
1101 /* And if that didn't work either, let's try to find a free one */
1103 for (; search_gid
> 0; search_gid
--) {
1104 r
= gid_is_ok(search_gid
);
1106 log_error("Failed to verify gid " GID_FMT
": %s", i
->gid
, strerror(-r
));
1112 if (search_gid
<= 0) {
1113 log_error("No free group ID available for %s.", i
->name
);
1118 i
->gid
= search_gid
;
1123 r
= hashmap_ensure_allocated(&todo_gids
, trivial_hash_func
, trivial_compare_func
);
1127 r
= hashmap_put(todo_gids
, GID_TO_PTR(i
->gid
), i
);
1131 i
->todo_group
= true;
1132 log_info("Creating group %s with gid " GID_FMT
".", i
->name
, i
->gid
);
1137 static int process_item(Item
*i
) {
1154 j
= hashmap_get(users
, i
->name
);
1156 /* There's already user to be created for this
1157 * name, let's process that in one step */
1166 j
->gid_path
= strdup(i
->gid_path
);
1174 return add_group(i
);
1178 assert_not_reached("Unknown item type");
1182 static void item_free(Item
*i
) {
1190 free(i
->description
);
1194 DEFINE_TRIVIAL_CLEANUP_FUNC(Item
*, item_free
);
1196 static int add_implicit(void) {
1201 /* Implicitly create additional users and groups, if they were listed in "m" lines */
1203 HASHMAP_FOREACH_KEY(l
, g
, members
, iterator
) {
1207 i
= hashmap_get(groups
, g
);
1209 _cleanup_(item_freep
) Item
*j
= NULL
;
1211 r
= hashmap_ensure_allocated(&groups
, string_hash_func
, string_compare_func
);
1219 j
->type
= ADD_GROUP
;
1220 j
->name
= strdup(g
);
1224 r
= hashmap_put(groups
, j
->name
, j
);
1228 log_debug("Adding implicit group '%s' due to m line", j
->name
);
1232 STRV_FOREACH(m
, l
) {
1234 i
= hashmap_get(users
, *m
);
1236 _cleanup_(item_freep
) Item
*j
= NULL
;
1238 r
= hashmap_ensure_allocated(&users
, string_hash_func
, string_compare_func
);
1247 j
->name
= strdup(*m
);
1251 r
= hashmap_put(users
, j
->name
, j
);
1255 log_debug("Adding implicit user '%s' due to m line", j
->name
);
1264 static bool item_equal(Item
*a
, Item
*b
) {
1268 if (a
->type
!= b
->type
)
1271 if (!streq_ptr(a
->name
, b
->name
))
1274 if (!streq_ptr(a
->uid_path
, b
->uid_path
))
1277 if (!streq_ptr(a
->gid_path
, b
->gid_path
))
1280 if (!streq_ptr(a
->description
, b
->description
))
1283 if (a
->uid_set
!= b
->uid_set
)
1286 if (a
->uid_set
&& a
->uid
!= b
->uid
)
1289 if (a
->gid_set
!= b
->gid_set
)
1292 if (a
->gid_set
&& a
->gid
!= b
->gid
)
1298 static bool valid_user_group_name(const char *u
) {
1305 if (!(u
[0] >= 'a' && u
[0] <= 'z') &&
1306 !(u
[0] >= 'A' && u
[0] <= 'Z') &&
1310 for (i
= u
+1; *i
; i
++) {
1311 if (!(*i
>= 'a' && *i
<= 'z') &&
1312 !(*i
>= 'A' && *i
<= 'Z') &&
1313 !(*i
>= '0' && *i
<= '9') &&
1319 sz
= sysconf(_SC_LOGIN_NAME_MAX
);
1322 if ((size_t) (i
-u
) > (size_t) sz
)
1325 if ((size_t) (i
-u
) > UT_NAMESIZE
- 1)
1331 static bool valid_gecos(const char *d
) {
1333 if (!utf8_is_valid(d
))
1336 if (string_has_cc(d
, NULL
))
1339 /* Colons are used as field separators, and hence not OK */
1346 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
1348 static const Specifier specifier_table
[] = {
1349 { 'm', specifier_machine_id
, NULL
},
1350 { 'b', specifier_boot_id
, NULL
},
1351 { 'H', specifier_host_name
, NULL
},
1352 { 'v', specifier_kernel_release
, NULL
},
1356 _cleanup_free_
char *action
= NULL
, *name
= NULL
, *id
= NULL
, *resolved_name
= NULL
;
1357 _cleanup_(item_freep
) Item
*i
= NULL
;
1373 log_error("[%s:%u] Syntax error.", fname
, line
);
1377 if (strlen(action
) != 1) {
1378 log_error("[%s:%u] Unknown modifier '%s'", fname
, line
, action
);
1382 if (!IN_SET(action
[0], ADD_USER
, ADD_GROUP
, ADD_MEMBER
)) {
1383 log_error("[%s:%u] Unknown command command type '%c'.", fname
, line
, action
[0]);
1387 r
= specifier_printf(name
, specifier_table
, NULL
, &resolved_name
);
1389 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1393 if (!valid_user_group_name(resolved_name
)) {
1394 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_name
);
1399 n
+= strspn(buffer
+n
, WHITESPACE
);
1401 if (STR_IN_SET(buffer
+ n
, "", "-"))
1405 switch (action
[0]) {
1408 _cleanup_free_
char *resolved_id
= NULL
;
1411 r
= hashmap_ensure_allocated(&members
, string_hash_func
, string_compare_func
);
1415 /* Try to extend an existing member or group item */
1417 if (!id
|| streq(id
, "-")) {
1418 log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname
, line
);
1422 r
= specifier_printf(id
, specifier_table
, NULL
, &resolved_id
);
1424 log_error("[%s:%u] Failed to replace specifiers: %s", fname
, line
, name
);
1428 if (!valid_user_group_name(resolved_id
)) {
1429 log_error("[%s:%u] '%s' is not a valid user or group name.", fname
, line
, resolved_id
);
1434 log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname
, line
);
1438 l
= hashmap_get(members
, resolved_id
);
1440 /* A list for this group name already exists, let's append to it */
1441 r
= strv_push(&l
, resolved_name
);
1445 resolved_name
= NULL
;
1447 assert_se(hashmap_update(members
, resolved_id
, l
) >= 0);
1449 /* No list for this group name exists yet, create one */
1451 l
= new0(char *, 2);
1455 l
[0] = resolved_name
;
1458 r
= hashmap_put(members
, resolved_id
, l
);
1464 resolved_id
= resolved_name
= NULL
;
1471 r
= hashmap_ensure_allocated(&users
, string_hash_func
, string_compare_func
);
1479 if (id
&& !streq(id
, "-")) {
1481 if (path_is_absolute(id
)) {
1482 i
->uid_path
= strdup(id
);
1486 path_kill_slashes(i
->uid_path
);
1489 r
= parse_uid(id
, &i
->uid
);
1491 log_error("Failed to parse UID: %s", id
);
1500 i
->description
= unquote(buffer
+n
, "\"");
1501 if (!i
->description
)
1504 if (!valid_gecos(i
->description
)) {
1505 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname
, line
, i
->description
);
1514 r
= hashmap_ensure_allocated(&groups
, string_hash_func
, string_compare_func
);
1519 log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname
, line
);
1527 if (id
&& !streq(id
, "-")) {
1529 if (path_is_absolute(id
)) {
1530 i
->gid_path
= strdup(id
);
1534 path_kill_slashes(i
->gid_path
);
1536 r
= parse_gid(id
, &i
->gid
);
1538 log_error("Failed to parse GID: %s", id
);
1553 i
->type
= action
[0];
1554 i
->name
= resolved_name
;
1555 resolved_name
= NULL
;
1557 existing
= hashmap_get(h
, i
->name
);
1560 /* Two identical items are fine */
1561 if (!item_equal(existing
, i
))
1562 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->name
);
1567 r
= hashmap_put(h
, i
->name
, i
);
1575 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1576 _cleanup_fclose_
FILE *rf
= NULL
;
1578 char line
[LINE_MAX
];
1587 r
= search_and_fopen_nulstr(fn
, "re", arg_root
, conf_file_dirs
, &rf
);
1589 if (ignore_enoent
&& r
== -ENOENT
)
1592 log_error("Failed to open '%s', ignoring: %s", fn
, strerror(-r
));
1599 FOREACH_LINE(line
, f
, break) {
1606 if (*l
== '#' || *l
== 0)
1609 k
= parse_line(fn
, v
, l
);
1610 if (k
< 0 && r
== 0)
1615 log_error("Failed to read from file %s: %m", fn
);
1623 static void free_database(Hashmap
*by_name
, Hashmap
*by_id
) {
1627 name
= hashmap_first(by_id
);
1631 hashmap_remove(by_name
, name
);
1633 hashmap_steal_first_key(by_id
);
1637 while ((name
= hashmap_steal_first_key(by_name
)))
1640 hashmap_free(by_name
);
1641 hashmap_free(by_id
);
1644 static void help(void) {
1645 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1646 "Creates system user accounts.\n\n"
1647 " -h --help Show this help\n"
1648 " --version Show package version\n"
1649 " --root=PATH Operate on an alternate filesystem root\n"
1650 , program_invocation_short_name
);
1653 static int parse_argv(int argc
, char *argv
[]) {
1656 ARG_VERSION
= 0x100,
1660 static const struct option options
[] = {
1661 { "help", no_argument
, NULL
, 'h' },
1662 { "version", no_argument
, NULL
, ARG_VERSION
},
1663 { "root", required_argument
, NULL
, ARG_ROOT
},
1672 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
1681 puts(PACKAGE_STRING
);
1682 puts(SYSTEMD_FEATURES
);
1687 arg_root
= path_make_absolute_cwd(optarg
);
1691 path_kill_slashes(arg_root
);
1698 assert_not_reached("Unhandled option");
1704 int main(int argc
, char *argv
[]) {
1706 _cleanup_close_
int lock
= -1;
1712 r
= parse_argv(argc
, argv
);
1716 log_set_target(LOG_TARGET_AUTO
);
1717 log_parse_environment();
1722 r
= label_init(NULL
);
1724 log_error("SELinux setup failed: %s", strerror(-r
));
1728 if (optind
< argc
) {
1731 for (j
= optind
; j
< argc
; j
++) {
1732 k
= read_config_file(argv
[j
], false);
1733 if (k
< 0 && r
== 0)
1737 _cleanup_strv_free_
char **files
= NULL
;
1740 r
= conf_files_list_nulstr(&files
, ".conf", arg_root
, conf_file_dirs
);
1742 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r
));
1746 STRV_FOREACH(f
, files
) {
1747 k
= read_config_file(*f
, true);
1748 if (k
< 0 && r
== 0)
1757 lock
= take_password_lock(arg_root
);
1759 log_error("Failed to take lock: %s", strerror(-lock
));
1763 r
= load_user_database();
1765 log_error("Failed to load user database: %s", strerror(-r
));
1769 r
= load_group_database();
1771 log_error("Failed to read group database: %s", strerror(-r
));
1775 HASHMAP_FOREACH(i
, groups
, iterator
)
1778 HASHMAP_FOREACH(i
, users
, iterator
)
1783 log_error("Failed to write files: %s", strerror(-r
));
1786 while ((i
= hashmap_steal_first(groups
)))
1789 while ((i
= hashmap_steal_first(users
)))
1792 while ((n
= hashmap_first_key(members
))) {
1793 strv_free(hashmap_steal_first(members
));
1797 hashmap_free(groups
);
1798 hashmap_free(users
);
1799 hashmap_free(members
);
1800 hashmap_free(todo_uids
);
1801 hashmap_free(todo_gids
);
1803 free_database(database_user
, database_uid
);
1804 free_database(database_group
, database_gid
);
1808 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;