chiark / gitweb /
tmpfiles: add support for creating symlinks, char/block device nodes
[elogind.git] / src / tmpfiles.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering, Kay Sievers
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <limits.h>
28 #include <dirent.h>
29 #include <grp.h>
30 #include <pwd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <getopt.h>
35 #include <stdbool.h>
36 #include <time.h>
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <glob.h>
40 #include <fnmatch.h>
41
42 #include "log.h"
43 #include "util.h"
44 #include "strv.h"
45 #include "label.h"
46 #include "set.h"
47
48 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
49  * them in the file system. This is intended to be used to create
50  * properly owned directories beneath /tmp, /var/tmp, /run, which are
51  * volatile and hence need to be recreated on bootup. */
52
53 typedef enum ItemType {
54         /* These ones take file names */
55         CREATE_FILE = 'f',
56         TRUNCATE_FILE = 'F',
57         CREATE_DIRECTORY = 'd',
58         TRUNCATE_DIRECTORY = 'D',
59         CREATE_FIFO = 'p',
60         CREATE_SYMLINK = 'L',
61         CREATE_CHAR_DEVICE = 'c',
62         CREATE_BLOCK_DEVICE = 'b',
63
64         /* These ones take globs */
65         IGNORE_PATH = 'x',
66         REMOVE_PATH = 'r',
67         RECURSIVE_REMOVE_PATH = 'R',
68         RELABEL_PATH = 'z',
69         RECURSIVE_RELABEL_PATH = 'Z'
70 } ItemType;
71
72 typedef struct Item {
73         ItemType type;
74
75         char *path;
76         char *argument;
77         uid_t uid;
78         gid_t gid;
79         mode_t mode;
80         usec_t age;
81
82         dev_t major_minor;
83
84         bool uid_set:1;
85         bool gid_set:1;
86         bool mode_set:1;
87         bool age_set:1;
88 } Item;
89
90 static Hashmap *items = NULL, *globs = NULL;
91 static Set *unix_sockets = NULL;
92
93 static bool arg_create = false;
94 static bool arg_clean = false;
95 static bool arg_remove = false;
96
97 static const char *arg_prefix = NULL;
98
99 #define MAX_DEPTH 256
100
101 static bool needs_glob(ItemType t) {
102         return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
103 }
104
105 static struct Item* find_glob(Hashmap *h, const char *match) {
106         Item *j;
107         Iterator i;
108
109         HASHMAP_FOREACH(j, h, i)
110                 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
111                         return j;
112
113         return NULL;
114 }
115
116 static void load_unix_sockets(void) {
117         FILE *f = NULL;
118         char line[LINE_MAX];
119
120         if (unix_sockets)
121                 return;
122
123         /* We maintain a cache of the sockets we found in
124          * /proc/net/unix to speed things up a little. */
125
126         unix_sockets = set_new(string_hash_func, string_compare_func);
127         if (!unix_sockets)
128                 return;
129
130         f = fopen("/proc/net/unix", "re");
131         if (!f)
132                 return;
133
134         /* Skip header */
135         if (!fgets(line, sizeof(line), f))
136                 goto fail;
137
138         for (;;) {
139                 char *p, *s;
140                 int k;
141
142                 if (!fgets(line, sizeof(line), f))
143                         break;
144
145                 truncate_nl(line);
146
147                 p = strchr(line, ':');
148                 if (!p)
149                         continue;
150
151                 if (strlen(p) < 37)
152                         continue;
153
154                 p += 37;
155                 p += strspn(p, WHITESPACE);
156                 p += strcspn(p, WHITESPACE); /* skip one more word */
157                 p += strspn(p, WHITESPACE);
158
159                 if (*p != '/')
160                         continue;
161
162                 s = strdup(p);
163                 if (!s)
164                         goto fail;
165
166                 path_kill_slashes(s);
167
168                 k = set_put(unix_sockets, s);
169                 if (k < 0) {
170                         free(s);
171
172                         if (k != -EEXIST)
173                                 goto fail;
174                 }
175         }
176
177         fclose(f);
178         return;
179
180 fail:
181         set_free_free(unix_sockets);
182         unix_sockets = NULL;
183
184         if (f)
185                 fclose(f);
186 }
187
188 static bool unix_socket_alive(const char *fn) {
189         assert(fn);
190
191         load_unix_sockets();
192
193         if (unix_sockets)
194                 return !!set_get(unix_sockets, (char*) fn);
195
196         /* We don't know, so assume yes */
197         return true;
198 }
199
200 static int dir_cleanup(
201                 const char *p,
202                 DIR *d,
203                 const struct stat *ds,
204                 usec_t cutoff,
205                 dev_t rootdev,
206                 bool mountpoint,
207                 int maxdepth)
208 {
209         struct dirent *dent;
210         struct timespec times[2];
211         bool deleted = false;
212         char *sub_path = NULL;
213         int r = 0;
214
215         while ((dent = readdir(d))) {
216                 struct stat s;
217                 usec_t age;
218
219                 if (streq(dent->d_name, ".") ||
220                     streq(dent->d_name, ".."))
221                         continue;
222
223                 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
224
225                         if (errno != ENOENT) {
226                                 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
227                                 r = -errno;
228                         }
229
230                         continue;
231                 }
232
233                 /* Stay on the same filesystem */
234                 if (s.st_dev != rootdev)
235                         continue;
236
237                 /* Do not delete read-only files owned by root */
238                 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
239                         continue;
240
241                 free(sub_path);
242                 sub_path = NULL;
243
244                 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
245                         log_error("Out of memory");
246                         r = -ENOMEM;
247                         goto finish;
248                 }
249
250                 /* Is there an item configured for this path? */
251                 if (hashmap_get(items, sub_path))
252                         continue;
253
254                 if (find_glob(globs, sub_path))
255                         continue;
256
257                 if (S_ISDIR(s.st_mode)) {
258
259                         if (mountpoint &&
260                             streq(dent->d_name, "lost+found") &&
261                             s.st_uid == 0)
262                                 continue;
263
264                         if (maxdepth <= 0)
265                                 log_warning("Reached max depth on %s.", sub_path);
266                         else {
267                                 DIR *sub_dir;
268                                 int q;
269
270                                 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW);
271                                 if (sub_dir == NULL) {
272                                         if (errno != ENOENT) {
273                                                 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
274                                                 r = -errno;
275                                         }
276
277                                         continue;
278                                 }
279
280                                 q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
281                                 closedir(sub_dir);
282
283                                 if (q < 0)
284                                         r = q;
285                         }
286
287                         /* Ignore ctime, we change it when deleting */
288                         age = MAX(timespec_load(&s.st_mtim),
289                                   timespec_load(&s.st_atim));
290                         if (age >= cutoff)
291                                 continue;
292
293                         log_debug("rmdir '%s'\n", sub_path);
294
295                         if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
296                                 if (errno != ENOENT && errno != ENOTEMPTY) {
297                                         log_error("rmdir(%s): %m", sub_path);
298                                         r = -errno;
299                                 }
300                         }
301
302                 } else {
303                         /* Skip files for which the sticky bit is
304                          * set. These are semantics we define, and are
305                          * unknown elsewhere. See XDG_RUNTIME_DIR
306                          * specification for details. */
307                         if (s.st_mode & S_ISVTX)
308                                 continue;
309
310                         if (mountpoint && S_ISREG(s.st_mode)) {
311                                 if (streq(dent->d_name, ".journal") &&
312                                     s.st_uid == 0)
313                                         continue;
314
315                                 if (streq(dent->d_name, "aquota.user") ||
316                                     streq(dent->d_name, "aquota.group"))
317                                         continue;
318                         }
319
320                         /* Ignore sockets that are listed in /proc/net/unix */
321                         if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
322                                 continue;
323
324                         /* Ignore device nodes */
325                         if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
326                                 continue;
327
328                         age = MAX3(timespec_load(&s.st_mtim),
329                                    timespec_load(&s.st_atim),
330                                    timespec_load(&s.st_ctim));
331
332                         if (age >= cutoff)
333                                 continue;
334
335                         log_debug("unlink '%s'\n", sub_path);
336
337                         if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
338                                 if (errno != ENOENT) {
339                                         log_error("unlink(%s): %m", sub_path);
340                                         r = -errno;
341                                 }
342                         }
343
344                         deleted = true;
345                 }
346         }
347
348 finish:
349         if (deleted) {
350                 /* Restore original directory timestamps */
351                 times[0] = ds->st_atim;
352                 times[1] = ds->st_mtim;
353
354                 if (futimens(dirfd(d), times) < 0)
355                         log_error("utimensat(%s): %m", p);
356         }
357
358         free(sub_path);
359
360         return r;
361 }
362
363 static int clean_item(Item *i) {
364         DIR *d;
365         struct stat s, ps;
366         bool mountpoint;
367         int r;
368         usec_t cutoff, n;
369
370         assert(i);
371
372         if (i->type != CREATE_DIRECTORY &&
373             i->type != TRUNCATE_DIRECTORY &&
374             i->type != IGNORE_PATH)
375                 return 0;
376
377         if (!i->age_set || i->age <= 0)
378                 return 0;
379
380         n = now(CLOCK_REALTIME);
381         if (n < i->age)
382                 return 0;
383
384         cutoff = n - i->age;
385
386         d = opendir(i->path);
387         if (!d) {
388                 if (errno == ENOENT)
389                         return 0;
390
391                 log_error("Failed to open directory %s: %m", i->path);
392                 return -errno;
393         }
394
395         if (fstat(dirfd(d), &s) < 0) {
396                 log_error("stat(%s) failed: %m", i->path);
397                 r = -errno;
398                 goto finish;
399         }
400
401         if (!S_ISDIR(s.st_mode)) {
402                 log_error("%s is not a directory.", i->path);
403                 r = -ENOTDIR;
404                 goto finish;
405         }
406
407         if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
408                 log_error("stat(%s/..) failed: %m", i->path);
409                 r = -errno;
410                 goto finish;
411         }
412
413         mountpoint = s.st_dev != ps.st_dev ||
414                      (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
415
416         r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
417
418 finish:
419         if (d)
420                 closedir(d);
421
422         return r;
423 }
424
425 static int item_set_perms(Item *i, const char *path) {
426         /* not using i->path directly because it may be a glob */
427         if (i->mode_set)
428                 if (chmod(path, i->mode) < 0) {
429                         log_error("chmod(%s) failed: %m", path);
430                         return -errno;
431                 }
432
433         if (i->uid_set || i->gid_set)
434                 if (chown(path,
435                           i->uid_set ? i->uid : (uid_t) -1,
436                           i->gid_set ? i->gid : (gid_t) -1) < 0) {
437
438                         log_error("chown(%s) failed: %m", path);
439                         return -errno;
440                 }
441
442         return label_fix(path, false);
443 }
444
445 static int recursive_relabel_children(Item *i, const char *path) {
446         DIR *d;
447         int ret = 0;
448
449         /* This returns the first error we run into, but nevertheless
450          * tries to go on */
451
452         d = opendir(path);
453         if (!d)
454                 return errno == ENOENT ? 0 : -errno;
455
456         for (;;) {
457                 struct dirent buf, *de;
458                 bool is_dir;
459                 int r;
460                 char *entry_path;
461
462                 r = readdir_r(d, &buf, &de);
463                 if (r != 0) {
464                         if (ret == 0)
465                                 ret = -r;
466                         break;
467                 }
468
469                 if (!de)
470                         break;
471
472                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
473                         continue;
474
475                 if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
476                         if (ret == 0)
477                                 ret = -ENOMEM;
478                         continue;
479                 }
480
481                 if (de->d_type == DT_UNKNOWN) {
482                         struct stat st;
483
484                         if (lstat(entry_path, &st) < 0) {
485                                 if (ret == 0 && errno != ENOENT)
486                                         ret = -errno;
487                                 free(entry_path);
488                                 continue;
489                         }
490
491                         is_dir = S_ISDIR(st.st_mode);
492
493                 } else
494                         is_dir = de->d_type == DT_DIR;
495
496                 r = item_set_perms(i, entry_path);
497                 if (r < 0) {
498                         if (ret == 0 && r != -ENOENT)
499                                 ret = r;
500                         free(entry_path);
501                         continue;
502                 }
503
504                 if (is_dir) {
505                         r = recursive_relabel_children(i, entry_path);
506                         if (r < 0 && ret == 0)
507                                 ret = r;
508                 }
509
510                 free(entry_path);
511         }
512
513         closedir(d);
514
515         return ret;
516 }
517
518 static int recursive_relabel(Item *i, const char *path) {
519         int r;
520         struct stat st;
521
522         r = item_set_perms(i, path);
523         if (r < 0)
524                 return r;
525
526         if (lstat(path, &st) < 0)
527                 return -errno;
528
529         if (S_ISDIR(st.st_mode))
530                 r = recursive_relabel_children(i, path);
531
532         return r;
533 }
534
535 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
536         int r = 0, k;
537         glob_t g;
538         char **fn;
539
540         zero(g);
541
542         errno = 0;
543         if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
544
545                 if (k != GLOB_NOMATCH) {
546                         if (errno != 0)
547                                 errno = EIO;
548
549                         log_error("glob(%s) failed: %m", i->path);
550                         return -errno;
551                 }
552         }
553
554         STRV_FOREACH(fn, g.gl_pathv)
555                 if ((k = action(i, *fn)) < 0)
556                         r = k;
557
558         globfree(&g);
559         return r;
560 }
561
562 static int create_item(Item *i) {
563         int r;
564         mode_t u;
565         struct stat st;
566
567         assert(i);
568
569         switch (i->type) {
570
571         case IGNORE_PATH:
572         case REMOVE_PATH:
573         case RECURSIVE_REMOVE_PATH:
574                 return 0;
575
576         case CREATE_FILE:
577         case TRUNCATE_FILE: {
578                 int fd;
579
580                 u = umask(0);
581                 fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW|
582                           (i->type == TRUNCATE_FILE ? O_TRUNC : 0), i->mode);
583                 umask(u);
584
585                 if (fd < 0) {
586                         log_error("Failed to create file %s: %m", i->path);
587                         return -errno;
588                 }
589
590                 close_nointr_nofail(fd);
591
592                 if (stat(i->path, &st) < 0) {
593                         log_error("stat(%s) failed: %m", i->path);
594                         return -errno;
595                 }
596
597                 if (!S_ISREG(st.st_mode)) {
598                         log_error("%s is not a file.", i->path);
599                         return -EEXIST;
600                 }
601
602                 r = item_set_perms(i, i->path);
603                 if (r < 0)
604                         return r;
605
606                 break;
607         }
608
609         case TRUNCATE_DIRECTORY:
610         case CREATE_DIRECTORY:
611
612                 u = umask(0);
613                 mkdir_parents(i->path, 0755);
614                 r = mkdir(i->path, i->mode);
615                 umask(u);
616
617                 if (r < 0 && errno != EEXIST) {
618                         log_error("Failed to create directory %s: %m", i->path);
619                         return -errno;
620                 }
621
622                 if (stat(i->path, &st) < 0) {
623                         log_error("stat(%s) failed: %m", i->path);
624                         return -errno;
625                 }
626
627                 if (!S_ISDIR(st.st_mode)) {
628                         log_error("%s is not a directory.", i->path);
629                         return -EEXIST;
630                 }
631
632                 r = item_set_perms(i, i->path);
633                 if (r < 0)
634                         return r;
635
636                 break;
637
638         case CREATE_FIFO:
639
640                 u = umask(0);
641                 r = mkfifo(i->path, i->mode);
642                 umask(u);
643
644                 if (r < 0 && errno != EEXIST) {
645                         log_error("Failed to create fifo %s: %m", i->path);
646                         return -errno;
647                 }
648
649                 if (stat(i->path, &st) < 0) {
650                         log_error("stat(%s) failed: %m", i->path);
651                         return -errno;
652                 }
653
654                 if (!S_ISFIFO(st.st_mode)) {
655                         log_error("%s is not a fifo.", i->path);
656                         return -EEXIST;
657                 }
658
659                 r = item_set_perms(i, i->path);
660                 if (r < 0)
661                         return r;
662
663                 break;
664
665         case CREATE_SYMLINK: {
666                 char *x;
667
668                 r = symlink(i->argument, i->path);
669                 if (r < 0 && errno != EEXIST) {
670                         log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
671                         return -errno;
672                 }
673
674                 r = readlink_malloc(i->path, &x);
675                 if (r < 0) {
676                         log_error("readlink(%s) failed: %s", i->path, strerror(-r));
677                         return -errno;
678                 }
679
680                 if (!streq(i->argument, x)) {
681                         free(x);
682                         log_error("%s is not the right symlinks.", i->path);
683                         return -EEXIST;
684                 }
685
686                 free(x);
687                 break;
688         }
689
690         case CREATE_BLOCK_DEVICE:
691         case CREATE_CHAR_DEVICE: {
692
693                 u = umask(0);
694                 r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
695                 umask(u);
696
697                 if (r < 0 && errno != EEXIST) {
698                         log_error("Failed to create device node %s: %m", i->path);
699                         return -errno;
700                 }
701
702                 if (stat(i->path, &st) < 0) {
703                         log_error("stat(%s) failed: %m", i->path);
704                         return -errno;
705                 }
706
707                 if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
708                         log_error("%s is not a device node.", i->path);
709                         return -EEXIST;
710                 }
711
712                 r = item_set_perms(i, i->path);
713                 if (r < 0)
714                         return r;
715
716                 break;
717         }
718
719         case RELABEL_PATH:
720
721                 r = glob_item(i, item_set_perms);
722                 if (r < 0)
723                         return 0;
724                 break;
725
726         case RECURSIVE_RELABEL_PATH:
727
728                 r = glob_item(i, recursive_relabel);
729                 if (r < 0)
730                         return r;
731         }
732
733         log_debug("%s created successfully.", i->path);
734
735         return 0;
736 }
737
738 static int remove_item_instance(Item *i, const char *instance) {
739         int r;
740
741         assert(i);
742
743         switch (i->type) {
744
745         case CREATE_FILE:
746         case TRUNCATE_FILE:
747         case CREATE_DIRECTORY:
748         case CREATE_FIFO:
749         case CREATE_SYMLINK:
750         case CREATE_BLOCK_DEVICE:
751         case CREATE_CHAR_DEVICE:
752         case IGNORE_PATH:
753         case RELABEL_PATH:
754         case RECURSIVE_RELABEL_PATH:
755                 break;
756
757         case REMOVE_PATH:
758                 if (remove(instance) < 0 && errno != ENOENT) {
759                         log_error("remove(%s): %m", instance);
760                         return -errno;
761                 }
762
763                 break;
764
765         case TRUNCATE_DIRECTORY:
766         case RECURSIVE_REMOVE_PATH:
767                 r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
768                 if (r < 0 && r != -ENOENT) {
769                         log_error("rm_rf(%s): %s", instance, strerror(-r));
770                         return r;
771                 }
772
773                 break;
774         }
775
776         return 0;
777 }
778
779 static int remove_item(Item *i) {
780         int r = 0;
781
782         assert(i);
783
784         switch (i->type) {
785
786         case CREATE_FILE:
787         case TRUNCATE_FILE:
788         case CREATE_DIRECTORY:
789         case CREATE_FIFO:
790         case CREATE_SYMLINK:
791         case CREATE_CHAR_DEVICE:
792         case CREATE_BLOCK_DEVICE:
793         case IGNORE_PATH:
794         case RELABEL_PATH:
795         case RECURSIVE_RELABEL_PATH:
796                 break;
797
798         case REMOVE_PATH:
799         case TRUNCATE_DIRECTORY:
800         case RECURSIVE_REMOVE_PATH:
801                 r = glob_item(i, remove_item_instance);
802                 break;
803         }
804
805         return r;
806 }
807
808 static int process_item(Item *i) {
809         int r, q, p;
810
811         assert(i);
812
813         r = arg_create ? create_item(i) : 0;
814         q = arg_remove ? remove_item(i) : 0;
815         p = arg_clean ? clean_item(i) : 0;
816
817         if (r < 0)
818                 return r;
819
820         if (q < 0)
821                 return q;
822
823         return p;
824 }
825
826 static void item_free(Item *i) {
827         assert(i);
828
829         free(i->path);
830         free(i->argument);
831         free(i);
832 }
833
834 static bool item_equal(Item *a, Item *b) {
835         assert(a);
836         assert(b);
837
838         if (!streq_ptr(a->path, b->path))
839                 return false;
840
841         if (a->type != b->type)
842                 return false;
843
844         if (a->uid_set != b->uid_set ||
845             (a->uid_set && a->uid != b->uid))
846             return false;
847
848         if (a->gid_set != b->gid_set ||
849             (a->gid_set && a->gid != b->gid))
850             return false;
851
852         if (a->mode_set != b->mode_set ||
853             (a->mode_set && a->mode != b->mode))
854             return false;
855
856         if (a->age_set != b->age_set ||
857             (a->age_set && a->age != b->age))
858             return false;
859
860         if (a->type == CREATE_SYMLINK &&
861             !streq(a->argument, b->argument))
862                 return false;
863
864         if ((a->type == CREATE_CHAR_DEVICE ||
865              a->type == CREATE_BLOCK_DEVICE) &&
866             a->major_minor != b->major_minor)
867                 return false;
868
869         return true;
870 }
871
872 static int parse_line(const char *fname, unsigned line, const char *buffer) {
873         Item *i, *existing;
874         char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
875         char type;
876         Hashmap *h;
877         int r;
878
879         assert(fname);
880         assert(line >= 1);
881         assert(buffer);
882
883         i = new0(Item, 1);
884         if (!i) {
885                 log_error("Out of memory");
886                 return -ENOMEM;
887         }
888
889         if (sscanf(buffer,
890                    "%c "
891                    "%ms "
892                    "%ms "
893                    "%ms "
894                    "%ms "
895                    "%ms "
896                    "%ms",
897                    &type,
898                    &i->path,
899                    &mode,
900                    &user,
901                    &group,
902                    &age,
903                    &i->argument) < 2) {
904                 log_error("[%s:%u] Syntax error.", fname, line);
905                 r = -EIO;
906                 goto finish;
907         }
908
909         switch(type) {
910
911         case CREATE_FILE:
912         case TRUNCATE_FILE:
913         case CREATE_DIRECTORY:
914         case TRUNCATE_DIRECTORY:
915         case CREATE_FIFO:
916         case IGNORE_PATH:
917         case REMOVE_PATH:
918         case RECURSIVE_REMOVE_PATH:
919         case RELABEL_PATH:
920         case RECURSIVE_RELABEL_PATH:
921                 break;
922
923         case CREATE_SYMLINK:
924                 if (!i->argument) {
925                         log_error("[%s:%u] Symlink file requires argument.", fname, line);
926                         r = -EBADMSG;
927                         goto finish;
928                 }
929                 break;
930
931         case CREATE_CHAR_DEVICE:
932         case CREATE_BLOCK_DEVICE: {
933                 unsigned major, minor;
934
935                 if (!i->argument) {
936                         log_error("[%s:%u] Device file requires argument.", fname, line);
937                         r = -EBADMSG;
938                         goto finish;
939                 }
940
941                 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
942                         log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
943                         r = -EBADMSG;
944                         goto finish;
945                 }
946
947                 i->major_minor = makedev(major, minor);
948                 break;
949         }
950
951         default:
952                 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
953                 r = -EBADMSG;
954                 goto finish;
955         }
956
957         i->type = type;
958
959         if (!path_is_absolute(i->path)) {
960                 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
961                 r = -EBADMSG;
962                 goto finish;
963         }
964
965         path_kill_slashes(i->path);
966
967         if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
968                 r = 0;
969                 goto finish;
970         }
971
972         if (user && !streq(user, "-")) {
973                 const char *u = user;
974
975                 r = get_user_creds(&u, &i->uid, NULL, NULL);
976                 if (r < 0) {
977                         log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
978                         goto finish;
979                 }
980
981                 i->uid_set = true;
982         }
983
984         if (group && !streq(group, "-")) {
985                 const char *g = group;
986
987                 r = get_group_creds(&g, &i->gid);
988                 if (r < 0) {
989                         log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
990                         goto finish;
991                 }
992
993                 i->gid_set = true;
994         }
995
996         if (mode && !streq(mode, "-")) {
997                 unsigned m;
998
999                 if (sscanf(mode, "%o", &m) != 1) {
1000                         log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1001                         r = -ENOENT;
1002                         goto finish;
1003                 }
1004
1005                 i->mode = m;
1006                 i->mode_set = true;
1007         } else
1008                 i->mode =
1009                         i->type == CREATE_DIRECTORY ||
1010                         i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
1011
1012         if (age && !streq(age, "-")) {
1013                 if (parse_usec(age, &i->age) < 0) {
1014                         log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1015                         r = -EBADMSG;
1016                         goto finish;
1017                 }
1018
1019                 i->age_set = true;
1020         }
1021
1022         h = needs_glob(i->type) ? globs : items;
1023
1024         existing = hashmap_get(h, i->path);
1025         if (existing) {
1026
1027                 /* Two identical items are fine */
1028                 if (!item_equal(existing, i))
1029                         log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
1030
1031                 r = 0;
1032                 goto finish;
1033         }
1034
1035         r = hashmap_put(h, i->path, i);
1036         if (r < 0) {
1037                 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
1038                 goto finish;
1039         }
1040
1041         i = NULL;
1042         r = 0;
1043
1044 finish:
1045         free(user);
1046         free(group);
1047         free(mode);
1048         free(age);
1049
1050         if (i)
1051                 item_free(i);
1052
1053         return r;
1054 }
1055
1056 static int help(void) {
1057
1058         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1059                "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
1060                "  -h --help             Show this help\n"
1061                "     --create           Create marked files/directories\n"
1062                "     --clean            Clean up marked directories\n"
1063                "     --remove           Remove marked files/directories\n"
1064                "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
1065                program_invocation_short_name);
1066
1067         return 0;
1068 }
1069
1070 static int parse_argv(int argc, char *argv[]) {
1071
1072         enum {
1073                 ARG_CREATE,
1074                 ARG_CLEAN,
1075                 ARG_REMOVE,
1076                 ARG_PREFIX
1077         };
1078
1079         static const struct option options[] = {
1080                 { "help",      no_argument,       NULL, 'h'           },
1081                 { "create",    no_argument,       NULL, ARG_CREATE    },
1082                 { "clean",     no_argument,       NULL, ARG_CLEAN     },
1083                 { "remove",    no_argument,       NULL, ARG_REMOVE    },
1084                 { "prefix",    required_argument, NULL, ARG_PREFIX    },
1085                 { NULL,        0,                 NULL, 0             }
1086         };
1087
1088         int c;
1089
1090         assert(argc >= 0);
1091         assert(argv);
1092
1093         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1094
1095                 switch (c) {
1096
1097                 case 'h':
1098                         help();
1099                         return 0;
1100
1101                 case ARG_CREATE:
1102                         arg_create = true;
1103                         break;
1104
1105                 case ARG_CLEAN:
1106                         arg_clean = true;
1107                         break;
1108
1109                 case ARG_REMOVE:
1110                         arg_remove = true;
1111                         break;
1112
1113                 case ARG_PREFIX:
1114                         arg_prefix = optarg;
1115                         break;
1116
1117                 case '?':
1118                         return -EINVAL;
1119
1120                 default:
1121                         log_error("Unknown option code %c", c);
1122                         return -EINVAL;
1123                 }
1124         }
1125
1126         if (!arg_clean && !arg_create && !arg_remove) {
1127                 log_error("You need to specify at least one of --clean, --create or --remove.");
1128                 return -EINVAL;
1129         }
1130
1131         return 1;
1132 }
1133
1134 static int read_config_file(const char *fn, bool ignore_enoent) {
1135         FILE *f;
1136         unsigned v = 0;
1137         int r = 0;
1138
1139         assert(fn);
1140
1141         f = fopen(fn, "re");
1142         if (!f) {
1143
1144                 if (ignore_enoent && errno == ENOENT)
1145                         return 0;
1146
1147                 log_error("Failed to open %s: %m", fn);
1148                 return -errno;
1149         }
1150
1151         log_debug("apply: %s\n", fn);
1152         for (;;) {
1153                 char line[LINE_MAX], *l;
1154                 int k;
1155
1156                 if (!(fgets(line, sizeof(line), f)))
1157                         break;
1158
1159                 v++;
1160
1161                 l = strstrip(line);
1162                 if (*l == '#' || *l == 0)
1163                         continue;
1164
1165                 if ((k = parse_line(fn, v, l)) < 0)
1166                         if (r == 0)
1167                                 r = k;
1168         }
1169
1170         if (ferror(f)) {
1171                 log_error("Failed to read from file %s: %m", fn);
1172                 if (r == 0)
1173                         r = -EIO;
1174         }
1175
1176         fclose(f);
1177
1178         return r;
1179 }
1180
1181 int main(int argc, char *argv[]) {
1182         int r;
1183         Item *i;
1184         Iterator iterator;
1185
1186         r = parse_argv(argc, argv);
1187         if (r <= 0)
1188                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1189
1190         log_set_target(LOG_TARGET_AUTO);
1191         log_parse_environment();
1192         log_open();
1193
1194         umask(0022);
1195
1196         label_init();
1197
1198         items = hashmap_new(string_hash_func, string_compare_func);
1199         globs = hashmap_new(string_hash_func, string_compare_func);
1200
1201         if (!items || !globs) {
1202                 log_error("Out of memory");
1203                 r = EXIT_FAILURE;
1204                 goto finish;
1205         }
1206
1207         r = EXIT_SUCCESS;
1208
1209         if (optind < argc) {
1210                 int j;
1211
1212                 for (j = optind; j < argc; j++)
1213                         if (read_config_file(argv[j], false) < 0)
1214                                 r = EXIT_FAILURE;
1215
1216         } else {
1217                 char **files, **f;
1218
1219                 r = conf_files_list(&files, ".conf",
1220                                     "/run/tmpfiles.d",
1221                                     "/etc/tmpfiles.d",
1222                                     "/usr/local/lib/tmpfiles.d",
1223                                     "/usr/lib/tmpfiles.d",
1224                                     NULL);
1225                 if (r < 0) {
1226                         r = EXIT_FAILURE;
1227                         log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
1228                         goto finish;
1229                 }
1230
1231                 STRV_FOREACH(f, files) {
1232                         if (read_config_file(*f, true) < 0)
1233                                 r = EXIT_FAILURE;
1234                 }
1235
1236                 strv_free(files);
1237         }
1238
1239         HASHMAP_FOREACH(i, globs, iterator)
1240                 process_item(i);
1241
1242         HASHMAP_FOREACH(i, items, iterator)
1243                 process_item(i);
1244
1245 finish:
1246         while ((i = hashmap_steal_first(items)))
1247                 item_free(i);
1248
1249         while ((i = hashmap_steal_first(globs)))
1250                 item_free(i);
1251
1252         hashmap_free(items);
1253         hashmap_free(globs);
1254
1255         set_free_free(unix_sockets);
1256
1257         label_finish();
1258
1259         return r;
1260 }