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