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