]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tmpfiles/tmpfiles.c
Merge pull request #883 from phomes/bool_vs_error_codes
[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 <stdio.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <getopt.h>
33 #include <stdbool.h>
34 #include <time.h>
35 #include <glob.h>
36 #include <fnmatch.h>
37 #include <sys/stat.h>
38 #include <sys/xattr.h>
39 #include <linux/fs.h>
40
41 #include "log.h"
42 #include "util.h"
43 #include "macro.h"
44 #include "missing.h"
45 #include "mkdir.h"
46 #include "path-util.h"
47 #include "strv.h"
48 #include "label.h"
49 #include "set.h"
50 #include "conf-files.h"
51 #include "capability.h"
52 #include "specifier.h"
53 #include "build.h"
54 #include "copy.h"
55 #include "rm-rf.h"
56 #include "selinux-util.h"
57 #include "btrfs-util.h"
58 #include "acl-util.h"
59 #include "formats-util.h"
60
61 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
62 * them in the file system. This is intended to be used to create
63 * properly owned directories beneath /tmp, /var/tmp, /run, which are
64 * volatile and hence need to be recreated on bootup. */
65
66 typedef enum ItemType {
67 /* These ones take file names */
68 CREATE_FILE = 'f',
69 TRUNCATE_FILE = 'F',
70 CREATE_DIRECTORY = 'd',
71 TRUNCATE_DIRECTORY = 'D',
72 CREATE_SUBVOLUME = 'v',
73 CREATE_FIFO = 'p',
74 CREATE_SYMLINK = 'L',
75 CREATE_CHAR_DEVICE = 'c',
76 CREATE_BLOCK_DEVICE = 'b',
77 COPY_FILES = 'C',
78
79 /* These ones take globs */
80 WRITE_FILE = 'w',
81 SET_XATTR = 't',
82 RECURSIVE_SET_XATTR = 'T',
83 SET_ACL = 'a',
84 RECURSIVE_SET_ACL = 'A',
85 SET_ATTRIBUTE = 'h',
86 RECURSIVE_SET_ATTRIBUTE = 'H',
87 IGNORE_PATH = 'x',
88 IGNORE_DIRECTORY_PATH = 'X',
89 REMOVE_PATH = 'r',
90 RECURSIVE_REMOVE_PATH = 'R',
91 RELABEL_PATH = 'z',
92 RECURSIVE_RELABEL_PATH = 'Z',
93 ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
94 } ItemType;
95
96 typedef struct Item {
97 ItemType type;
98
99 char *path;
100 char *argument;
101 char **xattrs;
102 #ifdef HAVE_ACL
103 acl_t acl_access;
104 acl_t acl_default;
105 #endif
106 uid_t uid;
107 gid_t gid;
108 mode_t mode;
109 usec_t age;
110
111 dev_t major_minor;
112 unsigned attribute_value;
113 unsigned attribute_mask;
114
115 bool uid_set:1;
116 bool gid_set:1;
117 bool mode_set:1;
118 bool age_set:1;
119 bool mask_perms:1;
120 bool attribute_set:1;
121
122 bool keep_first_level:1;
123
124 bool force:1;
125
126 bool done:1;
127 } Item;
128
129 typedef struct ItemArray {
130 Item *items;
131 size_t count;
132 size_t size;
133 } ItemArray;
134
135 static bool arg_create = false;
136 static bool arg_clean = false;
137 static bool arg_remove = false;
138 static bool arg_boot = false;
139
140 static char **arg_include_prefixes = NULL;
141 static char **arg_exclude_prefixes = NULL;
142 static char *arg_root = NULL;
143
144 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
145
146 #define MAX_DEPTH 256
147
148 static OrderedHashmap *items = NULL, *globs = NULL;
149 static Set *unix_sockets = NULL;
150
151 static const Specifier specifier_table[] = {
152 { 'm', specifier_machine_id, NULL },
153 { 'b', specifier_boot_id, NULL },
154 { 'H', specifier_host_name, NULL },
155 { 'v', specifier_kernel_release, NULL },
156 {}
157 };
158
159 static bool needs_glob(ItemType t) {
160 return IN_SET(t,
161 WRITE_FILE,
162 IGNORE_PATH,
163 IGNORE_DIRECTORY_PATH,
164 REMOVE_PATH,
165 RECURSIVE_REMOVE_PATH,
166 ADJUST_MODE,
167 RELABEL_PATH,
168 RECURSIVE_RELABEL_PATH,
169 SET_XATTR,
170 RECURSIVE_SET_XATTR,
171 SET_ACL,
172 RECURSIVE_SET_ACL,
173 SET_ATTRIBUTE,
174 RECURSIVE_SET_ATTRIBUTE);
175 }
176
177 static bool takes_ownership(ItemType t) {
178 return IN_SET(t,
179 CREATE_FILE,
180 TRUNCATE_FILE,
181 CREATE_DIRECTORY,
182 TRUNCATE_DIRECTORY,
183 CREATE_SUBVOLUME,
184 CREATE_FIFO,
185 CREATE_SYMLINK,
186 CREATE_CHAR_DEVICE,
187 CREATE_BLOCK_DEVICE,
188 COPY_FILES,
189 WRITE_FILE,
190 IGNORE_PATH,
191 IGNORE_DIRECTORY_PATH,
192 REMOVE_PATH,
193 RECURSIVE_REMOVE_PATH);
194 }
195
196 static struct Item* find_glob(OrderedHashmap *h, const char *match) {
197 ItemArray *j;
198 Iterator i;
199
200 ORDERED_HASHMAP_FOREACH(j, h, i) {
201 unsigned n;
202
203 for (n = 0; n < j->count; n++) {
204 Item *item = j->items + n;
205
206 if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
207 return item;
208 }
209 }
210
211 return NULL;
212 }
213
214 static void load_unix_sockets(void) {
215 _cleanup_fclose_ FILE *f = NULL;
216 char line[LINE_MAX];
217
218 if (unix_sockets)
219 return;
220
221 /* We maintain a cache of the sockets we found in
222 * /proc/net/unix to speed things up a little. */
223
224 unix_sockets = set_new(&string_hash_ops);
225 if (!unix_sockets)
226 return;
227
228 f = fopen("/proc/net/unix", "re");
229 if (!f)
230 return;
231
232 /* Skip header */
233 if (!fgets(line, sizeof(line), f))
234 goto fail;
235
236 for (;;) {
237 char *p, *s;
238 int k;
239
240 if (!fgets(line, sizeof(line), f))
241 break;
242
243 truncate_nl(line);
244
245 p = strchr(line, ':');
246 if (!p)
247 continue;
248
249 if (strlen(p) < 37)
250 continue;
251
252 p += 37;
253 p += strspn(p, WHITESPACE);
254 p += strcspn(p, WHITESPACE); /* skip one more word */
255 p += strspn(p, WHITESPACE);
256
257 if (*p != '/')
258 continue;
259
260 s = strdup(p);
261 if (!s)
262 goto fail;
263
264 path_kill_slashes(s);
265
266 k = set_consume(unix_sockets, s);
267 if (k < 0 && k != -EEXIST)
268 goto fail;
269 }
270
271 return;
272
273 fail:
274 set_free_free(unix_sockets);
275 unix_sockets = NULL;
276 }
277
278 static bool unix_socket_alive(const char *fn) {
279 assert(fn);
280
281 load_unix_sockets();
282
283 if (unix_sockets)
284 return !!set_get(unix_sockets, (char*) fn);
285
286 /* We don't know, so assume yes */
287 return true;
288 }
289
290 static int dir_is_mount_point(DIR *d, const char *subdir) {
291
292 union file_handle_union h = FILE_HANDLE_INIT;
293 int mount_id_parent, mount_id;
294 int r_p, r;
295
296 r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
297 if (r_p < 0)
298 r_p = -errno;
299
300 h.handle.handle_bytes = MAX_HANDLE_SZ;
301 r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
302 if (r < 0)
303 r = -errno;
304
305 /* got no handle; make no assumptions, return error */
306 if (r_p < 0 && r < 0)
307 return r_p;
308
309 /* got both handles; if they differ, it is a mount point */
310 if (r_p >= 0 && r >= 0)
311 return mount_id_parent != mount_id;
312
313 /* got only one handle; assume different mount points if one
314 * of both queries was not supported by the filesystem */
315 if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP)
316 return true;
317
318 /* return error */
319 if (r_p < 0)
320 return r_p;
321 return r;
322 }
323
324 static DIR* xopendirat_nomod(int dirfd, const char *path) {
325 DIR *dir;
326
327 dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
328 if (dir)
329 return dir;
330
331 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
332 if (errno != EPERM)
333 return NULL;
334
335 dir = xopendirat(dirfd, path, O_NOFOLLOW);
336 if (!dir)
337 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
338
339 return dir;
340 }
341
342 static DIR* opendir_nomod(const char *path) {
343 return xopendirat_nomod(AT_FDCWD, path);
344 }
345
346 static int dir_cleanup(
347 Item *i,
348 const char *p,
349 DIR *d,
350 const struct stat *ds,
351 usec_t cutoff,
352 dev_t rootdev,
353 bool mountpoint,
354 int maxdepth,
355 bool keep_this_level) {
356
357 struct dirent *dent;
358 struct timespec times[2];
359 bool deleted = false;
360 int r = 0;
361
362 while ((dent = readdir(d))) {
363 struct stat s;
364 usec_t age;
365 _cleanup_free_ char *sub_path = NULL;
366
367 if (STR_IN_SET(dent->d_name, ".", ".."))
368 continue;
369
370 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
371 if (errno == ENOENT)
372 continue;
373
374 /* FUSE, NFS mounts, SELinux might return EACCES */
375 if (errno == EACCES)
376 log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
377 else
378 log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
379 r = -errno;
380 continue;
381 }
382
383 /* Stay on the same filesystem */
384 if (s.st_dev != rootdev) {
385 log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
386 continue;
387 }
388
389 /* Try to detect bind mounts of the same filesystem instance; they
390 * do not differ in device major/minors. This type of query is not
391 * supported on all kernels or filesystem types though. */
392 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) {
393 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
394 p, dent->d_name);
395 continue;
396 }
397
398 /* Do not delete read-only files owned by root */
399 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) {
400 log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name);
401 continue;
402 }
403
404 sub_path = strjoin(p, "/", dent->d_name, NULL);
405 if (!sub_path) {
406 r = log_oom();
407 goto finish;
408 }
409
410 /* Is there an item configured for this path? */
411 if (ordered_hashmap_get(items, sub_path)) {
412 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
413 continue;
414 }
415
416 if (find_glob(globs, sub_path)) {
417 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path);
418 continue;
419 }
420
421 if (S_ISDIR(s.st_mode)) {
422
423 if (mountpoint &&
424 streq(dent->d_name, "lost+found") &&
425 s.st_uid == 0) {
426 log_debug("Ignoring \"%s\".", sub_path);
427 continue;
428 }
429
430 if (maxdepth <= 0)
431 log_warning("Reached max depth on \"%s\".", sub_path);
432 else {
433 _cleanup_closedir_ DIR *sub_dir;
434 int q;
435
436 sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
437 if (!sub_dir) {
438 if (errno != ENOENT)
439 r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
440
441 continue;
442 }
443
444 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
445 if (q < 0)
446 r = q;
447 }
448
449 /* Note: if you are wondering why we don't
450 * support the sticky bit for excluding
451 * directories from cleaning like we do it for
452 * other file system objects: well, the sticky
453 * bit already has a meaning for directories,
454 * so we don't want to overload that. */
455
456 if (keep_this_level) {
457 log_debug("Keeping \"%s\".", sub_path);
458 continue;
459 }
460
461 /* Ignore ctime, we change it when deleting */
462 age = timespec_load(&s.st_mtim);
463 if (age >= cutoff) {
464 char a[FORMAT_TIMESTAMP_MAX];
465 /* Follows spelling in stat(1). */
466 log_debug("Directory \"%s\": modify time %s is too new.",
467 sub_path,
468 format_timestamp_us(a, sizeof(a), age));
469 continue;
470 }
471
472 age = timespec_load(&s.st_atim);
473 if (age >= cutoff) {
474 char a[FORMAT_TIMESTAMP_MAX];
475 log_debug("Directory \"%s\": access time %s is too new.",
476 sub_path,
477 format_timestamp_us(a, sizeof(a), age));
478 continue;
479 }
480
481 log_debug("Removing directory \"%s\".", sub_path);
482 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
483 if (errno != ENOENT && errno != ENOTEMPTY) {
484 log_error_errno(errno, "rmdir(%s): %m", sub_path);
485 r = -errno;
486 }
487
488 } else {
489 /* Skip files for which the sticky bit is
490 * set. These are semantics we define, and are
491 * unknown elsewhere. See XDG_RUNTIME_DIR
492 * specification for details. */
493 if (s.st_mode & S_ISVTX) {
494 log_debug("Skipping \"%s\": sticky bit set.", sub_path);
495 continue;
496 }
497
498 if (mountpoint && S_ISREG(s.st_mode))
499 if (s.st_uid == 0 && STR_IN_SET(dent->d_name,
500 ".journal",
501 "aquota.user",
502 "aquota.group")) {
503 log_debug("Skipping \"%s\".", sub_path);
504 continue;
505 }
506
507 /* Ignore sockets that are listed in /proc/net/unix */
508 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
509 log_debug("Skipping \"%s\": live socket.", sub_path);
510 continue;
511 }
512
513 /* Ignore device nodes */
514 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
515 log_debug("Skipping \"%s\": a device.", sub_path);
516 continue;
517 }
518
519 /* Keep files on this level around if this is
520 * requested */
521 if (keep_this_level) {
522 log_debug("Keeping \"%s\".", sub_path);
523 continue;
524 }
525
526 age = timespec_load(&s.st_mtim);
527 if (age >= cutoff) {
528 char a[FORMAT_TIMESTAMP_MAX];
529 /* Follows spelling in stat(1). */
530 log_debug("File \"%s\": modify time %s is too new.",
531 sub_path,
532 format_timestamp_us(a, sizeof(a), age));
533 continue;
534 }
535
536 age = timespec_load(&s.st_atim);
537 if (age >= cutoff) {
538 char a[FORMAT_TIMESTAMP_MAX];
539 log_debug("File \"%s\": access time %s is too new.",
540 sub_path,
541 format_timestamp_us(a, sizeof(a), age));
542 continue;
543 }
544
545 age = timespec_load(&s.st_ctim);
546 if (age >= cutoff) {
547 char a[FORMAT_TIMESTAMP_MAX];
548 log_debug("File \"%s\": change time %s is too new.",
549 sub_path,
550 format_timestamp_us(a, sizeof(a), age));
551 continue;
552 }
553
554 log_debug("unlink \"%s\"", sub_path);
555
556 if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
557 if (errno != ENOENT)
558 r = log_error_errno(errno, "unlink(%s): %m", sub_path);
559
560 deleted = true;
561 }
562 }
563
564 finish:
565 if (deleted) {
566 usec_t age1, age2;
567 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
568
569 /* Restore original directory timestamps */
570 times[0] = ds->st_atim;
571 times[1] = ds->st_mtim;
572
573 age1 = timespec_load(&ds->st_atim);
574 age2 = timespec_load(&ds->st_mtim);
575 log_debug("Restoring access and modification time on \"%s\": %s, %s",
576 p,
577 format_timestamp_us(a, sizeof(a), age1),
578 format_timestamp_us(b, sizeof(b), age2));
579 if (futimens(dirfd(d), times) < 0)
580 log_error_errno(errno, "utimensat(%s): %m", p);
581 }
582
583 return r;
584 }
585
586 static int path_set_perms(Item *i, const char *path) {
587 _cleanup_close_ int fd = -1;
588 struct stat st;
589
590 assert(i);
591 assert(path);
592
593 /* We open the file with O_PATH here, to make the operation
594 * somewhat atomic. Also there's unfortunately no fchmodat()
595 * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
596 * O_PATH. */
597
598 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
599 if (fd < 0)
600 return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path);
601
602 if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
603 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
604
605 if (S_ISLNK(st.st_mode))
606 log_debug("Skipping mode an owner fix for symlink %s.", path);
607 else {
608 char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
609 xsprintf(fn, "/proc/self/fd/%i", fd);
610
611 /* not using i->path directly because it may be a glob */
612 if (i->mode_set) {
613 mode_t m = i->mode;
614
615 if (i->mask_perms) {
616 if (!(st.st_mode & 0111))
617 m &= ~0111;
618 if (!(st.st_mode & 0222))
619 m &= ~0222;
620 if (!(st.st_mode & 0444))
621 m &= ~0444;
622 if (!S_ISDIR(st.st_mode))
623 m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
624 }
625
626 if (m == (st.st_mode & 07777))
627 log_debug("\"%s\" has right mode %o", path, st.st_mode);
628 else {
629 log_debug("chmod \"%s\" to mode %o", path, m);
630 if (chmod(fn, m) < 0)
631 return log_error_errno(errno, "chmod(%s) failed: %m", path);
632 }
633 }
634
635 if ((i->uid != st.st_uid || i->gid != st.st_gid) &&
636 (i->uid_set || i->gid_set)) {
637 log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
638 path,
639 i->uid_set ? i->uid : UID_INVALID,
640 i->gid_set ? i->gid : GID_INVALID);
641 if (chown(fn,
642 i->uid_set ? i->uid : UID_INVALID,
643 i->gid_set ? i->gid : GID_INVALID) < 0)
644 return log_error_errno(errno, "chown(%s) failed: %m", path);
645 }
646 }
647
648 fd = safe_close(fd);
649
650 return label_fix(path, false, false);
651 }
652
653 static int parse_xattrs_from_arg(Item *i) {
654 const char *p;
655 int r;
656
657 assert(i);
658 assert(i->argument);
659
660 p = i->argument;
661
662 for (;;) {
663 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
664
665 r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE);
666 if (r < 0)
667 log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
668 if (r <= 0)
669 break;
670
671 r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced);
672 if (r < 0)
673 return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr);
674
675 r = split_pair(xattr_replaced, "=", &name, &value);
676 if (r < 0) {
677 log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
678 continue;
679 }
680
681 if (isempty(name) || isempty(value)) {
682 log_warning("Malformed extended attribute found, ignoring: %s", xattr);
683 continue;
684 }
685
686 if (strv_push_pair(&i->xattrs, name, value) < 0)
687 return log_oom();
688
689 name = value = NULL;
690 }
691
692 return 0;
693 }
694
695 static int path_set_xattrs(Item *i, const char *path) {
696 char **name, **value;
697
698 assert(i);
699 assert(path);
700
701 STRV_FOREACH_PAIR(name, value, i->xattrs) {
702 int n;
703
704 n = strlen(*value);
705 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
706 if (lsetxattr(path, *name, *value, n, 0) < 0) {
707 log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path);
708 return -errno;
709 }
710 }
711 return 0;
712 }
713
714 static int parse_acls_from_arg(Item *item) {
715 #ifdef HAVE_ACL
716 int r;
717
718 assert(item);
719
720 /* If force (= modify) is set, we will not modify the acl
721 * afterwards, so the mask can be added now if necessary. */
722
723 r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
724 if (r < 0)
725 log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
726 #else
727 log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
728 #endif
729
730 return 0;
731 }
732
733 #ifdef HAVE_ACL
734 static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) {
735 _cleanup_(acl_free_charpp) char *t = NULL;
736 _cleanup_(acl_freep) acl_t dup = NULL;
737 int r;
738
739 /* Returns 0 for success, positive error if already warned,
740 * negative error otherwise. */
741
742 if (modify) {
743 r = acls_for_file(path, type, acl, &dup);
744 if (r < 0)
745 return r;
746
747 r = calc_acl_mask_if_needed(&dup);
748 if (r < 0)
749 return r;
750 } else {
751 dup = acl_dup(acl);
752 if (!dup)
753 return -errno;
754
755 /* the mask was already added earlier if needed */
756 }
757
758 r = add_base_acls_if_needed(&dup, path);
759 if (r < 0)
760 return r;
761
762 t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
763 log_debug("Setting %s ACL %s on %s.",
764 type == ACL_TYPE_ACCESS ? "access" : "default",
765 strna(t), pretty);
766
767 r = acl_set_file(path, type, dup);
768 if (r < 0)
769 /* Return positive to indicate we already warned */
770 return -log_error_errno(errno,
771 "Setting %s ACL \"%s\" on %s failed: %m",
772 type == ACL_TYPE_ACCESS ? "access" : "default",
773 strna(t), pretty);
774
775 return 0;
776 }
777 #endif
778
779 static int path_set_acls(Item *item, const char *path) {
780 int r = 0;
781 #ifdef HAVE_ACL
782 char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
783 _cleanup_close_ int fd = -1;
784 struct stat st;
785
786 assert(item);
787 assert(path);
788
789 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
790 if (fd < 0)
791 return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
792
793 if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
794 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
795
796 if (S_ISLNK(st.st_mode)) {
797 log_debug("Skipping ACL fix for symlink %s.", path);
798 return 0;
799 }
800
801 xsprintf(fn, "/proc/self/fd/%i", fd);
802
803 if (item->acl_access)
804 r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
805
806 if (r == 0 && item->acl_default)
807 r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
808
809 if (r > 0)
810 return -r; /* already warned */
811 else if (r == -EOPNOTSUPP) {
812 log_debug_errno(r, "ACLs not supported by file system at %s", path);
813 return 0;
814 } else if (r < 0)
815 log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
816 #endif
817 return r;
818 }
819
820 #define ATTRIBUTES_ALL \
821 (FS_NOATIME_FL | \
822 FS_SYNC_FL | \
823 FS_DIRSYNC_FL | \
824 FS_APPEND_FL | \
825 FS_COMPR_FL | \
826 FS_NODUMP_FL | \
827 FS_EXTENT_FL | \
828 FS_IMMUTABLE_FL | \
829 FS_JOURNAL_DATA_FL | \
830 FS_SECRM_FL | \
831 FS_UNRM_FL | \
832 FS_NOTAIL_FL | \
833 FS_TOPDIR_FL | \
834 FS_NOCOW_FL)
835
836 static int parse_attribute_from_arg(Item *item) {
837
838 static const struct {
839 char character;
840 unsigned value;
841 } attributes[] = {
842 { 'A', FS_NOATIME_FL }, /* do not update atime */
843 { 'S', FS_SYNC_FL }, /* Synchronous updates */
844 { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */
845 { 'a', FS_APPEND_FL }, /* writes to file may only append */
846 { 'c', FS_COMPR_FL }, /* Compress file */
847 { 'd', FS_NODUMP_FL }, /* do not dump file */
848 { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/
849 { 'i', FS_IMMUTABLE_FL }, /* Immutable file */
850 { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
851 { 's', FS_SECRM_FL }, /* Secure deletion */
852 { 'u', FS_UNRM_FL }, /* Undelete */
853 { 't', FS_NOTAIL_FL }, /* file tail should not be merged */
854 { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/
855 { 'C', FS_NOCOW_FL }, /* Do not cow file */
856 };
857
858 enum {
859 MODE_ADD,
860 MODE_DEL,
861 MODE_SET
862 } mode = MODE_ADD;
863
864 unsigned value = 0, mask = 0;
865 const char *p;
866
867 assert(item);
868
869 p = item->argument;
870 if (p) {
871 if (*p == '+') {
872 mode = MODE_ADD;
873 p++;
874 } else if (*p == '-') {
875 mode = MODE_DEL;
876 p++;
877 } else if (*p == '=') {
878 mode = MODE_SET;
879 p++;
880 }
881 }
882
883 if (isempty(p) && mode != MODE_SET) {
884 log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
885 return -EINVAL;
886 }
887
888 for (; p && *p ; p++) {
889 unsigned i, v;
890
891 for (i = 0; i < ELEMENTSOF(attributes); i++)
892 if (*p == attributes[i].character)
893 break;
894
895 if (i >= ELEMENTSOF(attributes)) {
896 log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
897 return -EINVAL;
898 }
899
900 v = attributes[i].value;
901
902 if (mode == MODE_ADD || mode == MODE_SET)
903 value |= v;
904 else
905 value &= ~v;
906
907 mask |= v;
908 }
909
910 if (mode == MODE_SET)
911 mask |= ATTRIBUTES_ALL;
912
913 assert(mask != 0);
914
915 item->attribute_mask = mask;
916 item->attribute_value = value;
917 item->attribute_set = true;
918
919 return 0;
920 }
921
922 static int path_set_attribute(Item *item, const char *path) {
923 _cleanup_close_ int fd = -1;
924 struct stat st;
925 unsigned f;
926 int r;
927
928 if (!item->attribute_set || item->attribute_mask == 0)
929 return 0;
930
931 fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW);
932 if (fd < 0) {
933 if (errno == ELOOP)
934 return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path);
935
936 return log_error_errno(errno, "Cannot open '%s': %m", path);
937 }
938
939 if (fstat(fd, &st) < 0)
940 return log_error_errno(errno, "Cannot stat '%s': %m", path);
941
942 /* Issuing the file attribute ioctls on device nodes is not
943 * safe, as that will be delivered to the drivers, not the
944 * file system containing the device node. */
945 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
946 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
947 return -EINVAL;
948 }
949
950 f = item->attribute_value & item->attribute_mask;
951
952 /* Mask away directory-specific flags */
953 if (!S_ISDIR(st.st_mode))
954 f &= ~FS_DIRSYNC_FL;
955
956 r = chattr_fd(fd, f, item->attribute_mask);
957 if (r < 0)
958 log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING,
959 r,
960 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
961 path, item->attribute_value, item->attribute_mask);
962
963 return 0;
964 }
965
966 static int write_one_file(Item *i, const char *path) {
967 _cleanup_close_ int fd = -1;
968 int flags, r = 0;
969 struct stat st;
970
971 assert(i);
972 assert(path);
973
974 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
975 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
976
977 RUN_WITH_UMASK(0000) {
978 mac_selinux_create_file_prepare(path, S_IFREG);
979 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
980 mac_selinux_create_file_clear();
981 }
982
983 if (fd < 0) {
984 if (i->type == WRITE_FILE && errno == ENOENT) {
985 log_debug_errno(errno, "Not writing \"%s\": %m", path);
986 return 0;
987 }
988
989 r = -errno;
990 if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
991 (i->type == CREATE_FILE || st.st_size == 0))
992 goto check_mode;
993
994 return log_error_errno(r, "Failed to create file %s: %m", path);
995 }
996
997 if (i->argument) {
998 _cleanup_free_ char *unescaped = NULL, *replaced = NULL;
999
1000 log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
1001
1002 r = cunescape(i->argument, 0, &unescaped);
1003 if (r < 0)
1004 return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
1005
1006 r = specifier_printf(unescaped, specifier_table, NULL, &replaced);
1007 if (r < 0)
1008 return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped);
1009
1010 r = loop_write(fd, replaced, strlen(replaced), false);
1011 if (r < 0)
1012 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
1013 } else
1014 log_debug("\"%s\" has been created.", path);
1015
1016 fd = safe_close(fd);
1017
1018 if (stat(path, &st) < 0)
1019 return log_error_errno(errno, "stat(%s) failed: %m", path);
1020
1021 check_mode:
1022 if (!S_ISREG(st.st_mode)) {
1023 log_error("%s is not a file.", path);
1024 return -EEXIST;
1025 }
1026
1027 r = path_set_perms(i, path);
1028 if (r < 0)
1029 return r;
1030
1031 return 0;
1032 }
1033
1034 typedef int (*action_t)(Item *, const char *);
1035
1036 static int item_do_children(Item *i, const char *path, action_t action) {
1037 _cleanup_closedir_ DIR *d;
1038 int r = 0;
1039
1040 assert(i);
1041 assert(path);
1042
1043 /* This returns the first error we run into, but nevertheless
1044 * tries to go on */
1045
1046 d = opendir_nomod(path);
1047 if (!d)
1048 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
1049
1050 for (;;) {
1051 _cleanup_free_ char *p = NULL;
1052 struct dirent *de;
1053 int q;
1054
1055 errno = 0;
1056 de = readdir(d);
1057 if (!de) {
1058 if (errno != 0 && r == 0)
1059 r = -errno;
1060
1061 break;
1062 }
1063
1064 if (STR_IN_SET(de->d_name, ".", ".."))
1065 continue;
1066
1067 p = strjoin(path, "/", de->d_name, NULL);
1068 if (!p)
1069 return -ENOMEM;
1070
1071 q = action(i, p);
1072 if (q < 0 && q != -ENOENT && r == 0)
1073 r = q;
1074
1075 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
1076 q = item_do_children(i, p, action);
1077 if (q < 0 && r == 0)
1078 r = q;
1079 }
1080 }
1081
1082 return r;
1083 }
1084
1085 static int glob_item(Item *i, action_t action, bool recursive) {
1086 _cleanup_globfree_ glob_t g = {
1087 .gl_closedir = (void (*)(void *)) closedir,
1088 .gl_readdir = (struct dirent *(*)(void *)) readdir,
1089 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
1090 .gl_lstat = lstat,
1091 .gl_stat = stat,
1092 };
1093 int r = 0, k;
1094 char **fn;
1095
1096 errno = 0;
1097 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
1098 if (k != 0 && k != GLOB_NOMATCH)
1099 return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
1100
1101 STRV_FOREACH(fn, g.gl_pathv) {
1102 k = action(i, *fn);
1103 if (k < 0 && r == 0)
1104 r = k;
1105
1106 if (recursive) {
1107 k = item_do_children(i, *fn, action);
1108 if (k < 0 && r == 0)
1109 r = k;
1110 }
1111 }
1112
1113 return r;
1114 }
1115
1116 typedef enum {
1117 CREATION_NORMAL,
1118 CREATION_EXISTING,
1119 CREATION_FORCE,
1120 _CREATION_MODE_MAX,
1121 _CREATION_MODE_INVALID = -1
1122 } CreationMode;
1123
1124 static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
1125 [CREATION_NORMAL] = "Created",
1126 [CREATION_EXISTING] = "Found existing",
1127 [CREATION_FORCE] = "Created replacement",
1128 };
1129
1130 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
1131
1132 static int create_item(Item *i) {
1133 _cleanup_free_ char *resolved = NULL;
1134 struct stat st;
1135 int r = 0;
1136 CreationMode creation;
1137
1138 assert(i);
1139
1140 log_debug("Running create action for entry %c %s", (char) i->type, i->path);
1141
1142 switch (i->type) {
1143
1144 case IGNORE_PATH:
1145 case IGNORE_DIRECTORY_PATH:
1146 case REMOVE_PATH:
1147 case RECURSIVE_REMOVE_PATH:
1148 return 0;
1149
1150 case CREATE_FILE:
1151 case TRUNCATE_FILE:
1152 r = write_one_file(i, i->path);
1153 if (r < 0)
1154 return r;
1155 break;
1156
1157 case COPY_FILES: {
1158 r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
1159 if (r < 0)
1160 return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
1161
1162 log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
1163 r = copy_tree(resolved, i->path, false);
1164
1165 if (r == -EROFS && stat(i->path, &st) == 0)
1166 r = -EEXIST;
1167
1168 if (r < 0) {
1169 struct stat a, b;
1170
1171 if (r != -EEXIST)
1172 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
1173
1174 if (stat(resolved, &a) < 0)
1175 return log_error_errno(errno, "stat(%s) failed: %m", resolved);
1176
1177 if (stat(i->path, &b) < 0)
1178 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1179
1180 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
1181 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
1182 return 0;
1183 }
1184 }
1185
1186 r = path_set_perms(i, i->path);
1187 if (r < 0)
1188 return r;
1189
1190 break;
1191
1192 case WRITE_FILE:
1193 r = glob_item(i, write_one_file, false);
1194 if (r < 0)
1195 return r;
1196
1197 break;
1198
1199 case CREATE_DIRECTORY:
1200 case TRUNCATE_DIRECTORY:
1201 case CREATE_SUBVOLUME:
1202
1203 RUN_WITH_UMASK(0000)
1204 mkdir_parents_label(i->path, 0755);
1205
1206 if (i->type == CREATE_SUBVOLUME)
1207 RUN_WITH_UMASK((~i->mode) & 0777) {
1208 r = btrfs_subvol_make(i->path);
1209 log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path);
1210 }
1211 else
1212 r = 0;
1213
1214 if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
1215 RUN_WITH_UMASK(0000)
1216 r = mkdir_label(i->path, i->mode);
1217
1218 if (r < 0) {
1219 int k;
1220
1221 if (r != -EEXIST && r != -EROFS)
1222 return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
1223
1224 k = is_dir(i->path, false);
1225 if (k == -ENOENT && r == -EROFS)
1226 return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path);
1227 if (k < 0)
1228 return log_error_errno(k, "Failed to check if %s exists: %m", i->path);
1229 if (!k) {
1230 log_warning("\"%s\" already exists and is not a directory.", i->path);
1231 return 0;
1232 }
1233
1234 creation = CREATION_EXISTING;
1235 } else
1236 creation = CREATION_NORMAL;
1237
1238 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
1239
1240 r = path_set_perms(i, i->path);
1241 if (r < 0)
1242 return r;
1243
1244 break;
1245
1246 case CREATE_FIFO:
1247
1248 RUN_WITH_UMASK(0000) {
1249 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1250 r = mkfifo(i->path, i->mode);
1251 mac_selinux_create_file_clear();
1252 }
1253
1254 if (r < 0) {
1255 if (errno != EEXIST)
1256 return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
1257
1258 if (lstat(i->path, &st) < 0)
1259 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1260
1261 if (!S_ISFIFO(st.st_mode)) {
1262
1263 if (i->force) {
1264 RUN_WITH_UMASK(0000) {
1265 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1266 r = mkfifo_atomic(i->path, i->mode);
1267 mac_selinux_create_file_clear();
1268 }
1269
1270 if (r < 0)
1271 return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
1272 creation = CREATION_FORCE;
1273 } else {
1274 log_warning("\"%s\" already exists and is not a fifo.", i->path);
1275 return 0;
1276 }
1277 } else
1278 creation = CREATION_EXISTING;
1279 } else
1280 creation = CREATION_NORMAL;
1281 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
1282
1283 r = path_set_perms(i, i->path);
1284 if (r < 0)
1285 return r;
1286
1287 break;
1288 }
1289
1290 case CREATE_SYMLINK: {
1291 r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
1292 if (r < 0)
1293 return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument);
1294
1295 mac_selinux_create_file_prepare(i->path, S_IFLNK);
1296 r = symlink(resolved, i->path);
1297 mac_selinux_create_file_clear();
1298
1299 if (r < 0) {
1300 _cleanup_free_ char *x = NULL;
1301
1302 if (errno != EEXIST)
1303 return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path);
1304
1305 r = readlink_malloc(i->path, &x);
1306 if (r < 0 || !streq(resolved, x)) {
1307
1308 if (i->force) {
1309 mac_selinux_create_file_prepare(i->path, S_IFLNK);
1310 r = symlink_atomic(resolved, i->path);
1311 mac_selinux_create_file_clear();
1312
1313 if (r < 0)
1314 return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path);
1315
1316 creation = CREATION_FORCE;
1317 } else {
1318 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
1319 return 0;
1320 }
1321 } else
1322 creation = CREATION_EXISTING;
1323 } else
1324
1325 creation = CREATION_NORMAL;
1326 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
1327 break;
1328 }
1329
1330 case CREATE_BLOCK_DEVICE:
1331 case CREATE_CHAR_DEVICE: {
1332 mode_t file_type;
1333
1334 if (have_effective_cap(CAP_MKNOD) == 0) {
1335 /* In a container we lack CAP_MKNOD. We
1336 shouldn't attempt to create the device node in
1337 that case to avoid noise, and we don't support
1338 virtualized devices in containers anyway. */
1339
1340 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
1341 return 0;
1342 }
1343
1344 file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
1345
1346 RUN_WITH_UMASK(0000) {
1347 mac_selinux_create_file_prepare(i->path, file_type);
1348 r = mknod(i->path, i->mode | file_type, i->major_minor);
1349 mac_selinux_create_file_clear();
1350 }
1351
1352 if (r < 0) {
1353 if (errno == EPERM) {
1354 log_debug("We lack permissions, possibly because of cgroup configuration; "
1355 "skipping creation of device node %s.", i->path);
1356 return 0;
1357 }
1358
1359 if (errno != EEXIST)
1360 return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
1361
1362 if (lstat(i->path, &st) < 0)
1363 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1364
1365 if ((st.st_mode & S_IFMT) != file_type) {
1366
1367 if (i->force) {
1368
1369 RUN_WITH_UMASK(0000) {
1370 mac_selinux_create_file_prepare(i->path, file_type);
1371 r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
1372 mac_selinux_create_file_clear();
1373 }
1374
1375 if (r < 0)
1376 return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
1377 creation = CREATION_FORCE;
1378 } else {
1379 log_debug("%s is not a device node.", i->path);
1380 return 0;
1381 }
1382 } else
1383 creation = CREATION_EXISTING;
1384 } else
1385 creation = CREATION_NORMAL;
1386
1387 log_debug("%s %s device node \"%s\" %u:%u.",
1388 creation_mode_verb_to_string(creation),
1389 i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
1390 i->path, major(i->mode), minor(i->mode));
1391
1392 r = path_set_perms(i, i->path);
1393 if (r < 0)
1394 return r;
1395
1396 break;
1397 }
1398
1399 case ADJUST_MODE:
1400 case RELABEL_PATH:
1401 r = glob_item(i, path_set_perms, false);
1402 if (r < 0)
1403 return r;
1404 break;
1405
1406 case RECURSIVE_RELABEL_PATH:
1407 r = glob_item(i, path_set_perms, true);
1408 if (r < 0)
1409 return r;
1410 break;
1411
1412 case SET_XATTR:
1413 r = glob_item(i, path_set_xattrs, false);
1414 if (r < 0)
1415 return r;
1416 break;
1417
1418 case RECURSIVE_SET_XATTR:
1419 r = glob_item(i, path_set_xattrs, true);
1420 if (r < 0)
1421 return r;
1422 break;
1423
1424 case SET_ACL:
1425 r = glob_item(i, path_set_acls, false);
1426 if (r < 0)
1427 return r;
1428 break;
1429
1430 case RECURSIVE_SET_ACL:
1431 r = glob_item(i, path_set_acls, true);
1432 if (r < 0)
1433 return r;
1434 break;
1435
1436 case SET_ATTRIBUTE:
1437 r = glob_item(i, path_set_attribute, false);
1438 if (r < 0)
1439 return r;
1440 break;
1441
1442 case RECURSIVE_SET_ATTRIBUTE:
1443 r = glob_item(i, path_set_attribute, true);
1444 if (r < 0)
1445 return r;
1446 break;
1447 }
1448
1449 return 0;
1450 }
1451
1452 static int remove_item_instance(Item *i, const char *instance) {
1453 int r;
1454
1455 assert(i);
1456
1457 switch (i->type) {
1458
1459 case REMOVE_PATH:
1460 if (remove(instance) < 0 && errno != ENOENT)
1461 return log_error_errno(errno, "rm(%s): %m", instance);
1462
1463 break;
1464
1465 case TRUNCATE_DIRECTORY:
1466 case RECURSIVE_REMOVE_PATH:
1467 /* FIXME: we probably should use dir_cleanup() here
1468 * instead of rm_rf() so that 'x' is honoured. */
1469 log_debug("rm -rf \"%s\"", instance);
1470 r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL);
1471 if (r < 0 && r != -ENOENT)
1472 return log_error_errno(r, "rm_rf(%s): %m", instance);
1473
1474 break;
1475
1476 default:
1477 assert_not_reached("wut?");
1478 }
1479
1480 return 0;
1481 }
1482
1483 static int remove_item(Item *i) {
1484 int r = 0;
1485
1486 assert(i);
1487
1488 log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
1489
1490 switch (i->type) {
1491
1492 case CREATE_FILE:
1493 case TRUNCATE_FILE:
1494 case CREATE_DIRECTORY:
1495 case CREATE_SUBVOLUME:
1496 case CREATE_FIFO:
1497 case CREATE_SYMLINK:
1498 case CREATE_CHAR_DEVICE:
1499 case CREATE_BLOCK_DEVICE:
1500 case IGNORE_PATH:
1501 case IGNORE_DIRECTORY_PATH:
1502 case ADJUST_MODE:
1503 case RELABEL_PATH:
1504 case RECURSIVE_RELABEL_PATH:
1505 case WRITE_FILE:
1506 case COPY_FILES:
1507 case SET_XATTR:
1508 case RECURSIVE_SET_XATTR:
1509 case SET_ACL:
1510 case RECURSIVE_SET_ACL:
1511 case SET_ATTRIBUTE:
1512 case RECURSIVE_SET_ATTRIBUTE:
1513 break;
1514
1515 case REMOVE_PATH:
1516 case TRUNCATE_DIRECTORY:
1517 case RECURSIVE_REMOVE_PATH:
1518 r = glob_item(i, remove_item_instance, false);
1519 break;
1520 }
1521
1522 return r;
1523 }
1524
1525 static int clean_item_instance(Item *i, const char* instance) {
1526 _cleanup_closedir_ DIR *d = NULL;
1527 struct stat s, ps;
1528 bool mountpoint;
1529 usec_t cutoff, n;
1530 char timestamp[FORMAT_TIMESTAMP_MAX];
1531
1532 assert(i);
1533
1534 if (!i->age_set)
1535 return 0;
1536
1537 n = now(CLOCK_REALTIME);
1538 if (n < i->age)
1539 return 0;
1540
1541 cutoff = n - i->age;
1542
1543 d = opendir_nomod(instance);
1544 if (!d) {
1545 if (errno == ENOENT || errno == ENOTDIR) {
1546 log_debug_errno(errno, "Directory \"%s\": %m", instance);
1547 return 0;
1548 }
1549
1550 log_error_errno(errno, "Failed to open directory %s: %m", instance);
1551 return -errno;
1552 }
1553
1554 if (fstat(dirfd(d), &s) < 0)
1555 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1556
1557 if (!S_ISDIR(s.st_mode)) {
1558 log_error("%s is not a directory.", i->path);
1559 return -ENOTDIR;
1560 }
1561
1562 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
1563 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
1564
1565 mountpoint = s.st_dev != ps.st_dev ||
1566 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
1567
1568 log_debug("Cleanup threshold for %s \"%s\" is %s",
1569 mountpoint ? "mount point" : "directory",
1570 instance,
1571 format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
1572
1573 return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
1574 MAX_DEPTH, i->keep_first_level);
1575 }
1576
1577 static int clean_item(Item *i) {
1578 int r = 0;
1579
1580 assert(i);
1581
1582 log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
1583
1584 switch (i->type) {
1585 case CREATE_DIRECTORY:
1586 case CREATE_SUBVOLUME:
1587 case TRUNCATE_DIRECTORY:
1588 case IGNORE_PATH:
1589 case COPY_FILES:
1590 clean_item_instance(i, i->path);
1591 break;
1592 case IGNORE_DIRECTORY_PATH:
1593 r = glob_item(i, clean_item_instance, false);
1594 break;
1595 default:
1596 break;
1597 }
1598
1599 return r;
1600 }
1601
1602 static int process_item_array(ItemArray *array);
1603
1604 static int process_item(Item *i) {
1605 int r, q, p, t = 0;
1606 _cleanup_free_ char *prefix = NULL;
1607
1608 assert(i);
1609
1610 if (i->done)
1611 return 0;
1612
1613 i->done = true;
1614
1615 prefix = malloc(strlen(i->path) + 1);
1616 if (!prefix)
1617 return log_oom();
1618
1619 PATH_FOREACH_PREFIX(prefix, i->path) {
1620 ItemArray *j;
1621
1622 j = ordered_hashmap_get(items, prefix);
1623 if (j) {
1624 int s;
1625
1626 s = process_item_array(j);
1627 if (s < 0 && t == 0)
1628 t = s;
1629 }
1630 }
1631
1632 r = arg_create ? create_item(i) : 0;
1633 q = arg_remove ? remove_item(i) : 0;
1634 p = arg_clean ? clean_item(i) : 0;
1635
1636 return t < 0 ? t :
1637 r < 0 ? r :
1638 q < 0 ? q :
1639 p;
1640 }
1641
1642 static int process_item_array(ItemArray *array) {
1643 unsigned n;
1644 int r = 0, k;
1645
1646 assert(array);
1647
1648 for (n = 0; n < array->count; n++) {
1649 k = process_item(array->items + n);
1650 if (k < 0 && r == 0)
1651 r = k;
1652 }
1653
1654 return r;
1655 }
1656
1657 static void item_free_contents(Item *i) {
1658 assert(i);
1659 free(i->path);
1660 free(i->argument);
1661 strv_free(i->xattrs);
1662
1663 #ifdef HAVE_ACL
1664 acl_free(i->acl_access);
1665 acl_free(i->acl_default);
1666 #endif
1667 }
1668
1669 static void item_array_free(ItemArray *a) {
1670 unsigned n;
1671
1672 if (!a)
1673 return;
1674
1675 for (n = 0; n < a->count; n++)
1676 item_free_contents(a->items + n);
1677 free(a->items);
1678 free(a);
1679 }
1680
1681 static int item_compare(const void *a, const void *b) {
1682 const Item *x = a, *y = b;
1683
1684 /* Make sure that the ownership taking item is put first, so
1685 * that we first create the node, and then can adjust it */
1686
1687 if (takes_ownership(x->type) && !takes_ownership(y->type))
1688 return -1;
1689 if (!takes_ownership(x->type) && takes_ownership(y->type))
1690 return 1;
1691
1692 return (int) x->type - (int) y->type;
1693 }
1694
1695 static bool item_compatible(Item *a, Item *b) {
1696 assert(a);
1697 assert(b);
1698 assert(streq(a->path, b->path));
1699
1700 if (takes_ownership(a->type) && takes_ownership(b->type))
1701 /* check if the items are the same */
1702 return streq_ptr(a->argument, b->argument) &&
1703
1704 a->uid_set == b->uid_set &&
1705 a->uid == b->uid &&
1706
1707 a->gid_set == b->gid_set &&
1708 a->gid == b->gid &&
1709
1710 a->mode_set == b->mode_set &&
1711 a->mode == b->mode &&
1712
1713 a->age_set == b->age_set &&
1714 a->age == b->age &&
1715
1716 a->mask_perms == b->mask_perms &&
1717
1718 a->keep_first_level == b->keep_first_level &&
1719
1720 a->major_minor == b->major_minor;
1721
1722 return true;
1723 }
1724
1725 static bool should_include_path(const char *path) {
1726 char **prefix;
1727
1728 STRV_FOREACH(prefix, arg_exclude_prefixes)
1729 if (path_startswith(path, *prefix)) {
1730 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1731 path, *prefix);
1732 return false;
1733 }
1734
1735 STRV_FOREACH(prefix, arg_include_prefixes)
1736 if (path_startswith(path, *prefix)) {
1737 log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
1738 return true;
1739 }
1740
1741 /* no matches, so we should include this path only if we
1742 * have no whitelist at all */
1743 if (strv_length(arg_include_prefixes) == 0)
1744 return true;
1745
1746 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
1747 return false;
1748 }
1749
1750 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1751
1752 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
1753 _cleanup_(item_free_contents) Item i = {};
1754 ItemArray *existing;
1755 OrderedHashmap *h;
1756 int r, pos;
1757 bool force = false, boot = false;
1758
1759 assert(fname);
1760 assert(line >= 1);
1761 assert(buffer);
1762
1763 r = unquote_many_words(
1764 &buffer,
1765 0,
1766 &action,
1767 &path,
1768 &mode,
1769 &user,
1770 &group,
1771 &age,
1772 NULL);
1773 if (r < 0)
1774 return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
1775 else if (r < 2) {
1776 log_error("[%s:%u] Syntax error.", fname, line);
1777 return -EIO;
1778 }
1779
1780 if (!isempty(buffer) && !streq(buffer, "-")) {
1781 i.argument = strdup(buffer);
1782 if (!i.argument)
1783 return log_oom();
1784 }
1785
1786 if (isempty(action)) {
1787 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
1788 return -EINVAL;
1789 }
1790
1791 for (pos = 1; action[pos]; pos++) {
1792 if (action[pos] == '!' && !boot)
1793 boot = true;
1794 else if (action[pos] == '+' && !force)
1795 force = true;
1796 else {
1797 log_error("[%s:%u] Unknown modifiers in command '%s'",
1798 fname, line, action);
1799 return -EINVAL;
1800 }
1801 }
1802
1803 if (boot && !arg_boot) {
1804 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1805 action, path);
1806 return 0;
1807 }
1808
1809 i.type = action[0];
1810 i.force = force;
1811
1812 r = specifier_printf(path, specifier_table, NULL, &i.path);
1813 if (r < 0) {
1814 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1815 return r;
1816 }
1817
1818 switch (i.type) {
1819
1820 case CREATE_DIRECTORY:
1821 case CREATE_SUBVOLUME:
1822 case TRUNCATE_DIRECTORY:
1823 case CREATE_FIFO:
1824 case IGNORE_PATH:
1825 case IGNORE_DIRECTORY_PATH:
1826 case REMOVE_PATH:
1827 case RECURSIVE_REMOVE_PATH:
1828 case ADJUST_MODE:
1829 case RELABEL_PATH:
1830 case RECURSIVE_RELABEL_PATH:
1831 if (i.argument)
1832 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
1833
1834 break;
1835
1836 case CREATE_FILE:
1837 case TRUNCATE_FILE:
1838 break;
1839
1840 case CREATE_SYMLINK:
1841 if (!i.argument) {
1842 i.argument = strappend("/usr/share/factory/", i.path);
1843 if (!i.argument)
1844 return log_oom();
1845 }
1846 break;
1847
1848 case WRITE_FILE:
1849 if (!i.argument) {
1850 log_error("[%s:%u] Write file requires argument.", fname, line);
1851 return -EBADMSG;
1852 }
1853 break;
1854
1855 case COPY_FILES:
1856 if (!i.argument) {
1857 i.argument = strappend("/usr/share/factory/", i.path);
1858 if (!i.argument)
1859 return log_oom();
1860 } else if (!path_is_absolute(i.argument)) {
1861 log_error("[%s:%u] Source path is not absolute.", fname, line);
1862 return -EBADMSG;
1863 }
1864
1865 path_kill_slashes(i.argument);
1866 break;
1867
1868 case CREATE_CHAR_DEVICE:
1869 case CREATE_BLOCK_DEVICE: {
1870 unsigned major, minor;
1871
1872 if (!i.argument) {
1873 log_error("[%s:%u] Device file requires argument.", fname, line);
1874 return -EBADMSG;
1875 }
1876
1877 if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
1878 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
1879 return -EBADMSG;
1880 }
1881
1882 i.major_minor = makedev(major, minor);
1883 break;
1884 }
1885
1886 case SET_XATTR:
1887 case RECURSIVE_SET_XATTR:
1888 if (!i.argument) {
1889 log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
1890 return -EBADMSG;
1891 }
1892 r = parse_xattrs_from_arg(&i);
1893 if (r < 0)
1894 return r;
1895 break;
1896
1897 case SET_ACL:
1898 case RECURSIVE_SET_ACL:
1899 if (!i.argument) {
1900 log_error("[%s:%u] Set ACLs requires argument.", fname, line);
1901 return -EBADMSG;
1902 }
1903 r = parse_acls_from_arg(&i);
1904 if (r < 0)
1905 return r;
1906 break;
1907
1908 case SET_ATTRIBUTE:
1909 case RECURSIVE_SET_ATTRIBUTE:
1910 if (!i.argument) {
1911 log_error("[%s:%u] Set file attribute requires argument.", fname, line);
1912 return -EBADMSG;
1913 }
1914 r = parse_attribute_from_arg(&i);
1915 if (r < 0)
1916 return r;
1917 break;
1918
1919 default:
1920 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
1921 return -EBADMSG;
1922 }
1923
1924 if (!path_is_absolute(i.path)) {
1925 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
1926 return -EBADMSG;
1927 }
1928
1929 path_kill_slashes(i.path);
1930
1931 if (!should_include_path(i.path))
1932 return 0;
1933
1934 if (arg_root) {
1935 char *p;
1936
1937 p = prefix_root(arg_root, i.path);
1938 if (!p)
1939 return log_oom();
1940
1941 free(i.path);
1942 i.path = p;
1943 }
1944
1945 if (!isempty(user) && !streq(user, "-")) {
1946 const char *u = user;
1947
1948 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
1949 if (r < 0) {
1950 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1951 return r;
1952 }
1953
1954 i.uid_set = true;
1955 }
1956
1957 if (!isempty(group) && !streq(group, "-")) {
1958 const char *g = group;
1959
1960 r = get_group_creds(&g, &i.gid);
1961 if (r < 0) {
1962 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1963 return r;
1964 }
1965
1966 i.gid_set = true;
1967 }
1968
1969 if (!isempty(mode) && !streq(mode, "-")) {
1970 const char *mm = mode;
1971 unsigned m;
1972
1973 if (*mm == '~') {
1974 i.mask_perms = true;
1975 mm++;
1976 }
1977
1978 if (parse_mode(mm, &m) < 0) {
1979 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1980 return -EBADMSG;
1981 }
1982
1983 i.mode = m;
1984 i.mode_set = true;
1985 } else
1986 i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
1987 ? 0755 : 0644;
1988
1989 if (!isempty(age) && !streq(age, "-")) {
1990 const char *a = age;
1991
1992 if (*a == '~') {
1993 i.keep_first_level = true;
1994 a++;
1995 }
1996
1997 if (parse_sec(a, &i.age) < 0) {
1998 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1999 return -EBADMSG;
2000 }
2001
2002 i.age_set = true;
2003 }
2004
2005 h = needs_glob(i.type) ? globs : items;
2006
2007 existing = ordered_hashmap_get(h, i.path);
2008 if (existing) {
2009 unsigned n;
2010
2011 for (n = 0; n < existing->count; n++) {
2012 if (!item_compatible(existing->items + n, &i)) {
2013 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2014 fname, line, i.path);
2015 return 0;
2016 }
2017 }
2018 } else {
2019 existing = new0(ItemArray, 1);
2020 r = ordered_hashmap_put(h, i.path, existing);
2021 if (r < 0)
2022 return log_oom();
2023 }
2024
2025 if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
2026 return log_oom();
2027
2028 memcpy(existing->items + existing->count++, &i, sizeof(i));
2029
2030 /* Sort item array, to enforce stable ordering of application */
2031 qsort_safe(existing->items, existing->count, sizeof(Item), item_compare);
2032
2033 zero(i);
2034 return 0;
2035 }
2036
2037 static void help(void) {
2038 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2039 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2040 " -h --help Show this help\n"
2041 " --version Show package version\n"
2042 " --create Create marked files/directories\n"
2043 " --clean Clean up marked directories\n"
2044 " --remove Remove marked files/directories\n"
2045 " --boot Execute actions only safe at boot\n"
2046 " --prefix=PATH Only apply rules with the specified prefix\n"
2047 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2048 " --root=PATH Operate on an alternate filesystem root\n",
2049 program_invocation_short_name);
2050 }
2051
2052 static int parse_argv(int argc, char *argv[]) {
2053
2054 enum {
2055 ARG_VERSION = 0x100,
2056 ARG_CREATE,
2057 ARG_CLEAN,
2058 ARG_REMOVE,
2059 ARG_BOOT,
2060 ARG_PREFIX,
2061 ARG_EXCLUDE_PREFIX,
2062 ARG_ROOT,
2063 };
2064
2065 static const struct option options[] = {
2066 { "help", no_argument, NULL, 'h' },
2067 { "version", no_argument, NULL, ARG_VERSION },
2068 { "create", no_argument, NULL, ARG_CREATE },
2069 { "clean", no_argument, NULL, ARG_CLEAN },
2070 { "remove", no_argument, NULL, ARG_REMOVE },
2071 { "boot", no_argument, NULL, ARG_BOOT },
2072 { "prefix", required_argument, NULL, ARG_PREFIX },
2073 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
2074 { "root", required_argument, NULL, ARG_ROOT },
2075 {}
2076 };
2077
2078 int c;
2079
2080 assert(argc >= 0);
2081 assert(argv);
2082
2083 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
2084
2085 switch (c) {
2086
2087 case 'h':
2088 help();
2089 return 0;
2090
2091 case ARG_VERSION:
2092 puts(PACKAGE_STRING);
2093 puts(SYSTEMD_FEATURES);
2094 return 0;
2095
2096 case ARG_CREATE:
2097 arg_create = true;
2098 break;
2099
2100 case ARG_CLEAN:
2101 arg_clean = true;
2102 break;
2103
2104 case ARG_REMOVE:
2105 arg_remove = true;
2106 break;
2107
2108 case ARG_BOOT:
2109 arg_boot = true;
2110 break;
2111
2112 case ARG_PREFIX:
2113 if (strv_push(&arg_include_prefixes, optarg) < 0)
2114 return log_oom();
2115 break;
2116
2117 case ARG_EXCLUDE_PREFIX:
2118 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
2119 return log_oom();
2120 break;
2121
2122 case ARG_ROOT:
2123 free(arg_root);
2124 arg_root = path_make_absolute_cwd(optarg);
2125 if (!arg_root)
2126 return log_oom();
2127
2128 path_kill_slashes(arg_root);
2129 break;
2130
2131 case '?':
2132 return -EINVAL;
2133
2134 default:
2135 assert_not_reached("Unhandled option");
2136 }
2137
2138 if (!arg_clean && !arg_create && !arg_remove) {
2139 log_error("You need to specify at least one of --clean, --create or --remove.");
2140 return -EINVAL;
2141 }
2142
2143 return 1;
2144 }
2145
2146 static int read_config_file(const char *fn, bool ignore_enoent) {
2147 _cleanup_fclose_ FILE *f = NULL;
2148 char line[LINE_MAX];
2149 Iterator iterator;
2150 unsigned v = 0;
2151 Item *i;
2152 int r;
2153
2154 assert(fn);
2155
2156 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
2157 if (r < 0) {
2158 if (ignore_enoent && r == -ENOENT) {
2159 log_debug_errno(r, "Failed to open \"%s\": %m", fn);
2160 return 0;
2161 }
2162
2163 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
2164 }
2165 log_debug("Reading config file \"%s\".", fn);
2166
2167 FOREACH_LINE(line, f, break) {
2168 char *l;
2169 int k;
2170
2171 v++;
2172
2173 l = strstrip(line);
2174 if (*l == '#' || *l == 0)
2175 continue;
2176
2177 k = parse_line(fn, v, l);
2178 if (k < 0 && r == 0)
2179 r = k;
2180 }
2181
2182 /* we have to determine age parameter for each entry of type X */
2183 ORDERED_HASHMAP_FOREACH(i, globs, iterator) {
2184 Iterator iter;
2185 Item *j, *candidate_item = NULL;
2186
2187 if (i->type != IGNORE_DIRECTORY_PATH)
2188 continue;
2189
2190 ORDERED_HASHMAP_FOREACH(j, items, iter) {
2191 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
2192 continue;
2193
2194 if (path_equal(j->path, i->path)) {
2195 candidate_item = j;
2196 break;
2197 }
2198
2199 if ((!candidate_item && path_startswith(i->path, j->path)) ||
2200 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
2201 candidate_item = j;
2202 }
2203
2204 if (candidate_item && candidate_item->age_set) {
2205 i->age = candidate_item->age;
2206 i->age_set = true;
2207 }
2208 }
2209
2210 if (ferror(f)) {
2211 log_error_errno(errno, "Failed to read from file %s: %m", fn);
2212 if (r == 0)
2213 r = -EIO;
2214 }
2215
2216 return r;
2217 }
2218
2219 int main(int argc, char *argv[]) {
2220 int r, k;
2221 ItemArray *a;
2222 Iterator iterator;
2223
2224 r = parse_argv(argc, argv);
2225 if (r <= 0)
2226 goto finish;
2227
2228 log_set_target(LOG_TARGET_AUTO);
2229 log_parse_environment();
2230 log_open();
2231
2232 umask(0022);
2233
2234 mac_selinux_init(NULL);
2235
2236 items = ordered_hashmap_new(&string_hash_ops);
2237 globs = ordered_hashmap_new(&string_hash_ops);
2238
2239 if (!items || !globs) {
2240 r = log_oom();
2241 goto finish;
2242 }
2243
2244 r = 0;
2245
2246 if (optind < argc) {
2247 int j;
2248
2249 for (j = optind; j < argc; j++) {
2250 k = read_config_file(argv[j], false);
2251 if (k < 0 && r == 0)
2252 r = k;
2253 }
2254
2255 } else {
2256 _cleanup_strv_free_ char **files = NULL;
2257 char **f;
2258
2259 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
2260 if (r < 0) {
2261 log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
2262 goto finish;
2263 }
2264
2265 STRV_FOREACH(f, files) {
2266 k = read_config_file(*f, true);
2267 if (k < 0 && r == 0)
2268 r = k;
2269 }
2270 }
2271
2272 /* The non-globbing ones usually create things, hence we apply
2273 * them first */
2274 ORDERED_HASHMAP_FOREACH(a, items, iterator) {
2275 k = process_item_array(a);
2276 if (k < 0 && r == 0)
2277 r = k;
2278 }
2279
2280 /* The globbing ones usually alter things, hence we apply them
2281 * second. */
2282 ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
2283 k = process_item_array(a);
2284 if (k < 0 && r == 0)
2285 r = k;
2286 }
2287
2288 finish:
2289 while ((a = ordered_hashmap_steal_first(items)))
2290 item_array_free(a);
2291
2292 while ((a = ordered_hashmap_steal_first(globs)))
2293 item_array_free(a);
2294
2295 ordered_hashmap_free(items);
2296 ordered_hashmap_free(globs);
2297
2298 free(arg_include_prefixes);
2299 free(arg_exclude_prefixes);
2300 free(arg_root);
2301
2302 set_free_free(unix_sockets);
2303
2304 mac_selinux_finish();
2305
2306 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2307 }