]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tmpfiles/tmpfiles.c
tmpfiles: add 'a' type to set ACLs
[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 Copyright 2015 Zbigniew Jędrzejewski-Szmek
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <string.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 <glob.h>
38 #include <fnmatch.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/xattr.h>
43
44 #include "log.h"
45 #include "util.h"
46 #include "macro.h"
47 #include "missing.h"
48 #include "mkdir.h"
49 #include "path-util.h"
50 #include "strv.h"
51 #include "label.h"
52 #include "set.h"
53 #include "conf-files.h"
54 #include "capability.h"
55 #include "specifier.h"
56 #include "build.h"
57 #include "copy.h"
58 #include "selinux-util.h"
59 #include "btrfs-util.h"
60 #include "acl-util.h"
61
62 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
63 * them in the file system. This is intended to be used to create
64 * properly owned directories beneath /tmp, /var/tmp, /run, which are
65 * volatile and hence need to be recreated on bootup. */
66
67 typedef enum ItemType {
68 /* These ones take file names */
69 CREATE_FILE = 'f',
70 TRUNCATE_FILE = 'F',
71 CREATE_DIRECTORY = 'd',
72 TRUNCATE_DIRECTORY = 'D',
73 CREATE_SUBVOLUME = 'v',
74 CREATE_FIFO = 'p',
75 CREATE_SYMLINK = 'L',
76 CREATE_CHAR_DEVICE = 'c',
77 CREATE_BLOCK_DEVICE = 'b',
78 COPY_FILES = 'C',
79 SET_XATTR = 't',
80 SET_ACL = 'a',
81
82 /* These ones take globs */
83 WRITE_FILE = 'w',
84 IGNORE_PATH = 'x',
85 IGNORE_DIRECTORY_PATH = 'X',
86 REMOVE_PATH = 'r',
87 RECURSIVE_REMOVE_PATH = 'R',
88 ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
89 RELABEL_PATH = 'z',
90 RECURSIVE_RELABEL_PATH = 'Z',
91 } ItemType;
92
93 typedef struct Item {
94 ItemType type;
95
96 char *path;
97 char *argument;
98 char **xattrs;
99 #ifdef HAVE_ACL
100 acl_t acl_access;
101 acl_t acl_default;
102 #endif
103 uid_t uid;
104 gid_t gid;
105 mode_t mode;
106 usec_t age;
107
108 dev_t major_minor;
109
110 bool uid_set:1;
111 bool gid_set:1;
112 bool mode_set:1;
113 bool age_set:1;
114 bool mask_perms:1;
115
116 bool keep_first_level:1;
117
118 bool force:1;
119
120 bool done:1;
121 } Item;
122
123 typedef struct ItemArray {
124 Item *items;
125 size_t count;
126 size_t size;
127 } ItemArray;
128
129 static bool arg_create = false;
130 static bool arg_clean = false;
131 static bool arg_remove = false;
132 static bool arg_boot = false;
133
134 static char **arg_include_prefixes = NULL;
135 static char **arg_exclude_prefixes = NULL;
136 static char *arg_root = NULL;
137
138 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
139
140 #define MAX_DEPTH 256
141
142 static Hashmap *items = NULL, *globs = NULL;
143 static Set *unix_sockets = NULL;
144
145 static bool needs_glob(ItemType t) {
146 return IN_SET(t,
147 WRITE_FILE,
148 IGNORE_PATH,
149 IGNORE_DIRECTORY_PATH,
150 REMOVE_PATH,
151 RECURSIVE_REMOVE_PATH,
152 ADJUST_MODE,
153 RELABEL_PATH,
154 RECURSIVE_RELABEL_PATH);
155 }
156
157 static bool takes_ownership(ItemType t) {
158 return IN_SET(t,
159 CREATE_FILE,
160 TRUNCATE_FILE,
161 CREATE_DIRECTORY,
162 TRUNCATE_DIRECTORY,
163 CREATE_SUBVOLUME,
164 CREATE_FIFO,
165 CREATE_SYMLINK,
166 CREATE_CHAR_DEVICE,
167 CREATE_BLOCK_DEVICE,
168 COPY_FILES,
169
170 WRITE_FILE,
171 IGNORE_PATH,
172 IGNORE_DIRECTORY_PATH,
173 REMOVE_PATH,
174 RECURSIVE_REMOVE_PATH);
175 }
176
177 static struct Item* find_glob(Hashmap *h, const char *match) {
178 ItemArray *j;
179 Iterator i;
180
181 HASHMAP_FOREACH(j, h, i) {
182 unsigned n;
183
184 for (n = 0; n < j->count; n++) {
185 Item *item = j->items + n;
186
187 if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
188 return item;
189 }
190 }
191
192 return NULL;
193 }
194
195 static void load_unix_sockets(void) {
196 _cleanup_fclose_ FILE *f = NULL;
197 char line[LINE_MAX];
198
199 if (unix_sockets)
200 return;
201
202 /* We maintain a cache of the sockets we found in
203 * /proc/net/unix to speed things up a little. */
204
205 unix_sockets = set_new(&string_hash_ops);
206 if (!unix_sockets)
207 return;
208
209 f = fopen("/proc/net/unix", "re");
210 if (!f)
211 return;
212
213 /* Skip header */
214 if (!fgets(line, sizeof(line), f))
215 goto fail;
216
217 for (;;) {
218 char *p, *s;
219 int k;
220
221 if (!fgets(line, sizeof(line), f))
222 break;
223
224 truncate_nl(line);
225
226 p = strchr(line, ':');
227 if (!p)
228 continue;
229
230 if (strlen(p) < 37)
231 continue;
232
233 p += 37;
234 p += strspn(p, WHITESPACE);
235 p += strcspn(p, WHITESPACE); /* skip one more word */
236 p += strspn(p, WHITESPACE);
237
238 if (*p != '/')
239 continue;
240
241 s = strdup(p);
242 if (!s)
243 goto fail;
244
245 path_kill_slashes(s);
246
247 k = set_consume(unix_sockets, s);
248 if (k < 0 && k != -EEXIST)
249 goto fail;
250 }
251
252 return;
253
254 fail:
255 set_free_free(unix_sockets);
256 unix_sockets = NULL;
257 }
258
259 static bool unix_socket_alive(const char *fn) {
260 assert(fn);
261
262 load_unix_sockets();
263
264 if (unix_sockets)
265 return !!set_get(unix_sockets, (char*) fn);
266
267 /* We don't know, so assume yes */
268 return true;
269 }
270
271 static int dir_is_mount_point(DIR *d, const char *subdir) {
272
273 union file_handle_union h = FILE_HANDLE_INIT;
274 int mount_id_parent, mount_id;
275 int r_p, r;
276
277 r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
278 if (r_p < 0)
279 r_p = -errno;
280
281 h.handle.handle_bytes = MAX_HANDLE_SZ;
282 r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
283 if (r < 0)
284 r = -errno;
285
286 /* got no handle; make no assumptions, return error */
287 if (r_p < 0 && r < 0)
288 return r_p;
289
290 /* got both handles; if they differ, it is a mount point */
291 if (r_p >= 0 && r >= 0)
292 return mount_id_parent != mount_id;
293
294 /* got only one handle; assume different mount points if one
295 * of both queries was not supported by the filesystem */
296 if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP)
297 return true;
298
299 /* return error */
300 if (r_p < 0)
301 return r_p;
302 return r;
303 }
304
305 static int dir_cleanup(
306 Item *i,
307 const char *p,
308 DIR *d,
309 const struct stat *ds,
310 usec_t cutoff,
311 dev_t rootdev,
312 bool mountpoint,
313 int maxdepth,
314 bool keep_this_level) {
315
316 struct dirent *dent;
317 struct timespec times[2];
318 bool deleted = false;
319 int r = 0;
320
321 while ((dent = readdir(d))) {
322 struct stat s;
323 usec_t age;
324 _cleanup_free_ char *sub_path = NULL;
325
326 if (streq(dent->d_name, ".") ||
327 streq(dent->d_name, ".."))
328 continue;
329
330 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
331 if (errno == ENOENT)
332 continue;
333
334 /* FUSE, NFS mounts, SELinux might return EACCES */
335 if (errno == EACCES)
336 log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
337 else
338 log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
339 r = -errno;
340 continue;
341 }
342
343 /* Stay on the same filesystem */
344 if (s.st_dev != rootdev)
345 continue;
346
347 /* Try to detect bind mounts of the same filesystem instance; they
348 * do not differ in device major/minors. This type of query is not
349 * supported on all kernels or filesystem types though. */
350 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0)
351 continue;
352
353 /* Do not delete read-only files owned by root */
354 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
355 continue;
356
357 sub_path = strjoin(p, "/", dent->d_name, NULL);
358 if (!sub_path) {
359 r = log_oom();
360 goto finish;
361 }
362
363 /* Is there an item configured for this path? */
364 if (hashmap_get(items, sub_path))
365 continue;
366
367 if (find_glob(globs, sub_path))
368 continue;
369
370 if (S_ISDIR(s.st_mode)) {
371
372 if (mountpoint &&
373 streq(dent->d_name, "lost+found") &&
374 s.st_uid == 0)
375 continue;
376
377 if (maxdepth <= 0)
378 log_warning("Reached max depth on %s.", sub_path);
379 else {
380 _cleanup_closedir_ DIR *sub_dir;
381 int q;
382
383 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
384 if (!sub_dir) {
385 if (errno != ENOENT) {
386 log_error_errno(errno, "opendir(%s/%s) failed: %m", p, dent->d_name);
387 r = -errno;
388 }
389
390 continue;
391 }
392
393 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
394 if (q < 0)
395 r = q;
396 }
397
398 /* Note: if you are wondering why we don't
399 * support the sticky bit for excluding
400 * directories from cleaning like we do it for
401 * other file system objects: well, the sticky
402 * bit already has a meaning for directories,
403 * so we don't want to overload that. */
404
405 if (keep_this_level)
406 continue;
407
408 /* Ignore ctime, we change it when deleting */
409 age = MAX(timespec_load(&s.st_mtim),
410 timespec_load(&s.st_atim));
411 if (age >= cutoff)
412 continue;
413
414 if (i->type != IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
415 log_debug("rmdir '%s'", sub_path);
416
417 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
418 if (errno != ENOENT && errno != ENOTEMPTY) {
419 log_error_errno(errno, "rmdir(%s): %m", sub_path);
420 r = -errno;
421 }
422 }
423 }
424
425 } else {
426 /* Skip files for which the sticky bit is
427 * set. These are semantics we define, and are
428 * unknown elsewhere. See XDG_RUNTIME_DIR
429 * specification for details. */
430 if (s.st_mode & S_ISVTX)
431 continue;
432
433 if (mountpoint && S_ISREG(s.st_mode)) {
434 if (streq(dent->d_name, ".journal") &&
435 s.st_uid == 0)
436 continue;
437
438 if (streq(dent->d_name, "aquota.user") ||
439 streq(dent->d_name, "aquota.group"))
440 continue;
441 }
442
443 /* Ignore sockets that are listed in /proc/net/unix */
444 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
445 continue;
446
447 /* Ignore device nodes */
448 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
449 continue;
450
451 /* Keep files on this level around if this is
452 * requested */
453 if (keep_this_level)
454 continue;
455
456 age = MAX3(timespec_load(&s.st_mtim),
457 timespec_load(&s.st_atim),
458 timespec_load(&s.st_ctim));
459
460 if (age >= cutoff)
461 continue;
462
463 log_debug("unlink '%s'", sub_path);
464
465 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
466 if (errno != ENOENT) {
467 log_error_errno(errno, "unlink(%s): %m", sub_path);
468 r = -errno;
469 }
470 }
471
472 deleted = true;
473 }
474 }
475
476 finish:
477 if (deleted) {
478 /* Restore original directory timestamps */
479 times[0] = ds->st_atim;
480 times[1] = ds->st_mtim;
481
482 if (futimens(dirfd(d), times) < 0)
483 log_error_errno(errno, "utimensat(%s): %m", p);
484 }
485
486 return r;
487 }
488
489 static int item_set_perms(Item *i, const char *path) {
490 struct stat st;
491 bool st_valid;
492
493 assert(i);
494 assert(path);
495
496 st_valid = stat(path, &st) == 0;
497
498 /* not using i->path directly because it may be a glob */
499 if (i->mode_set) {
500 mode_t m = i->mode;
501
502 if (i->mask_perms && st_valid) {
503 if (!(st.st_mode & 0111))
504 m &= ~0111;
505 if (!(st.st_mode & 0222))
506 m &= ~0222;
507 if (!(st.st_mode & 0444))
508 m &= ~0444;
509 if (!S_ISDIR(st.st_mode))
510 m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
511 }
512
513 if (!st_valid || m != (st.st_mode & 07777)) {
514 if (chmod(path, m) < 0)
515 return log_error_errno(errno, "chmod(%s) failed: %m", path);
516 }
517 }
518
519 if ((!st_valid || (i->uid != st.st_uid || i->gid != st.st_gid)) &&
520 (i->uid_set || i->gid_set))
521 if (chown(path,
522 i->uid_set ? i->uid : UID_INVALID,
523 i->gid_set ? i->gid : GID_INVALID) < 0)
524
525 return log_error_errno(errno, "chown(%s) failed: %m", path);
526
527 return label_fix(path, false, false);
528 }
529
530 static int get_xattrs_from_arg(Item *i) {
531 char *xattr;
532 const char *p;
533 int r;
534
535 assert(i);
536 assert(i->argument);
537
538 p = i->argument;
539
540 while ((r = unquote_first_word(&p, &xattr, false)) > 0) {
541 _cleanup_free_ char *tmp = NULL, *name = NULL,
542 *value = NULL, *value2 = NULL, *_xattr = xattr;
543
544 r = split_pair(xattr, "=", &name, &value);
545 if (r < 0) {
546 log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr);
547 continue;
548 }
549
550 if (strempty(name) || strempty(value)) {
551 log_warning("Malformed xattr found: \"%s\" - ignoring.", xattr);
552 continue;
553 }
554
555 tmp = unquote(value, "\"");
556 if (!tmp)
557 return log_oom();
558
559 value2 = cunescape(tmp);
560 if (!value2)
561 return log_oom();
562
563 if (strv_push_pair(&i->xattrs, name, value2) < 0)
564 return log_oom();
565 name = value2 = NULL;
566 }
567
568 return r;
569 }
570
571 static int item_set_xattrs(Item *i, const char *path) {
572 char **name, **value;
573
574 assert(i);
575 assert(path);
576
577 STRV_FOREACH_PAIR(name, value, i->xattrs) {
578 int n;
579
580 n = strlen(*value);
581 if (lsetxattr(path, *name, *value, n, 0) < 0) {
582 log_error("Setting extended attribute %s=%s on %s failed: %m",
583 *name, *value, path);
584 return -errno;
585 }
586 }
587 return 0;
588 }
589
590 static int get_acls_from_arg(Item *item) {
591 #ifdef HAVE_ACL
592 int r;
593 _cleanup_(acl_freep) acl_t a = NULL, d = NULL;
594
595 assert(item);
596
597 r = parse_acl(item->argument, &item->acl_access, &item->acl_default);
598 if (r < 0)
599 log_warning_errno(errno, "Failed to parse ACL \"%s\": %m. Ignoring",
600 item->argument);
601 #else
602 log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
603 #endif
604
605 return 0;
606 }
607
608 static int item_set_acl(Item *item, const char *path) {
609 #ifdef HAVE_ACL
610 int r;
611
612 assert(item);
613 assert(path);
614
615 if (item->acl_access) {
616 r = acl_set_file(path, ACL_TYPE_ACCESS, item->acl_access);
617 if (r < 0) {
618 _cleanup_(acl_free_charpp) char *t;
619
620 t = acl_to_any_text(item->acl_access, NULL, ',', TEXT_ABBREVIATE);
621 return log_error_errno(errno,
622 "Setting access ACL \"%s\" on %s failed: %m",
623 strna(t), path);
624 }
625 }
626
627 if (item->acl_default) {
628 r = acl_set_file(path, ACL_TYPE_DEFAULT, item->acl_default);
629 if (r < 0) {
630 _cleanup_(acl_free_charpp) char *t;
631
632 t = acl_to_any_text(item->acl_default, NULL, ',', TEXT_ABBREVIATE);
633 return log_error_errno(errno,
634 "Setting default ACL \"%s\" on %s failed: %m",
635 strna(t), path);
636 }
637 }
638 #endif
639
640 return 0;
641 }
642
643 static int write_one_file(Item *i, const char *path) {
644 _cleanup_close_ int fd = -1;
645 int flags, r = 0;
646 struct stat st;
647
648 assert(i);
649 assert(path);
650
651 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
652 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
653
654 RUN_WITH_UMASK(0000) {
655 mac_selinux_create_file_prepare(path, S_IFREG);
656 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
657 mac_selinux_create_file_clear();
658 }
659
660 if (fd < 0) {
661 if (i->type == WRITE_FILE && errno == ENOENT)
662 return 0;
663
664 log_error_errno(errno, "Failed to create file %s: %m", path);
665 return -errno;
666 }
667
668 if (i->argument) {
669 _cleanup_free_ char *unescaped;
670 ssize_t n;
671 size_t l;
672
673 unescaped = cunescape(i->argument);
674 if (!unescaped)
675 return log_oom();
676
677 l = strlen(unescaped);
678 n = write(fd, unescaped, l);
679
680 if (n < 0 || (size_t) n < l) {
681 log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
682 return n < 0 ? n : -EIO;
683 }
684 }
685
686 fd = safe_close(fd);
687
688 if (stat(path, &st) < 0)
689 return log_error_errno(errno, "stat(%s) failed: %m", path);
690
691 if (!S_ISREG(st.st_mode)) {
692 log_error("%s is not a file.", path);
693 return -EEXIST;
694 }
695
696 r = item_set_perms(i, path);
697 if (r < 0)
698 return r;
699
700 return 0;
701 }
702
703 static int item_set_perms_children(Item *i, const char *path) {
704 _cleanup_closedir_ DIR *d;
705 int r = 0;
706
707 assert(i);
708 assert(path);
709
710 /* This returns the first error we run into, but nevertheless
711 * tries to go on */
712
713 d = opendir(path);
714 if (!d)
715 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
716
717 for (;;) {
718 _cleanup_free_ char *p = NULL;
719 struct dirent *de;
720 int q;
721
722 errno = 0;
723 de = readdir(d);
724 if (!de) {
725 if (errno != 0 && r == 0)
726 r = -errno;
727
728 break;
729 }
730
731 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
732 continue;
733
734 p = strjoin(path, "/", de->d_name, NULL);
735 if (!p)
736 return -ENOMEM;
737
738 q = item_set_perms(i, p);
739 if (q < 0 && q != -ENOENT && r == 0)
740 r = q;
741
742 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
743 q = item_set_perms_children(i, p);
744 if (q < 0 && r == 0)
745 r = q;
746 }
747 }
748
749 return r;
750 }
751
752 static int item_set_perms_recursive(Item *i, const char *path) {
753 int r, q;
754
755 assert(i);
756 assert(path);
757
758 r = item_set_perms(i, path);
759 if (r < 0)
760 return r;
761
762 q = item_set_perms_children(i, path);
763 if (q < 0 && r == 0)
764 r = q;
765
766 return r;
767 }
768
769 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
770 _cleanup_globfree_ glob_t g = {};
771 int r = 0, k;
772 char **fn;
773
774 errno = 0;
775 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
776 if (k != 0 && k != GLOB_NOMATCH) {
777 if (errno == 0)
778 errno = EIO;
779
780 log_error_errno(errno, "glob(%s) failed: %m", i->path);
781 return -errno;
782 }
783
784 STRV_FOREACH(fn, g.gl_pathv) {
785 k = action(i, *fn);
786 if (k < 0 && r == 0)
787 r = k;
788 }
789
790 return r;
791 }
792
793 static int create_item(Item *i) {
794 struct stat st;
795 int r = 0;
796
797 assert(i);
798
799 switch (i->type) {
800
801 case IGNORE_PATH:
802 case IGNORE_DIRECTORY_PATH:
803 case REMOVE_PATH:
804 case RECURSIVE_REMOVE_PATH:
805 return 0;
806
807 case CREATE_FILE:
808 case TRUNCATE_FILE:
809 r = write_one_file(i, i->path);
810 if (r < 0)
811 return r;
812 break;
813
814 case COPY_FILES:
815 r = copy_tree(i->argument, i->path, false);
816 if (r < 0) {
817 struct stat a, b;
818
819 if (r != -EEXIST)
820 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
821
822 if (stat(i->argument, &a) < 0)
823 return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
824
825 if (stat(i->path, &b) < 0)
826 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
827
828 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
829 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
830 return 0;
831 }
832 }
833
834 r = item_set_perms(i, i->path);
835 if (r < 0)
836 return r;
837
838 break;
839
840 case WRITE_FILE:
841 r = glob_item(i, write_one_file);
842 if (r < 0)
843 return r;
844
845 break;
846
847 case CREATE_DIRECTORY:
848 case TRUNCATE_DIRECTORY:
849 case CREATE_SUBVOLUME:
850
851 RUN_WITH_UMASK(0000)
852 mkdir_parents_label(i->path, 0755);
853
854 if (i->type == CREATE_SUBVOLUME) {
855 RUN_WITH_UMASK((~i->mode) & 0777)
856 r = btrfs_subvol_make(i->path);
857 } else
858 r = 0;
859
860 if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) {
861 RUN_WITH_UMASK(0000)
862 r = mkdir_label(i->path, i->mode);
863 }
864
865 if (r < 0) {
866 if (r != -EEXIST)
867 return log_error_errno(r, "Failed to create directory or subvolume %s: %m", i->path);
868
869 if (stat(i->path, &st) < 0)
870 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
871
872 if (!S_ISDIR(st.st_mode)) {
873 log_debug("%s already exists and is not a directory.", i->path);
874 return 0;
875 }
876 }
877
878 r = item_set_perms(i, i->path);
879 if (r < 0)
880 return r;
881
882 break;
883
884 case CREATE_FIFO:
885
886 RUN_WITH_UMASK(0000) {
887 mac_selinux_create_file_prepare(i->path, S_IFIFO);
888 r = mkfifo(i->path, i->mode);
889 mac_selinux_create_file_clear();
890 }
891
892 if (r < 0) {
893 if (errno != EEXIST)
894 return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
895
896 if (stat(i->path, &st) < 0)
897 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
898
899 if (!S_ISFIFO(st.st_mode)) {
900
901 if (i->force) {
902
903 RUN_WITH_UMASK(0000) {
904 mac_selinux_create_file_prepare(i->path, S_IFIFO);
905 r = mkfifo_atomic(i->path, i->mode);
906 mac_selinux_create_file_clear();
907 }
908
909 if (r < 0)
910 return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
911 } else {
912 log_debug("%s is not a fifo.", i->path);
913 return 0;
914 }
915 }
916 }
917
918 r = item_set_perms(i, i->path);
919 if (r < 0)
920 return r;
921
922 break;
923
924 case CREATE_SYMLINK:
925
926 mac_selinux_create_file_prepare(i->path, S_IFLNK);
927 r = symlink(i->argument, i->path);
928 mac_selinux_create_file_clear();
929
930 if (r < 0) {
931 _cleanup_free_ char *x = NULL;
932
933 if (errno != EEXIST)
934 return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path);
935
936 r = readlink_malloc(i->path, &x);
937 if (r < 0 || !streq(i->argument, x)) {
938
939 if (i->force) {
940 mac_selinux_create_file_prepare(i->path, S_IFLNK);
941 r = symlink_atomic(i->argument, i->path);
942 mac_selinux_create_file_clear();
943
944 if (r < 0)
945 return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
946 } else {
947 log_debug("%s is not a symlink or does not point to the correct path.", i->path);
948 return 0;
949 }
950 }
951 }
952
953 break;
954
955 case CREATE_BLOCK_DEVICE:
956 case CREATE_CHAR_DEVICE: {
957 mode_t file_type;
958
959 if (have_effective_cap(CAP_MKNOD) == 0) {
960 /* In a container we lack CAP_MKNOD. We
961 shouldn't attempt to create the device node in
962 that case to avoid noise, and we don't support
963 virtualized devices in containers anyway. */
964
965 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
966 return 0;
967 }
968
969 file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
970
971 RUN_WITH_UMASK(0000) {
972 mac_selinux_create_file_prepare(i->path, file_type);
973 r = mknod(i->path, i->mode | file_type, i->major_minor);
974 mac_selinux_create_file_clear();
975 }
976
977 if (r < 0) {
978 if (errno == EPERM) {
979 log_debug("We lack permissions, possibly because of cgroup configuration; "
980 "skipping creation of device node %s.", i->path);
981 return 0;
982 }
983
984 if (errno != EEXIST)
985 return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
986
987 if (stat(i->path, &st) < 0)
988 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
989
990 if ((st.st_mode & S_IFMT) != file_type) {
991
992 if (i->force) {
993
994 RUN_WITH_UMASK(0000) {
995 mac_selinux_create_file_prepare(i->path, file_type);
996 r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
997 mac_selinux_create_file_clear();
998 }
999
1000 if (r < 0)
1001 return log_error_errno(r, "Failed to create device node %s: %m", i->path);
1002 } else {
1003 log_debug("%s is not a device node.", i->path);
1004 return 0;
1005 }
1006 }
1007 }
1008
1009 r = item_set_perms(i, i->path);
1010 if (r < 0)
1011 return r;
1012
1013 break;
1014 }
1015
1016 case ADJUST_MODE:
1017 case RELABEL_PATH:
1018
1019 r = glob_item(i, item_set_perms);
1020 if (r < 0)
1021 return r;
1022 break;
1023
1024 case RECURSIVE_RELABEL_PATH:
1025
1026 r = glob_item(i, item_set_perms_recursive);
1027 if (r < 0)
1028 return r;
1029 break;
1030
1031 case SET_XATTR:
1032 r = item_set_xattrs(i, i->path);
1033 if (r < 0)
1034 return r;
1035 break;
1036
1037 case SET_ACL:
1038 r = item_set_acl(i, i->path);
1039 if (r < 0)
1040 return r;
1041 }
1042
1043 log_debug("%s created successfully.", i->path);
1044
1045 return 0;
1046 }
1047
1048 static int remove_item_instance(Item *i, const char *instance) {
1049 int r;
1050
1051 assert(i);
1052
1053 switch (i->type) {
1054
1055 case CREATE_FILE:
1056 case TRUNCATE_FILE:
1057 case CREATE_DIRECTORY:
1058 case CREATE_SUBVOLUME:
1059 case CREATE_FIFO:
1060 case CREATE_SYMLINK:
1061 case CREATE_BLOCK_DEVICE:
1062 case CREATE_CHAR_DEVICE:
1063 case IGNORE_PATH:
1064 case IGNORE_DIRECTORY_PATH:
1065 case ADJUST_MODE:
1066 case RELABEL_PATH:
1067 case RECURSIVE_RELABEL_PATH:
1068 case WRITE_FILE:
1069 case COPY_FILES:
1070 case SET_XATTR:
1071 case SET_ACL:
1072 break;
1073
1074 case REMOVE_PATH:
1075 if (remove(instance) < 0 && errno != ENOENT)
1076 return log_error_errno(errno, "rm(%s): %m", instance);
1077
1078 break;
1079
1080 case TRUNCATE_DIRECTORY:
1081 case RECURSIVE_REMOVE_PATH:
1082 /* FIXME: we probably should use dir_cleanup() here
1083 * instead of rm_rf() so that 'x' is honoured. */
1084 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
1085 if (r < 0 && r != -ENOENT)
1086 return log_error_errno(r, "rm_rf(%s): %m", instance);
1087
1088 break;
1089 }
1090
1091 return 0;
1092 }
1093
1094 static int remove_item(Item *i) {
1095 int r = 0;
1096
1097 assert(i);
1098
1099 switch (i->type) {
1100
1101 case CREATE_FILE:
1102 case TRUNCATE_FILE:
1103 case CREATE_DIRECTORY:
1104 case CREATE_SUBVOLUME:
1105 case CREATE_FIFO:
1106 case CREATE_SYMLINK:
1107 case CREATE_CHAR_DEVICE:
1108 case CREATE_BLOCK_DEVICE:
1109 case IGNORE_PATH:
1110 case IGNORE_DIRECTORY_PATH:
1111 case ADJUST_MODE:
1112 case RELABEL_PATH:
1113 case RECURSIVE_RELABEL_PATH:
1114 case WRITE_FILE:
1115 case COPY_FILES:
1116 case SET_XATTR:
1117 case SET_ACL:
1118 break;
1119
1120 case REMOVE_PATH:
1121 case TRUNCATE_DIRECTORY:
1122 case RECURSIVE_REMOVE_PATH:
1123 r = glob_item(i, remove_item_instance);
1124 break;
1125 }
1126
1127 return r;
1128 }
1129
1130 static int clean_item_instance(Item *i, const char* instance) {
1131 _cleanup_closedir_ DIR *d = NULL;
1132 struct stat s, ps;
1133 bool mountpoint;
1134 int r;
1135 usec_t cutoff, n;
1136
1137 assert(i);
1138
1139 if (!i->age_set)
1140 return 0;
1141
1142 n = now(CLOCK_REALTIME);
1143 if (n < i->age)
1144 return 0;
1145
1146 cutoff = n - i->age;
1147
1148 d = opendir(instance);
1149 if (!d) {
1150 if (errno == ENOENT || errno == ENOTDIR)
1151 return 0;
1152
1153 log_error_errno(errno, "Failed to open directory %s: %m", i->path);
1154 return -errno;
1155 }
1156
1157 if (fstat(dirfd(d), &s) < 0)
1158 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1159
1160 if (!S_ISDIR(s.st_mode)) {
1161 log_error("%s is not a directory.", i->path);
1162 return -ENOTDIR;
1163 }
1164
1165 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
1166 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
1167
1168 mountpoint = s.st_dev != ps.st_dev ||
1169 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
1170
1171 r = dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
1172 MAX_DEPTH, i->keep_first_level);
1173 return r;
1174 }
1175
1176 static int clean_item(Item *i) {
1177 int r = 0;
1178
1179 assert(i);
1180
1181 switch (i->type) {
1182 case CREATE_DIRECTORY:
1183 case CREATE_SUBVOLUME:
1184 case TRUNCATE_DIRECTORY:
1185 case IGNORE_PATH:
1186 case COPY_FILES:
1187 clean_item_instance(i, i->path);
1188 break;
1189 case IGNORE_DIRECTORY_PATH:
1190 r = glob_item(i, clean_item_instance);
1191 break;
1192 default:
1193 break;
1194 }
1195
1196 return r;
1197 }
1198
1199 static int process_item_array(ItemArray *array);
1200
1201 static int process_item(Item *i) {
1202 int r, q, p, t = 0;
1203 _cleanup_free_ char *prefix = NULL;
1204
1205 assert(i);
1206
1207 if (i->done)
1208 return 0;
1209
1210 i->done = true;
1211
1212 prefix = malloc(strlen(i->path) + 1);
1213 if (!prefix)
1214 return log_oom();
1215
1216 PATH_FOREACH_PREFIX(prefix, i->path) {
1217 ItemArray *j;
1218
1219 j = hashmap_get(items, prefix);
1220 if (j) {
1221 int s;
1222
1223 s = process_item_array(j);
1224 if (s < 0 && t == 0)
1225 t = s;
1226 }
1227 }
1228
1229 r = arg_create ? create_item(i) : 0;
1230 q = arg_remove ? remove_item(i) : 0;
1231 p = arg_clean ? clean_item(i) : 0;
1232
1233 return t < 0 ? t :
1234 r < 0 ? r :
1235 q < 0 ? q :
1236 p;
1237 }
1238
1239 static int process_item_array(ItemArray *array) {
1240 unsigned n;
1241 int r = 0, k;
1242
1243 assert(array);
1244
1245 for (n = 0; n < array->count; n++) {
1246 k = process_item(array->items + n);
1247 if (k < 0 && r == 0)
1248 r = k;
1249 }
1250
1251 return r;
1252 }
1253
1254 static void item_free_contents(Item *i) {
1255 assert(i);
1256 free(i->path);
1257 free(i->argument);
1258 strv_free(i->xattrs);
1259
1260 #ifdef HAVE_ACL
1261 acl_free(i->acl_access);
1262 acl_free(i->acl_default);
1263 #endif
1264 }
1265
1266 static void item_array_free(ItemArray *a) {
1267 unsigned n;
1268
1269 if (!a)
1270 return;
1271
1272 for (n = 0; n < a->count; n++)
1273 item_free_contents(a->items + n);
1274 free(a->items);
1275 free(a);
1276 }
1277
1278 static bool item_compatible(Item *a, Item *b) {
1279 assert(a);
1280 assert(b);
1281 assert(streq(a->path, b->path));
1282
1283 if (takes_ownership(a->type) && takes_ownership(b->type))
1284 /* check if the items are the same */
1285 return streq_ptr(a->argument, b->argument) &&
1286
1287 a->uid_set == b->uid_set &&
1288 a->uid == b->uid &&
1289
1290 a->gid_set == b->gid_set &&
1291 a->gid == b->gid &&
1292
1293 a->mode_set == b->mode_set &&
1294 a->mode == b->mode &&
1295
1296 a->age_set == b->age_set &&
1297 a->age == b->age &&
1298
1299 a->mask_perms == b->mask_perms &&
1300
1301 a->keep_first_level == b->keep_first_level &&
1302
1303 a->major_minor == b->major_minor;
1304
1305 return true;
1306 }
1307
1308 static bool should_include_path(const char *path) {
1309 char **prefix;
1310
1311 STRV_FOREACH(prefix, arg_exclude_prefixes)
1312 if (path_startswith(path, *prefix))
1313 return false;
1314
1315 STRV_FOREACH(prefix, arg_include_prefixes)
1316 if (path_startswith(path, *prefix))
1317 return true;
1318
1319 /* no matches, so we should include this path only if we
1320 * have no whitelist at all */
1321 return strv_length(arg_include_prefixes) == 0;
1322 }
1323
1324 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1325
1326 static const Specifier specifier_table[] = {
1327 { 'm', specifier_machine_id, NULL },
1328 { 'b', specifier_boot_id, NULL },
1329 { 'H', specifier_host_name, NULL },
1330 { 'v', specifier_kernel_release, NULL },
1331 {}
1332 };
1333
1334 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
1335 _cleanup_(item_free_contents) Item i = {};
1336 ItemArray *existing;
1337 Hashmap *h;
1338 int r, c = -1, pos;
1339 bool force = false, boot = false;
1340
1341 assert(fname);
1342 assert(line >= 1);
1343 assert(buffer);
1344
1345 r = sscanf(buffer,
1346 "%ms %ms %ms %ms %ms %ms %n",
1347 &action,
1348 &path,
1349 &mode,
1350 &user,
1351 &group,
1352 &age,
1353 &c);
1354 if (r < 2) {
1355 log_error("[%s:%u] Syntax error.", fname, line);
1356 return -EIO;
1357 }
1358
1359 if (isempty(action)) {
1360 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
1361 return -EINVAL;
1362 }
1363
1364 for (pos = 1; action[pos]; pos++) {
1365 if (action[pos] == '!' && !boot)
1366 boot = true;
1367 else if (action[pos] == '+' && !force)
1368 force = true;
1369 else {
1370 log_error("[%s:%u] Unknown modifiers in command '%s'",
1371 fname, line, action);
1372 return -EINVAL;
1373 }
1374 }
1375
1376 if (boot && !arg_boot)
1377 return 0;
1378
1379 i.type = action[0];
1380 i.force = force;
1381
1382 r = specifier_printf(path, specifier_table, NULL, &i.path);
1383 if (r < 0) {
1384 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1385 return r;
1386 }
1387
1388 if (c >= 0) {
1389 c += strspn(buffer+c, WHITESPACE);
1390 if (buffer[c] != 0 && (buffer[c] != '-' || buffer[c+1] != 0)) {
1391 i.argument = unquote(buffer+c, "\"");
1392 if (!i.argument)
1393 return log_oom();
1394 }
1395 }
1396
1397 switch (i.type) {
1398
1399 case CREATE_FILE:
1400 case TRUNCATE_FILE:
1401 case CREATE_DIRECTORY:
1402 case CREATE_SUBVOLUME:
1403 case TRUNCATE_DIRECTORY:
1404 case CREATE_FIFO:
1405 case IGNORE_PATH:
1406 case IGNORE_DIRECTORY_PATH:
1407 case REMOVE_PATH:
1408 case RECURSIVE_REMOVE_PATH:
1409 case ADJUST_MODE:
1410 case RELABEL_PATH:
1411 case RECURSIVE_RELABEL_PATH:
1412 break;
1413
1414 case CREATE_SYMLINK:
1415 if (!i.argument) {
1416 i.argument = strappend("/usr/share/factory/", i.path);
1417 if (!i.argument)
1418 return log_oom();
1419 }
1420 break;
1421
1422 case WRITE_FILE:
1423 if (!i.argument) {
1424 log_error("[%s:%u] Write file requires argument.", fname, line);
1425 return -EBADMSG;
1426 }
1427 break;
1428
1429 case COPY_FILES:
1430 if (!i.argument) {
1431 i.argument = strappend("/usr/share/factory/", i.path);
1432 if (!i.argument)
1433 return log_oom();
1434 } else if (!path_is_absolute(i.argument)) {
1435 log_error("[%s:%u] Source path is not absolute.", fname, line);
1436 return -EBADMSG;
1437 }
1438
1439 path_kill_slashes(i.argument);
1440 break;
1441
1442 case CREATE_CHAR_DEVICE:
1443 case CREATE_BLOCK_DEVICE: {
1444 unsigned major, minor;
1445
1446 if (!i.argument) {
1447 log_error("[%s:%u] Device file requires argument.", fname, line);
1448 return -EBADMSG;
1449 }
1450
1451 if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
1452 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
1453 return -EBADMSG;
1454 }
1455
1456 i.major_minor = makedev(major, minor);
1457 break;
1458 }
1459
1460 case SET_XATTR:
1461 if (!i.argument) {
1462 log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
1463 return -EBADMSG;
1464 }
1465 r = get_xattrs_from_arg(&i);
1466 if (r < 0)
1467 return r;
1468 break;
1469
1470 case SET_ACL:
1471 if (!i.argument) {
1472 log_error("[%s:%u] Set ACLs requires argument.", fname, line);
1473 return -EBADMSG;
1474 }
1475 r = get_acls_from_arg(&i);
1476 if (r < 0)
1477 return r;
1478 break;
1479
1480 default:
1481 log_error("[%s:%u] Unknown command type '%c'.", fname, line, i.type);
1482 return -EBADMSG;
1483 }
1484
1485 if (!path_is_absolute(i.path)) {
1486 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
1487 return -EBADMSG;
1488 }
1489
1490 path_kill_slashes(i.path);
1491
1492 if (!should_include_path(i.path))
1493 return 0;
1494
1495 if (arg_root) {
1496 char *p;
1497
1498 p = strappend(arg_root, i.path);
1499 if (!p)
1500 return log_oom();
1501
1502 free(i.path);
1503 i.path = p;
1504 }
1505
1506 if (user && !streq(user, "-")) {
1507 const char *u = user;
1508
1509 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
1510 if (r < 0) {
1511 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1512 return r;
1513 }
1514
1515 i.uid_set = true;
1516 }
1517
1518 if (group && !streq(group, "-")) {
1519 const char *g = group;
1520
1521 r = get_group_creds(&g, &i.gid);
1522 if (r < 0) {
1523 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1524 return r;
1525 }
1526
1527 i.gid_set = true;
1528 }
1529
1530 if (mode && !streq(mode, "-")) {
1531 const char *mm = mode;
1532 unsigned m;
1533
1534 if (*mm == '~') {
1535 i.mask_perms = true;
1536 mm++;
1537 }
1538
1539 if (sscanf(mm, "%o", &m) != 1) {
1540 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1541 return -ENOENT;
1542 }
1543
1544 i.mode = m;
1545 i.mode_set = true;
1546 } else
1547 i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
1548 ? 0755 : 0644;
1549
1550 if (age && !streq(age, "-")) {
1551 const char *a = age;
1552
1553 if (*a == '~') {
1554 i.keep_first_level = true;
1555 a++;
1556 }
1557
1558 if (parse_sec(a, &i.age) < 0) {
1559 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1560 return -EBADMSG;
1561 }
1562
1563 i.age_set = true;
1564 }
1565
1566 h = needs_glob(i.type) ? globs : items;
1567
1568 existing = hashmap_get(h, i.path);
1569 if (existing) {
1570 unsigned n;
1571
1572 for (n = 0; n < existing->count; n++) {
1573 if (!item_compatible(existing->items + n, &i))
1574 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
1575 fname, line, i.path);
1576 }
1577 } else {
1578 existing = new0(ItemArray, 1);
1579 r = hashmap_put(h, i.path, existing);
1580 if (r < 0)
1581 return log_oom();
1582 }
1583
1584 if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
1585 return log_oom();
1586
1587 memcpy(existing->items + existing->count++, &i, sizeof(i));
1588 zero(i);
1589 return 0;
1590 }
1591
1592 static void help(void) {
1593 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1594 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
1595 " -h --help Show this help\n"
1596 " --version Show package version\n"
1597 " --create Create marked files/directories\n"
1598 " --clean Clean up marked directories\n"
1599 " --remove Remove marked files/directories\n"
1600 " --boot Execute actions only safe at boot\n"
1601 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n"
1602 " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n"
1603 " --root=PATH Operate on an alternate filesystem root\n",
1604 program_invocation_short_name);
1605 }
1606
1607 static int parse_argv(int argc, char *argv[]) {
1608
1609 enum {
1610 ARG_VERSION = 0x100,
1611 ARG_CREATE,
1612 ARG_CLEAN,
1613 ARG_REMOVE,
1614 ARG_BOOT,
1615 ARG_PREFIX,
1616 ARG_EXCLUDE_PREFIX,
1617 ARG_ROOT,
1618 };
1619
1620 static const struct option options[] = {
1621 { "help", no_argument, NULL, 'h' },
1622 { "version", no_argument, NULL, ARG_VERSION },
1623 { "create", no_argument, NULL, ARG_CREATE },
1624 { "clean", no_argument, NULL, ARG_CLEAN },
1625 { "remove", no_argument, NULL, ARG_REMOVE },
1626 { "boot", no_argument, NULL, ARG_BOOT },
1627 { "prefix", required_argument, NULL, ARG_PREFIX },
1628 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
1629 { "root", required_argument, NULL, ARG_ROOT },
1630 {}
1631 };
1632
1633 int c;
1634
1635 assert(argc >= 0);
1636 assert(argv);
1637
1638 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
1639
1640 switch (c) {
1641
1642 case 'h':
1643 help();
1644 return 0;
1645
1646 case ARG_VERSION:
1647 puts(PACKAGE_STRING);
1648 puts(SYSTEMD_FEATURES);
1649 return 0;
1650
1651 case ARG_CREATE:
1652 arg_create = true;
1653 break;
1654
1655 case ARG_CLEAN:
1656 arg_clean = true;
1657 break;
1658
1659 case ARG_REMOVE:
1660 arg_remove = true;
1661 break;
1662
1663 case ARG_BOOT:
1664 arg_boot = true;
1665 break;
1666
1667 case ARG_PREFIX:
1668 if (strv_push(&arg_include_prefixes, optarg) < 0)
1669 return log_oom();
1670 break;
1671
1672 case ARG_EXCLUDE_PREFIX:
1673 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
1674 return log_oom();
1675 break;
1676
1677 case ARG_ROOT:
1678 free(arg_root);
1679 arg_root = path_make_absolute_cwd(optarg);
1680 if (!arg_root)
1681 return log_oom();
1682
1683 path_kill_slashes(arg_root);
1684 break;
1685
1686 case '?':
1687 return -EINVAL;
1688
1689 default:
1690 assert_not_reached("Unhandled option");
1691 }
1692
1693 if (!arg_clean && !arg_create && !arg_remove) {
1694 log_error("You need to specify at least one of --clean, --create or --remove.");
1695 return -EINVAL;
1696 }
1697
1698 return 1;
1699 }
1700
1701 static int read_config_file(const char *fn, bool ignore_enoent) {
1702 _cleanup_fclose_ FILE *f = NULL;
1703 char line[LINE_MAX];
1704 Iterator iterator;
1705 unsigned v = 0;
1706 Item *i;
1707 int r;
1708
1709 assert(fn);
1710
1711 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1712 if (r < 0) {
1713 if (ignore_enoent && r == -ENOENT)
1714 return 0;
1715
1716 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
1717 }
1718
1719 FOREACH_LINE(line, f, break) {
1720 char *l;
1721 int k;
1722
1723 v++;
1724
1725 l = strstrip(line);
1726 if (*l == '#' || *l == 0)
1727 continue;
1728
1729 k = parse_line(fn, v, l);
1730 if (k < 0 && r == 0)
1731 r = k;
1732 }
1733
1734 /* we have to determine age parameter for each entry of type X */
1735 HASHMAP_FOREACH(i, globs, iterator) {
1736 Iterator iter;
1737 Item *j, *candidate_item = NULL;
1738
1739 if (i->type != IGNORE_DIRECTORY_PATH)
1740 continue;
1741
1742 HASHMAP_FOREACH(j, items, iter) {
1743 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
1744 continue;
1745
1746 if (path_equal(j->path, i->path)) {
1747 candidate_item = j;
1748 break;
1749 }
1750
1751 if ((!candidate_item && path_startswith(i->path, j->path)) ||
1752 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
1753 candidate_item = j;
1754 }
1755
1756 if (candidate_item && candidate_item->age_set) {
1757 i->age = candidate_item->age;
1758 i->age_set = true;
1759 }
1760 }
1761
1762 if (ferror(f)) {
1763 log_error_errno(errno, "Failed to read from file %s: %m", fn);
1764 if (r == 0)
1765 r = -EIO;
1766 }
1767
1768 return r;
1769 }
1770
1771 int main(int argc, char *argv[]) {
1772 int r, k;
1773 ItemArray *a;
1774 Iterator iterator;
1775
1776 r = parse_argv(argc, argv);
1777 if (r <= 0)
1778 goto finish;
1779
1780 log_set_target(LOG_TARGET_AUTO);
1781 log_parse_environment();
1782 log_open();
1783
1784 umask(0022);
1785
1786 mac_selinux_init(NULL);
1787
1788 items = hashmap_new(&string_hash_ops);
1789 globs = hashmap_new(&string_hash_ops);
1790
1791 if (!items || !globs) {
1792 r = log_oom();
1793 goto finish;
1794 }
1795
1796 r = 0;
1797
1798 if (optind < argc) {
1799 int j;
1800
1801 for (j = optind; j < argc; j++) {
1802 k = read_config_file(argv[j], false);
1803 if (k < 0 && r == 0)
1804 r = k;
1805 }
1806
1807 } else {
1808 _cleanup_strv_free_ char **files = NULL;
1809 char **f;
1810
1811 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1812 if (r < 0) {
1813 log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
1814 goto finish;
1815 }
1816
1817 STRV_FOREACH(f, files) {
1818 k = read_config_file(*f, true);
1819 if (k < 0 && r == 0)
1820 r = k;
1821 }
1822 }
1823
1824 HASHMAP_FOREACH(a, globs, iterator) {
1825 k = process_item_array(a);
1826 if (k < 0 && r == 0)
1827 r = k;
1828 }
1829
1830 HASHMAP_FOREACH(a, items, iterator) {
1831 k = process_item_array(a);
1832 if (k < 0 && r == 0)
1833 r = k;
1834 }
1835
1836 finish:
1837 while ((a = hashmap_steal_first(items)))
1838 item_array_free(a);
1839
1840 while ((a = hashmap_steal_first(globs)))
1841 item_array_free(a);
1842
1843 hashmap_free(items);
1844 hashmap_free(globs);
1845
1846 free(arg_include_prefixes);
1847 free(arg_exclude_prefixes);
1848 free(arg_root);
1849
1850 set_free_free(unix_sockets);
1851
1852 mac_selinux_finish();
1853
1854 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1855 }