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