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