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