chiark / gitweb /
sysusers: optionally, read sysuers configuration from standard input
[elogind.git] / src / sysusers / sysusers.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <shadow.h>
26 #include <gshadow.h>
27 #include <getopt.h>
28 #include <utmp.h>
29
30 #include "util.h"
31 #include "hashmap.h"
32 #include "specifier.h"
33 #include "path-util.h"
34 #include "build.h"
35 #include "strv.h"
36 #include "conf-files.h"
37 #include "copy.h"
38 #include "utf8.h"
39 #include "label.h"
40 #include "fileio-label.h"
41
42 typedef enum ItemType {
43         ADD_USER = 'u',
44         ADD_GROUP = 'g',
45         ADD_MEMBER = 'm',
46 } ItemType;
47 typedef struct Item {
48         ItemType type;
49
50         char *name;
51         char *uid_path;
52         char *gid_path;
53         char *description;
54
55         gid_t gid;
56         uid_t uid;
57
58         bool gid_set:1;
59         bool uid_set:1;
60
61         bool todo_user:1;
62         bool todo_group:1;
63 } Item;
64
65 static char *arg_root = NULL;
66
67 static const char conf_file_dirs[] =
68         "/etc/sysusers.d\0"
69         "/run/sysusers.d\0"
70         "/usr/local/lib/sysusers.d\0"
71         "/usr/lib/sysusers.d\0"
72 #ifdef HAVE_SPLIT_USR
73         "/lib/sysusers.d\0"
74 #endif
75         ;
76
77 static Hashmap *users = NULL, *groups = NULL;
78 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
79 static Hashmap *members = NULL;
80
81 static Hashmap *database_uid = NULL, *database_user = NULL;
82 static Hashmap *database_gid = NULL, *database_group = NULL;
83
84 static uid_t search_uid = SYSTEM_UID_MAX;
85 static gid_t search_gid = SYSTEM_GID_MAX;
86
87 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
88 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
89
90 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
91 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
92
93 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
94
95 static int load_user_database(void) {
96         _cleanup_fclose_ FILE *f = NULL;
97         const char *passwd_path;
98         struct passwd *pw;
99         int r;
100
101         passwd_path = fix_root("/etc/passwd");
102         f = fopen(passwd_path, "re");
103         if (!f)
104                 return errno == ENOENT ? 0 : -errno;
105
106         r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
107         if (r < 0)
108                 return r;
109
110         r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
111         if (r < 0)
112                 return r;
113
114         errno = 0;
115         while ((pw = fgetpwent(f))) {
116                 char *n;
117                 int k, q;
118
119                 n = strdup(pw->pw_name);
120                 if (!n)
121                         return -ENOMEM;
122
123                 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
124                 if (k < 0 && k != -EEXIST) {
125                         free(n);
126                         return k;
127                 }
128
129                 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
130                 if (q < 0 && q != -EEXIST) {
131                         if (k < 0)
132                                 free(n);
133                         return q;
134                 }
135
136                 if (q < 0 && k < 0)
137                         free(n);
138
139                 errno = 0;
140         }
141         if (!IN_SET(errno, 0, ENOENT))
142                 return -errno;
143
144         return 0;
145 }
146
147 static int load_group_database(void) {
148         _cleanup_fclose_ FILE *f = NULL;
149         const char *group_path;
150         struct group *gr;
151         int r;
152
153         group_path = fix_root("/etc/group");
154         f = fopen(group_path, "re");
155         if (!f)
156                 return errno == ENOENT ? 0 : -errno;
157
158         r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
159         if (r < 0)
160                 return r;
161
162         r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
163         if (r < 0)
164                 return r;
165
166         errno = 0;
167         while ((gr = fgetgrent(f))) {
168                 char *n;
169                 int k, q;
170
171                 n = strdup(gr->gr_name);
172                 if (!n)
173                         return -ENOMEM;
174
175                 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
176                 if (k < 0 && k != -EEXIST) {
177                         free(n);
178                         return k;
179                 }
180
181                 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
182                 if (q < 0 && q != -EEXIST) {
183                         if (k < 0)
184                                 free(n);
185                         return q;
186                 }
187
188                 if (q < 0 && k < 0)
189                         free(n);
190
191                 errno = 0;
192         }
193         if (!IN_SET(errno, 0, ENOENT))
194                 return -errno;
195
196         return 0;
197 }
198
199 static int make_backup(const char *target, const char *x) {
200         _cleanup_close_ int src = -1;
201         _cleanup_fclose_ FILE *dst = NULL;
202         char *backup, *temp;
203         struct timespec ts[2];
204         struct stat st;
205         int r;
206
207         src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
208         if (src < 0) {
209                 if (errno == ENOENT) /* No backup necessary... */
210                         return 0;
211
212                 return -errno;
213         }
214
215         if (fstat(src, &st) < 0)
216                 return -errno;
217
218         r = fopen_temporary_label(target, x, &dst, &temp);
219         if (r < 0)
220                 return r;
221
222         r = copy_bytes(src, fileno(dst), (off_t) -1);
223         if (r < 0)
224                 goto fail;
225
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... */
228
229         backup = strappenda(x, "-");
230
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);
234
235         if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0)
236                 log_warning("Failed to change ownership of %s: %m", backup);
237
238         ts[0] = st.st_atim;
239         ts[1] = st.st_mtim;
240         futimens(fileno(dst), ts);
241
242         if (rename(temp, backup) < 0)
243                 goto fail;
244
245         return 0;
246
247 fail:
248         unlink(temp);
249         return r;
250 }
251
252 static int putgrent_with_members(const struct group *gr, FILE *group) {
253         char **a;
254
255         assert(gr);
256         assert(group);
257
258         a = hashmap_get(members, gr->gr_name);
259         if (a) {
260                 _cleanup_strv_free_ char **l = NULL;
261                 bool added = false;
262                 char **i;
263
264                 l = strv_copy(gr->gr_mem);
265                 if (!l)
266                         return -ENOMEM;
267
268                 STRV_FOREACH(i, a) {
269                         if (strv_find(l, *i))
270                                 continue;
271
272                         if (strv_extend(&l, *i) < 0)
273                                 return -ENOMEM;
274
275                         added = true;
276                 }
277
278                 if (added) {
279                         struct group t;
280
281                         strv_uniq(l);
282                         strv_sort(l);
283
284                         t = *gr;
285                         t.gr_mem = l;
286
287                         errno = 0;
288                         if (putgrent(&t, group) != 0)
289                                 return errno ? -errno : -EIO;
290
291                         return 1;
292                 }
293         }
294
295         errno = 0;
296         if (putgrent(gr, group) != 0)
297                 return errno ? -errno : -EIO;
298
299         return 0;
300 }
301
302 static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
303         char **a;
304
305         assert(sg);
306         assert(gshadow);
307
308         a = hashmap_get(members, sg->sg_namp);
309         if (a) {
310                 _cleanup_strv_free_ char **l = NULL;
311                 bool added = false;
312                 char **i;
313
314                 l = strv_copy(sg->sg_mem);
315                 if (!l)
316                         return -ENOMEM;
317
318                 STRV_FOREACH(i, a) {
319                         if (strv_find(l, *i))
320                                 continue;
321
322                         if (strv_extend(&l, *i) < 0)
323                                 return -ENOMEM;
324
325                         added = true;
326                 }
327
328                 if (added) {
329                         struct sgrp t;
330
331                         strv_uniq(l);
332                         strv_sort(l);
333
334                         t = *sg;
335                         t.sg_mem = l;
336
337                         errno = 0;
338                         if (putsgent(&t, gshadow) != 0)
339                                 return errno ? -errno : -EIO;
340
341                         return 1;
342                 }
343         }
344
345         errno = 0;
346         if (putsgent(sg, gshadow) != 0)
347                 return errno ? -errno : -EIO;
348
349         return 0;
350 }
351
352 static int write_files(void) {
353
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;
358         Iterator iterator;
359         Item *i;
360         int r;
361
362         if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
363                 _cleanup_fclose_ FILE *original = NULL;
364
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);
368                 if (r < 0)
369                         goto finish;
370
371                 if (fchmod(fileno(group), 0644) < 0) {
372                         r = -errno;
373                         goto finish;
374                 }
375
376                 original = fopen(group_path, "re");
377                 if (original) {
378                         struct group *gr;
379
380                         errno = 0;
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. */
389
390                                 i = hashmap_get(groups, gr->gr_name);
391                                 if (i && i->todo_group) {
392                                         r = -EEXIST;
393                                         goto finish;
394                                 }
395
396                                 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
397                                         r = -EEXIST;
398                                         goto finish;
399                                 }
400
401                                 r = putgrent_with_members(gr, group);
402                                 if (r < 0)
403                                         goto finish;
404                                 if (r > 0)
405                                         group_changed = true;
406
407                                 errno = 0;
408                         }
409                         if (!IN_SET(errno, 0, ENOENT)) {
410                                 r = -errno;
411                                 goto finish;
412                         }
413
414                 } else if (errno != ENOENT) {
415                         r = -errno;
416                         goto finish;
417                 }
418
419                 HASHMAP_FOREACH(i, todo_gids, iterator) {
420                         struct group n = {
421                                 .gr_name = i->name,
422                                 .gr_gid = i->gid,
423                                 .gr_passwd = (char*) "x",
424                         };
425
426                         r = putgrent_with_members(&n, group);
427                         if (r < 0)
428                                 goto finish;
429
430                         group_changed = true;
431                 }
432
433                 r = fflush_and_check(group);
434                 if (r < 0)
435                         goto finish;
436
437                 if (original) {
438                         fclose(original);
439                         original = NULL;
440                 }
441
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);
445                 if (r < 0)
446                         goto finish;
447
448                 if (fchmod(fileno(gshadow), 0000) < 0) {
449                         r = -errno;
450                         goto finish;
451                 }
452
453                 original = fopen(gshadow_path, "re");
454                 if (original) {
455                         struct sgrp *sg;
456
457                         errno = 0;
458                         while ((sg = fgetsgent(original))) {
459
460                                 i = hashmap_get(groups, sg->sg_namp);
461                                 if (i && i->todo_group) {
462                                         r = -EEXIST;
463                                         goto finish;
464                                 }
465
466                                 r = putsgent_with_members(sg, gshadow);
467                                 if (r < 0)
468                                         goto finish;
469                                 if (r > 0)
470                                         group_changed = true;
471
472                                 errno = 0;
473                         }
474                         if (!IN_SET(errno, 0, ENOENT)) {
475                                 r = -errno;
476                                 goto finish;
477                         }
478
479                 } else if (errno != ENOENT) {
480                         r = -errno;
481                         goto finish;
482                 }
483
484                 HASHMAP_FOREACH(i, todo_gids, iterator) {
485                         struct sgrp n = {
486                                 .sg_namp = i->name,
487                                 .sg_passwd = (char*) "!!",
488                         };
489
490                         r = putsgent_with_members(&n, gshadow);
491                         if (r < 0)
492                                 goto finish;
493
494                         group_changed = true;
495                 }
496
497                 r = fflush_and_check(gshadow);
498                 if (r < 0)
499                         goto finish;
500         }
501
502         if (hashmap_size(todo_uids) > 0) {
503                 _cleanup_fclose_ FILE *original = NULL;
504                 long lstchg;
505
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);
509                 if (r < 0)
510                         goto finish;
511
512                 if (fchmod(fileno(passwd), 0644) < 0) {
513                         r = -errno;
514                         goto finish;
515                 }
516
517                 original = fopen(passwd_path, "re");
518                 if (original) {
519                         struct passwd *pw;
520
521                         errno = 0;
522                         while ((pw = fgetpwent(original))) {
523
524                                 i = hashmap_get(users, pw->pw_name);
525                                 if (i && i->todo_user) {
526                                         r = -EEXIST;
527                                         goto finish;
528                                 }
529
530                                 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
531                                         r = -EEXIST;
532                                         goto finish;
533                                 }
534
535                                 errno = 0;
536                                 if (putpwent(pw, passwd) < 0) {
537                                         r = errno ? -errno : -EIO;
538                                         goto finish;
539                                 }
540
541                                 errno = 0;
542                         }
543                         if (!IN_SET(errno, 0, ENOENT)) {
544                                 r = -errno;
545                                 goto finish;
546                         }
547
548                 } else if (errno != ENOENT) {
549                         r = -errno;
550                         goto finish;
551                 }
552
553                 HASHMAP_FOREACH(i, todo_uids, iterator) {
554                         struct passwd n = {
555                                 .pw_name = i->name,
556                                 .pw_uid = i->uid,
557                                 .pw_gid = i->gid,
558                                 .pw_gecos = i->description,
559                                 .pw_passwd = (char*) "x",
560                         };
561
562                         /* Initialize the home directory and the shell
563                          * to nologin, with one exception: for root we
564                          * patch in something special */
565                         if (i->uid == 0) {
566                                 n.pw_shell = (char*) "/bin/sh";
567                                 n.pw_dir = (char*) "/root";
568                         } else {
569                                 n.pw_shell = (char*) "/sbin/nologin";
570                                 n.pw_dir = (char*) "/";
571                         }
572
573                         errno = 0;
574                         if (putpwent(&n, passwd) != 0) {
575                                 r = errno ? -errno : -EIO;
576                                 goto finish;
577                         }
578                 }
579
580                 r = fflush_and_check(passwd);
581                 if (r < 0)
582                         goto finish;
583
584                 if (original) {
585                         fclose(original);
586                         original = NULL;
587                 }
588
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);
592                 if (r < 0)
593                         goto finish;
594
595                 if (fchmod(fileno(shadow), 0000) < 0) {
596                         r = -errno;
597                         goto finish;
598                 }
599
600                 original = fopen(shadow_path, "re");
601                 if (original) {
602                         struct spwd *sp;
603
604                         errno = 0;
605                         while ((sp = fgetspent(original))) {
606
607                                 i = hashmap_get(users, sp->sp_namp);
608                                 if (i && i->todo_user) {
609                                         r = -EEXIST;
610                                         goto finish;
611                                 }
612
613                                 errno = 0;
614                                 if (putspent(sp, shadow) < 0) {
615                                         r = errno ? -errno : -EIO;
616                                         goto finish;
617                                 }
618
619                                 errno = 0;
620                         }
621                         if (!IN_SET(errno, 0, ENOENT)) {
622                                 r = -errno;
623                                 goto finish;
624                         }
625                 } else if (errno != ENOENT) {
626                         r = -errno;
627                         goto finish;
628                 }
629
630                 lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
631                 HASHMAP_FOREACH(i, todo_uids, iterator) {
632                         struct spwd n = {
633                                 .sp_namp = i->name,
634                                 .sp_pwdp = (char*) "!!",
635                                 .sp_lstchg = lstchg,
636                                 .sp_min = -1,
637                                 .sp_max = -1,
638                                 .sp_warn = -1,
639                                 .sp_inact = -1,
640                                 .sp_expire = -1,
641                                 .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
642                         };
643
644                         errno = 0;
645                         if (putspent(&n, shadow) != 0) {
646                                 r = errno ? -errno : -EIO;
647                                 goto finish;
648                         }
649                 }
650
651                 r = fflush_and_check(shadow);
652                 if (r < 0)
653                         goto finish;
654         }
655
656         /* Make a backup of the old files */
657         if (group_changed) {
658                 if (group) {
659                         r = make_backup("/etc/group", group_path);
660                         if (r < 0)
661                                 goto finish;
662                 }
663                 if (gshadow) {
664                         r = make_backup("/etc/gshadow", gshadow_path);
665                         if (r < 0)
666                                 goto finish;
667                 }
668         }
669
670         if (passwd) {
671                 r = make_backup("/etc/passwd", passwd_path);
672                 if (r < 0)
673                         goto finish;
674         }
675         if (shadow) {
676                 r = make_backup("/etc/shadow", shadow_path);
677                 if (r < 0)
678                         goto finish;
679         }
680
681         /* And make the new files count */
682         if (group_changed) {
683                 if (group) {
684                         if (rename(group_tmp, group_path) < 0) {
685                                 r = -errno;
686                                 goto finish;
687                         }
688
689                         free(group_tmp);
690                         group_tmp = NULL;
691                 }
692                 if (gshadow) {
693                         if (rename(gshadow_tmp, gshadow_path) < 0) {
694                                 r = -errno;
695                                 goto finish;
696                         }
697
698                         free(gshadow_tmp);
699                         gshadow_tmp = NULL;
700                 }
701         }
702
703         if (passwd) {
704                 if (rename(passwd_tmp, passwd_path) < 0) {
705                         r = -errno;
706                         goto finish;
707                 }
708
709                 free(passwd_tmp);
710                 passwd_tmp = NULL;
711         }
712         if (shadow) {
713                 if (rename(shadow_tmp, shadow_path) < 0) {
714                         r = -errno;
715                         goto finish;
716                 }
717
718                 free(shadow_tmp);
719                 shadow_tmp = NULL;
720         }
721
722         r = 0;
723
724 finish:
725         if (passwd_tmp)
726                 unlink(passwd_tmp);
727         if (shadow_tmp)
728                 unlink(shadow_tmp);
729         if (group_tmp)
730                 unlink(group_tmp);
731         if (gshadow_tmp)
732                 unlink(gshadow_tmp);
733
734         return r;
735 }
736
737 static int uid_is_ok(uid_t uid, const char *name) {
738         struct passwd *p;
739         struct group *g;
740         const char *n;
741         Item *i;
742
743         /* Let's see if we already have assigned the UID a second time */
744         if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
745                 return 0;
746
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))
751                 return 0;
752
753         /* Let's check the files directly */
754         if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
755                 return 0;
756
757         n = hashmap_get(database_gid, GID_TO_PTR(uid));
758         if (n && !streq(n, name))
759                 return 0;
760
761         /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
762         if (!arg_root) {
763                 errno = 0;
764                 p = getpwuid(uid);
765                 if (p)
766                         return 0;
767                 if (!IN_SET(errno, 0, ENOENT))
768                         return -errno;
769
770                 errno = 0;
771                 g = getgrgid((gid_t) uid);
772                 if (g) {
773                         if (!streq(g->gr_name, name))
774                                 return 0;
775                 } else if (!IN_SET(errno, 0, ENOENT))
776                         return -errno;
777         }
778
779         return 1;
780 }
781
782 static int root_stat(const char *p, struct stat *st) {
783         const char *fix;
784
785         fix = fix_root(p);
786         if (stat(fix, st) < 0)
787                 return -errno;
788
789         return 0;
790 }
791
792 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
793         struct stat st;
794         bool found_uid = false, found_gid = false;
795         uid_t uid;
796         gid_t gid;
797
798         assert(i);
799
800         /* First, try to get the gid directly */
801         if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
802                 gid = st.st_gid;
803                 found_gid = true;
804         }
805
806         /* Then, try to get the uid directly */
807         if ((_uid || (_gid && !found_gid))
808             && i->uid_path
809             && root_stat(i->uid_path, &st) >= 0) {
810
811                 uid = st.st_uid;
812                 found_uid = true;
813
814                 /* If we need the gid, but had no success yet, also derive it from the uid path */
815                 if (_gid && !found_gid) {
816                         gid = st.st_gid;
817                         found_gid = true;
818                 }
819         }
820
821         /* If that didn't work yet, then let's reuse the gid as uid */
822         if (_uid && !found_uid && i->gid_path) {
823
824                 if (found_gid) {
825                         uid = (uid_t) gid;
826                         found_uid = true;
827                 } else if (root_stat(i->gid_path, &st) >= 0) {
828                         uid = (uid_t) st.st_gid;
829                         found_uid = true;
830                 }
831         }
832
833         if (_uid) {
834                 if (!found_uid)
835                         return 0;
836
837                 *_uid = uid;
838         }
839
840         if (_gid) {
841                 if (!found_gid)
842                         return 0;
843
844                 *_gid = gid;
845         }
846
847         return 1;
848 }
849
850 static int add_user(Item *i) {
851         void *z;
852         int r;
853
854         assert(i);
855
856         /* Check the database directly */
857         z = hashmap_get(database_user, i->name);
858         if (z) {
859                 log_debug("User %s already exists.", i->name);
860                 i->uid = PTR_TO_UID(z);
861                 i->uid_set = true;
862                 return 0;
863         }
864
865         if (!arg_root) {
866                 struct passwd *p;
867                 struct spwd *sp;
868
869                 /* Also check NSS */
870                 errno = 0;
871                 p = getpwnam(i->name);
872                 if (p) {
873                         log_debug("User %s already exists.", i->name);
874                         i->uid = p->pw_uid;
875                         i->uid_set = true;
876
877                         free(i->description);
878                         i->description = strdup(p->pw_gecos);
879                         return 0;
880                 }
881                 if (!IN_SET(errno, 0, ENOENT)) {
882                         log_error("Failed to check if user %s already exists: %m", i->name);
883                         return -errno;
884                 }
885
886                 /* And shadow too, just to be sure */
887                 errno = 0;
888                 sp = getspnam(i->name);
889                 if (sp) {
890                         log_error("User %s already exists in shadow database, but not in user database.", i->name);
891                         return -EBADMSG;
892                 }
893                 if (!IN_SET(errno, 0, ENOENT)) {
894                         log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
895                         return -errno;
896                 }
897         }
898
899         /* Try to use the suggested numeric uid */
900         if (i->uid_set) {
901                 r = uid_is_ok(i->uid, i->name);
902                 if (r < 0) {
903                         log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
904                         return r;
905                 }
906                 if (r == 0) {
907                         log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
908                         i->uid_set = false;
909                 }
910         }
911
912         /* If that didn't work, try to read it from the specified path */
913         if (!i->uid_set) {
914                 uid_t c;
915
916                 if (read_id_from_file(i, &c, NULL) > 0) {
917
918                         if (c <= 0 || c > SYSTEM_UID_MAX)
919                                 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
920                         else {
921                                 r = uid_is_ok(c, i->name);
922                                 if (r < 0) {
923                                         log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
924                                         return r;
925                                 } else if (r > 0) {
926                                         i->uid = c;
927                                         i->uid_set = true;
928                                 } else
929                                         log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
930                         }
931                 }
932         }
933
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);
937                 if (r < 0) {
938                         log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
939                         return r;
940                 }
941                 if (r > 0) {
942                         i->uid = (uid_t) i->gid;
943                         i->uid_set = true;
944                 }
945         }
946
947         /* And if that didn't work either, let's try to find a free one */
948         if (!i->uid_set) {
949                 for (; search_uid > 0; search_uid--) {
950
951                         r = uid_is_ok(search_uid, i->name);
952                         if (r < 0) {
953                                 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
954                                 return r;
955                         } else if (r > 0)
956                                 break;
957                 }
958
959                 if (search_uid <= 0) {
960                         log_error("No free user ID available for %s.", i->name);
961                         return -E2BIG;
962                 }
963
964                 i->uid_set = true;
965                 i->uid = search_uid;
966
967                 search_uid--;
968         }
969
970         r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
971         if (r < 0)
972                 return log_oom();
973
974         r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
975         if (r < 0)
976                 return log_oom();
977
978         i->todo_user = true;
979         log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
980
981         return 0;
982 }
983
984 static int gid_is_ok(gid_t gid) {
985         struct group *g;
986         struct passwd *p;
987
988         if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
989                 return 0;
990
991         /* Avoid reusing gids that are already used by a different user */
992         if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
993                 return 0;
994
995         if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
996                 return 0;
997
998         if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
999                 return 0;
1000
1001         if (!arg_root) {
1002                 errno = 0;
1003                 g = getgrgid(gid);
1004                 if (g)
1005                         return 0;
1006                 if (!IN_SET(errno, 0, ENOENT))
1007                         return -errno;
1008
1009                 errno = 0;
1010                 p = getpwuid((uid_t) gid);
1011                 if (p)
1012                         return 0;
1013                 if (!IN_SET(errno, 0, ENOENT))
1014                         return -errno;
1015         }
1016
1017         return 1;
1018 }
1019
1020 static int add_group(Item *i) {
1021         void *z;
1022         int r;
1023
1024         assert(i);
1025
1026         /* Check the database directly */
1027         z = hashmap_get(database_group, i->name);
1028         if (z) {
1029                 log_debug("Group %s already exists.", i->name);
1030                 i->gid = PTR_TO_GID(z);
1031                 i->gid_set = true;
1032                 return 0;
1033         }
1034
1035         /* Also check NSS */
1036         if (!arg_root) {
1037                 struct group *g;
1038
1039                 errno = 0;
1040                 g = getgrnam(i->name);
1041                 if (g) {
1042                         log_debug("Group %s already exists.", i->name);
1043                         i->gid = g->gr_gid;
1044                         i->gid_set = true;
1045                         return 0;
1046                 }
1047                 if (!IN_SET(errno, 0, ENOENT)) {
1048                         log_error("Failed to check if group %s already exists: %m", i->name);
1049                         return -errno;
1050                 }
1051         }
1052
1053         /* Try to use the suggested numeric gid */
1054         if (i->gid_set) {
1055                 r = gid_is_ok(i->gid);
1056                 if (r < 0) {
1057                         log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1058                         return r;
1059                 }
1060                 if (r == 0) {
1061                         log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
1062                         i->gid_set = false;
1063                 }
1064         }
1065
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);
1069                 if (r < 0) {
1070                         log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1071                         return r;
1072                 }
1073                 if (r > 0) {
1074                         i->gid = (gid_t) i->uid;
1075                         i->gid_set = true;
1076                 }
1077         }
1078
1079         /* If that didn't work, try to read it from the specified path */
1080         if (!i->gid_set) {
1081                 gid_t c;
1082
1083                 if (read_id_from_file(i, NULL, &c) > 0) {
1084
1085                         if (c <= 0 || c > SYSTEM_GID_MAX)
1086                                 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
1087                         else {
1088                                 r = gid_is_ok(c);
1089                                 if (r < 0) {
1090                                         log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1091                                         return r;
1092                                 } else if (r > 0) {
1093                                         i->gid = c;
1094                                         i->gid_set = true;
1095                                 } else
1096                                         log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
1097                         }
1098                 }
1099         }
1100
1101         /* And if that didn't work either, let's try to find a free one */
1102         if (!i->gid_set) {
1103                 for (; search_gid > 0; search_gid--) {
1104                         r = gid_is_ok(search_gid);
1105                         if (r < 0) {
1106                                 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
1107                                 return r;
1108                         } else if (r > 0)
1109                                 break;
1110                 }
1111
1112                 if (search_gid <= 0) {
1113                         log_error("No free group ID available for %s.", i->name);
1114                         return -E2BIG;
1115                 }
1116
1117                 i->gid_set = true;
1118                 i->gid = search_gid;
1119
1120                 search_gid--;
1121         }
1122
1123         r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
1124         if (r < 0)
1125                 return log_oom();
1126
1127         r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
1128         if (r < 0)
1129                 return log_oom();
1130
1131         i->todo_group = true;
1132         log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
1133
1134         return 0;
1135 }
1136
1137 static int process_item(Item *i) {
1138         int r;
1139
1140         assert(i);
1141
1142         switch (i->type) {
1143
1144         case ADD_USER:
1145                 r = add_group(i);
1146                 if (r < 0)
1147                         return r;
1148
1149                 return add_user(i);
1150
1151         case ADD_GROUP: {
1152                 Item *j;
1153
1154                 j = hashmap_get(users, i->name);
1155                 if (j) {
1156                         /* There's already user to be created for this
1157                          * name, let's process that in one step */
1158
1159                         if (i->gid_set) {
1160                                 j->gid = i->gid;
1161                                 j->gid_set = true;
1162                         }
1163
1164                         if (i->gid_path) {
1165                                 free(j->gid_path);
1166                                 j->gid_path = strdup(i->gid_path);
1167                                 if (!j->gid_path)
1168                                         return log_oom();
1169                         }
1170
1171                         return 0;
1172                 }
1173
1174                 return add_group(i);
1175         }
1176
1177         default:
1178                 assert_not_reached("Unknown item type");
1179         }
1180 }
1181
1182 static void item_free(Item *i) {
1183
1184         if (!i)
1185                 return;
1186
1187         free(i->name);
1188         free(i->uid_path);
1189         free(i->gid_path);
1190         free(i->description);
1191         free(i);
1192 }
1193
1194 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
1195
1196 static int add_implicit(void) {
1197         char *g, **l;
1198         Iterator iterator;
1199         int r;
1200
1201         /* Implicitly create additional users and groups, if they were listed in "m" lines */
1202
1203         HASHMAP_FOREACH_KEY(l, g, members, iterator) {
1204                 Item *i;
1205                 char **m;
1206
1207                 i = hashmap_get(groups, g);
1208                 if (!i) {
1209                         _cleanup_(item_freep) Item *j = NULL;
1210
1211                         r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1212                         if (r < 0)
1213                                 return log_oom();
1214
1215                         j = new0(Item, 1);
1216                         if (!j)
1217                                 return log_oom();
1218
1219                         j->type = ADD_GROUP;
1220                         j->name = strdup(g);
1221                         if (!j->name)
1222                                 return log_oom();
1223
1224                         r = hashmap_put(groups, j->name, j);
1225                         if (r < 0)
1226                                 return log_oom();
1227
1228                         log_debug("Adding implicit group '%s' due to m line", j->name);
1229                         j = NULL;
1230                 }
1231
1232                 STRV_FOREACH(m, l) {
1233
1234                         i = hashmap_get(users, *m);
1235                         if (!i) {
1236                                 _cleanup_(item_freep) Item *j = NULL;
1237
1238                                 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1239                                 if (r < 0)
1240                                         return log_oom();
1241
1242                                 j = new0(Item, 1);
1243                                 if (!j)
1244                                         return log_oom();
1245
1246                                 j->type = ADD_USER;
1247                                 j->name = strdup(*m);
1248                                 if (!j->name)
1249                                         return log_oom();
1250
1251                                 r = hashmap_put(users, j->name, j);
1252                                 if (r < 0)
1253                                         return log_oom();
1254
1255                                 log_debug("Adding implicit user '%s' due to m line", j->name);
1256                                 j = NULL;
1257                         }
1258                 }
1259         }
1260
1261         return 0;
1262 }
1263
1264 static bool item_equal(Item *a, Item *b) {
1265         assert(a);
1266         assert(b);
1267
1268         if (a->type != b->type)
1269                 return false;
1270
1271         if (!streq_ptr(a->name, b->name))
1272                 return false;
1273
1274         if (!streq_ptr(a->uid_path, b->uid_path))
1275                 return false;
1276
1277         if (!streq_ptr(a->gid_path, b->gid_path))
1278                 return false;
1279
1280         if (!streq_ptr(a->description, b->description))
1281                 return false;
1282
1283         if (a->uid_set != b->uid_set)
1284                 return false;
1285
1286         if (a->uid_set && a->uid != b->uid)
1287                 return false;
1288
1289         if (a->gid_set != b->gid_set)
1290                 return false;
1291
1292         if (a->gid_set && a->gid != b->gid)
1293                 return false;
1294
1295         return true;
1296 }
1297
1298 static bool valid_user_group_name(const char *u) {
1299         const char *i;
1300         long sz;
1301
1302         if (isempty(u))
1303                 return false;
1304
1305         if (!(u[0] >= 'a' && u[0] <= 'z') &&
1306             !(u[0] >= 'A' && u[0] <= 'Z') &&
1307             u[0] != '_')
1308                 return false;
1309
1310         for (i = u+1; *i; i++) {
1311                 if (!(*i >= 'a' && *i <= 'z') &&
1312                     !(*i >= 'A' && *i <= 'Z') &&
1313                     !(*i >= '0' && *i <= '9') &&
1314                     *i != '_' &&
1315                     *i != '-')
1316                         return false;
1317         }
1318
1319         sz = sysconf(_SC_LOGIN_NAME_MAX);
1320         assert_se(sz > 0);
1321
1322         if ((size_t) (i-u) > (size_t) sz)
1323                 return false;
1324
1325         if ((size_t) (i-u) > UT_NAMESIZE - 1)
1326                 return false;
1327
1328         return true;
1329 }
1330
1331 static bool valid_gecos(const char *d) {
1332
1333         if (!utf8_is_valid(d))
1334                 return false;
1335
1336         if (string_has_cc(d, NULL))
1337                 return false;
1338
1339         /* Colons are used as field separators, and hence not OK */
1340         if (strchr(d, ':'))
1341                 return false;
1342
1343         return true;
1344 }
1345
1346 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1347
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 },
1353                 {}
1354         };
1355
1356         _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL;
1357         _cleanup_(item_freep) Item *i = NULL;
1358         Item *existing;
1359         Hashmap *h;
1360         int r, n = -1;
1361
1362         assert(fname);
1363         assert(line >= 1);
1364         assert(buffer);
1365
1366         r = sscanf(buffer,
1367                    "%ms %ms %ms %n",
1368                    &action,
1369                    &name,
1370                    &id,
1371                    &n);
1372         if (r < 2) {
1373                 log_error("[%s:%u] Syntax error.", fname, line);
1374                 return -EIO;
1375         }
1376
1377         if (strlen(action) != 1) {
1378                 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1379                 return -EINVAL;
1380         }
1381
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]);
1384                 return -EBADMSG;
1385         }
1386
1387         r = specifier_printf(name, specifier_table, NULL, &resolved_name);
1388         if (r < 0) {
1389                 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1390                 return r;
1391         }
1392
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);
1395                 return -EINVAL;
1396         }
1397
1398         if (n >= 0) {
1399                 n += strspn(buffer+n, WHITESPACE);
1400
1401                 if (STR_IN_SET(buffer + n, "", "-"))
1402                         n = -1;
1403         }
1404
1405         switch (action[0]) {
1406
1407         case ADD_MEMBER: {
1408                 _cleanup_free_ char *resolved_id = NULL;
1409                 char **l;
1410
1411                 r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
1412                 if (r < 0)
1413                         return log_oom();
1414
1415                 /* Try to extend an existing member or group item */
1416
1417                 if (!id || streq(id, "-")) {
1418                         log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
1419                         return -EINVAL;
1420                 }
1421
1422                 r = specifier_printf(id, specifier_table, NULL, &resolved_id);
1423                 if (r < 0) {
1424                         log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1425                         return r;
1426                 }
1427
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);
1430                         return -EINVAL;
1431                 }
1432
1433                 if (n >= 0) {
1434                         log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
1435                         return -EINVAL;
1436                 }
1437
1438                 l = hashmap_get(members, resolved_id);
1439                 if (l) {
1440                         /* A list for this group name already exists, let's append to it */
1441                         r = strv_push(&l, resolved_name);
1442                         if (r < 0)
1443                                 return log_oom();
1444
1445                         resolved_name = NULL;
1446
1447                         assert_se(hashmap_update(members, resolved_id, l) >= 0);
1448                 } else {
1449                         /* No list for this group name exists yet, create one */
1450
1451                         l = new0(char *, 2);
1452                         if (!l)
1453                                 return -ENOMEM;
1454
1455                         l[0] = resolved_name;
1456                         l[1] = NULL;
1457
1458                         r = hashmap_put(members, resolved_id, l);
1459                         if (r < 0) {
1460                                 free(l);
1461                                 return log_oom();
1462                         }
1463
1464                         resolved_id = resolved_name = NULL;
1465                 }
1466
1467                 return 0;
1468         }
1469
1470         case ADD_USER:
1471                 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1472                 if (r < 0)
1473                         return log_oom();
1474
1475                 i = new0(Item, 1);
1476                 if (!i)
1477                         return log_oom();
1478
1479                 if (id && !streq(id, "-")) {
1480
1481                         if (path_is_absolute(id)) {
1482                                 i->uid_path = strdup(id);
1483                                 if (!i->uid_path)
1484                                         return log_oom();
1485
1486                                 path_kill_slashes(i->uid_path);
1487
1488                         } else {
1489                                 r = parse_uid(id, &i->uid);
1490                                 if (r < 0) {
1491                                         log_error("Failed to parse UID: %s", id);
1492                                         return -EBADMSG;
1493                                 }
1494
1495                                 i->uid_set = true;
1496                         }
1497                 }
1498
1499                 if (n >= 0) {
1500                         i->description = unquote(buffer+n, "\"");
1501                         if (!i->description)
1502                                 return log_oom();
1503
1504                         if (!valid_gecos(i->description)) {
1505                                 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1506                                 return -EINVAL;
1507                         }
1508                 }
1509
1510                 h = users;
1511                 break;
1512
1513         case ADD_GROUP:
1514                 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1515                 if (r < 0)
1516                         return log_oom();
1517
1518                 if (n >= 0) {
1519                         log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
1520                         return -EINVAL;
1521                 }
1522
1523                 i = new0(Item, 1);
1524                 if (!i)
1525                         return log_oom();
1526
1527                 if (id && !streq(id, "-")) {
1528
1529                         if (path_is_absolute(id)) {
1530                                 i->gid_path = strdup(id);
1531                                 if (!i->gid_path)
1532                                         return log_oom();
1533
1534                                 path_kill_slashes(i->gid_path);
1535                         } else {
1536                                 r = parse_gid(id, &i->gid);
1537                                 if (r < 0) {
1538                                         log_error("Failed to parse GID: %s", id);
1539                                         return -EBADMSG;
1540                                 }
1541
1542                                 i->gid_set = true;
1543                         }
1544                 }
1545
1546
1547                 h = groups;
1548                 break;
1549         default:
1550                 return -EBADMSG;
1551         }
1552
1553         i->type = action[0];
1554         i->name = resolved_name;
1555         resolved_name = NULL;
1556
1557         existing = hashmap_get(h, i->name);
1558         if (existing) {
1559
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);
1563
1564                 return 0;
1565         }
1566
1567         r = hashmap_put(h, i->name, i);
1568         if (r < 0)
1569                 return log_oom();
1570
1571         i = NULL;
1572         return 0;
1573 }
1574
1575 static int read_config_file(const char *fn, bool ignore_enoent) {
1576         _cleanup_fclose_ FILE *rf = NULL;
1577         FILE *f = NULL;
1578         char line[LINE_MAX];
1579         unsigned v = 0;
1580         int r;
1581
1582         assert(fn);
1583
1584         if (streq(fn, "-"))
1585                 f = stdin;
1586         else {
1587                 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf);
1588                 if (r < 0) {
1589                         if (ignore_enoent && r == -ENOENT)
1590                                 return 0;
1591
1592                         log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1593                         return r;
1594                 }
1595
1596                 f = rf;
1597         }
1598
1599         FOREACH_LINE(line, f, break) {
1600                 char *l;
1601                 int k;
1602
1603                 v++;
1604
1605                 l = strstrip(line);
1606                 if (*l == '#' || *l == 0)
1607                         continue;
1608
1609                 k = parse_line(fn, v, l);
1610                 if (k < 0 && r == 0)
1611                         r = k;
1612         }
1613
1614         if (ferror(f)) {
1615                 log_error("Failed to read from file %s: %m", fn);
1616                 if (r == 0)
1617                         r = -EIO;
1618         }
1619
1620         return r;
1621 }
1622
1623 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1624         char *name;
1625
1626         for (;;) {
1627                 name = hashmap_first(by_id);
1628                 if (!name)
1629                         break;
1630
1631                 hashmap_remove(by_name, name);
1632
1633                 hashmap_steal_first_key(by_id);
1634                 free(name);
1635         }
1636
1637         while ((name = hashmap_steal_first_key(by_name)))
1638                 free(name);
1639
1640         hashmap_free(by_name);
1641         hashmap_free(by_id);
1642 }
1643
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);
1651 }
1652
1653 static int parse_argv(int argc, char *argv[]) {
1654
1655         enum {
1656                 ARG_VERSION = 0x100,
1657                 ARG_ROOT,
1658         };
1659
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    },
1664                 {}
1665         };
1666
1667         int c;
1668
1669         assert(argc >= 0);
1670         assert(argv);
1671
1672         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1673
1674                 switch (c) {
1675
1676                 case 'h':
1677                         help();
1678                         return 0;
1679
1680                 case ARG_VERSION:
1681                         puts(PACKAGE_STRING);
1682                         puts(SYSTEMD_FEATURES);
1683                         return 0;
1684
1685                 case ARG_ROOT:
1686                         free(arg_root);
1687                         arg_root = path_make_absolute_cwd(optarg);
1688                         if (!arg_root)
1689                                 return log_oom();
1690
1691                         path_kill_slashes(arg_root);
1692                         break;
1693
1694                 case '?':
1695                         return -EINVAL;
1696
1697                 default:
1698                         assert_not_reached("Unhandled option");
1699                 }
1700
1701         return 1;
1702 }
1703
1704 int main(int argc, char *argv[]) {
1705
1706         _cleanup_close_ int lock = -1;
1707         Iterator iterator;
1708         int r, k;
1709         Item *i;
1710         char *n;
1711
1712         r = parse_argv(argc, argv);
1713         if (r <= 0)
1714                 goto finish;
1715
1716         log_set_target(LOG_TARGET_AUTO);
1717         log_parse_environment();
1718         log_open();
1719
1720         umask(0022);
1721
1722         r = label_init(NULL);
1723         if (r < 0) {
1724                 log_error("SELinux setup failed: %s", strerror(-r));
1725                 goto finish;
1726         }
1727
1728         if (optind < argc) {
1729                 int j;
1730
1731                 for (j = optind; j < argc; j++) {
1732                         k = read_config_file(argv[j], false);
1733                         if (k < 0 && r == 0)
1734                                 r = k;
1735                 }
1736         } else {
1737                 _cleanup_strv_free_ char **files = NULL;
1738                 char **f;
1739
1740                 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1741                 if (r < 0) {
1742                         log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1743                         goto finish;
1744                 }
1745
1746                 STRV_FOREACH(f, files) {
1747                         k = read_config_file(*f, true);
1748                         if (k < 0 && r == 0)
1749                                 r = k;
1750                 }
1751         }
1752
1753         r = add_implicit();
1754         if (r < 0)
1755                 goto finish;
1756
1757         lock = take_password_lock(arg_root);
1758         if (lock < 0) {
1759                 log_error("Failed to take lock: %s", strerror(-lock));
1760                 goto finish;
1761         }
1762
1763         r = load_user_database();
1764         if (r < 0) {
1765                 log_error("Failed to load user database: %s", strerror(-r));
1766                 goto finish;
1767         }
1768
1769         r = load_group_database();
1770         if (r < 0) {
1771                 log_error("Failed to read group database: %s", strerror(-r));
1772                 goto finish;
1773         }
1774
1775         HASHMAP_FOREACH(i, groups, iterator)
1776                 process_item(i);
1777
1778         HASHMAP_FOREACH(i, users, iterator)
1779                 process_item(i);
1780
1781         r = write_files();
1782         if (r < 0)
1783                 log_error("Failed to write files: %s", strerror(-r));
1784
1785 finish:
1786         while ((i = hashmap_steal_first(groups)))
1787                 item_free(i);
1788
1789         while ((i = hashmap_steal_first(users)))
1790                 item_free(i);
1791
1792         while ((n = hashmap_first_key(members))) {
1793                 strv_free(hashmap_steal_first(members));
1794                 free(n);
1795         }
1796
1797         hashmap_free(groups);
1798         hashmap_free(users);
1799         hashmap_free(members);
1800         hashmap_free(todo_uids);
1801         hashmap_free(todo_gids);
1802
1803         free_database(database_user, database_uid);
1804         free_database(database_group, database_gid);
1805
1806         free(arg_root);
1807
1808         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1809 }