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