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