]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tmpfiles/tmpfiles.c
cg_path_get_user_unit(): Did not correctly parse user-unit templates.
[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 632 _cleanup_free_ char *tmp = NULL, *name = NULL,
4cd552bc 633 *value = NULL, *value2 = NULL, *_xattr = xattr;
505ef0e3 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;
f8eeeaf9
ZJS
685
686 assert(item);
687
50d9e46d
ZJS
688 /* If force (= modify) is set, we will not modify the acl
689 * afterwards, so the mask can be added now if necessary. */
690 r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
f8eeeaf9
ZJS
691 if (r < 0)
692 log_warning_errno(errno, "Failed to parse ACL \"%s\": %m. Ignoring",
693 item->argument);
694#else
695 log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
696#endif
697
698 return 0;
699}
700
50d9e46d 701static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modify) {
dd4105b0 702 _cleanup_(acl_freep) acl_t dup = NULL;
50d9e46d 703 int r;
582deb84 704 _cleanup_(acl_free_charpp) char *t = NULL;
50d9e46d
ZJS
705
706 if (modify) {
dd4105b0 707 r = acls_for_file(path, type, acl, &dup);
50d9e46d
ZJS
708 if (r < 0)
709 return r;
50d9e46d 710
dd4105b0
ZJS
711 r = calc_acl_mask_if_needed(&dup);
712 if (r < 0)
713 return r;
714 } else {
715 dup = acl_dup(acl);
716 if (!dup)
717 return -errno;
718
719 /* the mask was already added earlier if needed */
720 }
721
722 r = add_base_acls_if_needed(&dup, path);
723 if (r < 0)
724 return r;
725
582deb84
ZJS
726 t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
727 log_debug("\"%s\": setting %s ACL \"%s\"", path,
728 type == ACL_TYPE_ACCESS ? "access" : "default",
729 strna(t));
50d9e46d 730
582deb84
ZJS
731 r = acl_set_file(path, type, dup);
732 if (r < 0)
733 return log_error_errno(-errno,
734 "Setting %s ACL \"%s\" on %s failed: %m",
735 type == ACL_TYPE_ACCESS ? "access" : "default",
736 strna(t), path);
737 return 0;
50d9e46d
ZJS
738}
739
b705ab6a 740static int path_set_acls(Item *item, const char *path) {
f8eeeaf9
ZJS
741#ifdef HAVE_ACL
742 int r;
743
744 assert(item);
745 assert(path);
746
747 if (item->acl_access) {
50d9e46d
ZJS
748 r = path_set_acl(path, ACL_TYPE_ACCESS, item->acl_access, item->force);
749 if (r < 0)
750 return r;
f8eeeaf9
ZJS
751 }
752
753 if (item->acl_default) {
50d9e46d
ZJS
754 r = path_set_acl(path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
755 if (r < 0)
756 return r;
f8eeeaf9
ZJS
757 }
758#endif
759
760 return 0;
761}
762
d4e9eb91 763static int write_one_file(Item *i, const char *path) {
43ad6e31
LP
764 _cleanup_close_ int fd = -1;
765 int flags, r = 0;
d4e9eb91 766 struct stat st;
d4e9eb91 767
874f1947
LP
768 assert(i);
769 assert(path);
770
43ad6e31
LP
771 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
772 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
d4e9eb91 773
43ad6e31 774 RUN_WITH_UMASK(0000) {
ecabcf8b 775 mac_selinux_create_file_prepare(path, S_IFREG);
43ad6e31 776 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
ecabcf8b 777 mac_selinux_create_file_clear();
5c0d398d 778 }
d4e9eb91
DR
779
780 if (fd < 0) {
582deb84
ZJS
781 if (i->type == WRITE_FILE && errno == ENOENT) {
782 log_debug_errno(errno, "Not writing \"%s\": %m", path);
d4e9eb91 783 return 0;
582deb84 784 }
d4e9eb91 785
56f64d95 786 log_error_errno(errno, "Failed to create file %s: %m", path);
d4e9eb91
DR
787 return -errno;
788 }
789
790 if (i->argument) {
cde684a2 791 _cleanup_free_ char *unescaped;
582deb84
ZJS
792
793 log_debug("%s to \"%s\".",
794 i->type == CREATE_FILE ? "Appending" : "Writing", path);
d4e9eb91 795
54693d9b 796 unescaped = cunescape(i->argument);
43ad6e31 797 if (!unescaped)
54693d9b 798 return log_oom();
d4e9eb91 799
582deb84
ZJS
800 r = loop_write(fd, unescaped, strlen(unescaped), false);
801 if (r < 0)
802 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
803 } else
804 log_debug("\"%s\" has been created.", path);
d4e9eb91 805
43ad6e31 806 fd = safe_close(fd);
3612fbc1 807
4a62c710
MS
808 if (stat(path, &st) < 0)
809 return log_error_errno(errno, "stat(%s) failed: %m", path);
d4e9eb91
DR
810
811 if (!S_ISREG(st.st_mode)) {
812 log_error("%s is not a file.", path);
813 return -EEXIST;
814 }
815
b705ab6a 816 r = path_set_perms(i, path);
d4e9eb91
DR
817 if (r < 0)
818 return r;
819
820 return 0;
821}
822
081043cf
ZJS
823typedef int (*action_t)(Item *, const char *);
824
825static int item_do_children(Item *i, const char *path, action_t action) {
7fd1b19b 826 _cleanup_closedir_ DIR *d;
e73a03e0
LP
827 int r = 0;
828
829 assert(i);
830 assert(path);
a8d88783
MS
831
832 /* This returns the first error we run into, but nevertheless
833 * tries to go on */
834
df99a9ef
ZJS
835 d = opendir_nomod(path);
836 if (!d)
e73a03e0 837 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
a8d88783
MS
838
839 for (;;) {
e73a03e0 840 _cleanup_free_ char *p = NULL;
7d5e9c0f 841 struct dirent *de;
e73a03e0 842 int q;
a8d88783 843
d78096b3
FW
844 errno = 0;
845 de = readdir(d);
e73a03e0
LP
846 if (!de) {
847 if (errno != 0 && r == 0)
848 r = -errno;
a8d88783 849
a8d88783 850 break;
e73a03e0 851 }
a8d88783 852
7fcb4b9b 853 if (STR_IN_SET(de->d_name, ".", ".."))
a8d88783
MS
854 continue;
855
e73a03e0
LP
856 p = strjoin(path, "/", de->d_name, NULL);
857 if (!p)
858 return -ENOMEM;
a8d88783 859
081043cf 860 q = action(i, p);
e73a03e0
LP
861 if (q < 0 && q != -ENOENT && r == 0)
862 r = q;
a8d88783 863
e73a03e0 864 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
081043cf 865 q = item_do_children(i, p, action);
e73a03e0
LP
866 if (q < 0 && r == 0)
867 r = q;
a8d88783 868 }
a8d88783
MS
869 }
870
e73a03e0 871 return r;
a8d88783
MS
872}
873
081043cf 874static int glob_item(Item *i, action_t action, bool recursive) {
df99a9ef 875 _cleanup_globfree_ glob_t g = {
ebf31a1f
ZJS
876 .gl_closedir = (void (*)(void *)) closedir,
877 .gl_readdir = (struct dirent *(*)(void *)) readdir,
878 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
df99a9ef
ZJS
879 .gl_lstat = lstat,
880 .gl_stat = stat,
881 };
e73a03e0 882 int r = 0, k;
99e68c0b
MS
883 char **fn;
884
99e68c0b 885 errno = 0;
df99a9ef 886 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
081043cf
ZJS
887 if (k != 0 && k != GLOB_NOMATCH)
888 return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
99e68c0b 889
c84a9488
ZJS
890 STRV_FOREACH(fn, g.gl_pathv) {
891 k = action(i, *fn);
e73a03e0 892 if (k < 0 && r == 0)
99e68c0b 893 r = k;
081043cf
ZJS
894
895 if (recursive) {
896 k = item_do_children(i, *fn, action);
897 if (k < 0 && r == 0)
898 r = k;
899 }
c84a9488 900 }
99e68c0b 901
99e68c0b
MS
902 return r;
903}
904
294929f8
ZJS
905typedef enum {
906 CREATION_NORMAL,
907 CREATION_EXISTING,
908 CREATION_FORCE,
7a7d5db7
LP
909 _CREATION_MODE_MAX,
910 _CREATION_MODE_INVALID = -1
294929f8
ZJS
911} CreationMode;
912
7a7d5db7
LP
913static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
914 [CREATION_NORMAL] = "Created",
915 [CREATION_EXISTING] = "Found existing",
916 [CREATION_FORCE] = "Created replacement",
917};
918
919DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
294929f8 920
3b63d2d3 921static int create_item(Item *i) {
3b63d2d3 922 struct stat st;
df28bc08 923 int r = 0;
294929f8 924 CreationMode creation;
5008d581 925
3b63d2d3 926 assert(i);
5008d581 927
582deb84
ZJS
928 log_debug("Running create action for entry %c %s", (char) i->type, i->path);
929
3b63d2d3
LP
930 switch (i->type) {
931
932 case IGNORE_PATH:
78a92a5a 933 case IGNORE_DIRECTORY_PATH:
3b63d2d3
LP
934 case REMOVE_PATH:
935 case RECURSIVE_REMOVE_PATH:
936 return 0;
5008d581 937
3b63d2d3 938 case CREATE_FILE:
31ed59c5 939 case TRUNCATE_FILE:
1845fdd9
DR
940 r = write_one_file(i, i->path);
941 if (r < 0)
942 return r;
943 break;
265ffa1e 944
849958d1 945 case COPY_FILES:
582deb84 946 log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
e156347e 947 r = copy_tree(i->argument, i->path, false);
849958d1 948 if (r < 0) {
e156347e
LP
949 struct stat a, b;
950
8d3d7072
MS
951 if (r != -EEXIST)
952 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
e156347e 953
4a62c710
MS
954 if (stat(i->argument, &a) < 0)
955 return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
e156347e 956
4a62c710
MS
957 if (stat(i->path, &b) < 0)
958 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
e156347e
LP
959
960 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
961 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
962 return 0;
963 }
849958d1
LP
964 }
965
b705ab6a 966 r = path_set_perms(i, i->path);
849958d1
LP
967 if (r < 0)
968 return r;
969
970 break;
971
d4e9eb91 972 case WRITE_FILE:
081043cf 973 r = glob_item(i, write_one_file, false);
f05bc3f7
MS
974 if (r < 0)
975 return r;
5008d581 976
3b63d2d3
LP
977 break;
978
3b63d2d3 979 case CREATE_DIRECTORY:
d7b8eec7
LP
980 case TRUNCATE_DIRECTORY:
981 case CREATE_SUBVOLUME:
5008d581 982
d7b8eec7 983 RUN_WITH_UMASK(0000)
5c0d398d 984 mkdir_parents_label(i->path, 0755);
d7b8eec7 985
582deb84
ZJS
986 if (i->type == CREATE_SUBVOLUME)
987 RUN_WITH_UMASK((~i->mode) & 0777) {
d7b8eec7 988 r = btrfs_subvol_make(i->path);
582deb84
ZJS
989 log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path);
990 }
991 else
d7b8eec7
LP
992 r = 0;
993
582deb84 994 if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
d7b8eec7
LP
995 RUN_WITH_UMASK(0000)
996 r = mkdir_label(i->path, i->mode);
5008d581 997
e156347e 998 if (r < 0) {
f647962d 999 if (r != -EEXIST)
582deb84 1000 return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
5008d581 1001
4a62c710
MS
1002 if (stat(i->path, &st) < 0)
1003 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
5008d581 1004
e156347e 1005 if (!S_ISDIR(st.st_mode)) {
582deb84 1006 log_debug("\"%s\" already exists and is not a directory.", i->path);
e156347e
LP
1007 return 0;
1008 }
294929f8
ZJS
1009
1010 creation = CREATION_EXISTING;
1011 } else
1012 creation = CREATION_NORMAL;
7a7d5db7 1013 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
5008d581 1014
b705ab6a 1015 r = path_set_perms(i, i->path);
f05bc3f7
MS
1016 if (r < 0)
1017 return r;
3b63d2d3
LP
1018
1019 break;
ee17ee7c
LP
1020
1021 case CREATE_FIFO:
1022
5c0d398d 1023 RUN_WITH_UMASK(0000) {
ecabcf8b 1024 mac_selinux_create_file_prepare(i->path, S_IFIFO);
5c0d398d 1025 r = mkfifo(i->path, i->mode);
ecabcf8b 1026 mac_selinux_create_file_clear();
5c0d398d 1027 }
ee17ee7c 1028
1554afae 1029 if (r < 0) {
4a62c710
MS
1030 if (errno != EEXIST)
1031 return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
ee17ee7c 1032
4a62c710
MS
1033 if (stat(i->path, &st) < 0)
1034 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
ee17ee7c 1035
1554afae
LP
1036 if (!S_ISFIFO(st.st_mode)) {
1037
1038 if (i->force) {
1039
1040 RUN_WITH_UMASK(0000) {
ecabcf8b 1041 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1554afae 1042 r = mkfifo_atomic(i->path, i->mode);
ecabcf8b 1043 mac_selinux_create_file_clear();
1554afae
LP
1044 }
1045
f647962d
MS
1046 if (r < 0)
1047 return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
294929f8 1048 creation = CREATION_FORCE;
1554afae
LP
1049 } else {
1050 log_debug("%s is not a fifo.", i->path);
1051 return 0;
1052 }
294929f8
ZJS
1053 } else
1054 creation = CREATION_EXISTING;
1055 } else
1056 creation = CREATION_NORMAL;
7a7d5db7 1057 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
ee17ee7c 1058
b705ab6a 1059 r = path_set_perms(i, i->path);
f05bc3f7
MS
1060 if (r < 0)
1061 return r;
ee17ee7c
LP
1062
1063 break;
a8d88783 1064
2e78fa79 1065 case CREATE_SYMLINK:
468d726b 1066
ecabcf8b 1067 mac_selinux_create_file_prepare(i->path, S_IFLNK);
468d726b 1068 r = symlink(i->argument, i->path);
ecabcf8b 1069 mac_selinux_create_file_clear();
e9a5ef7c 1070
468d726b 1071 if (r < 0) {
2e78fa79 1072 _cleanup_free_ char *x = NULL;
468d726b 1073
4a62c710
MS
1074 if (errno != EEXIST)
1075 return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path);
2e78fa79
LP
1076
1077 r = readlink_malloc(i->path, &x);
1078 if (r < 0 || !streq(i->argument, x)) {
1079
1080 if (i->force) {
ecabcf8b 1081 mac_selinux_create_file_prepare(i->path, S_IFLNK);
2e78fa79 1082 r = symlink_atomic(i->argument, i->path);
ecabcf8b 1083 mac_selinux_create_file_clear();
2e78fa79 1084
f647962d
MS
1085 if (r < 0)
1086 return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
294929f8 1087 creation = CREATION_FORCE;
1554afae 1088 } else {
582deb84 1089 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
1554afae
LP
1090 return 0;
1091 }
294929f8
ZJS
1092 } else
1093 creation = CREATION_EXISTING;
1094 } else
1095 creation = CREATION_NORMAL;
7a7d5db7 1096 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
468d726b 1097
468d726b 1098 break;
468d726b
LP
1099
1100 case CREATE_BLOCK_DEVICE:
1101 case CREATE_CHAR_DEVICE: {
cb7ed9df
LP
1102 mode_t file_type;
1103
1104 if (have_effective_cap(CAP_MKNOD) == 0) {
1105 /* In a container we lack CAP_MKNOD. We
ab06eef8 1106 shouldn't attempt to create the device node in
cb7ed9df
LP
1107 that case to avoid noise, and we don't support
1108 virtualized devices in containers anyway. */
1109
1110 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
1111 return 0;
1112 }
1113
1554afae 1114 file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
468d726b 1115
5c0d398d 1116 RUN_WITH_UMASK(0000) {
ecabcf8b 1117 mac_selinux_create_file_prepare(i->path, file_type);
5c0d398d 1118 r = mknod(i->path, i->mode | file_type, i->major_minor);
ecabcf8b 1119 mac_selinux_create_file_clear();
5c0d398d 1120 }
468d726b 1121
6555ad8e
KS
1122 if (r < 0) {
1123 if (errno == EPERM) {
1124 log_debug("We lack permissions, possibly because of cgroup configuration; "
1125 "skipping creation of device node %s.", i->path);
1126 return 0;
1127 }
1128
4a62c710
MS
1129 if (errno != EEXIST)
1130 return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
468d726b 1131
4a62c710
MS
1132 if (stat(i->path, &st) < 0)
1133 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
468d726b 1134
1554afae
LP
1135 if ((st.st_mode & S_IFMT) != file_type) {
1136
1137 if (i->force) {
1138
1139 RUN_WITH_UMASK(0000) {
ecabcf8b 1140 mac_selinux_create_file_prepare(i->path, file_type);
1554afae 1141 r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
ecabcf8b 1142 mac_selinux_create_file_clear();
1554afae
LP
1143 }
1144
f647962d 1145 if (r < 0)
294929f8
ZJS
1146 return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
1147 creation = CREATION_FORCE;
1554afae
LP
1148 } else {
1149 log_debug("%s is not a device node.", i->path);
1150 return 0;
1151 }
294929f8
ZJS
1152 } else
1153 creation = CREATION_EXISTING;
1154 } else
1155 creation = CREATION_NORMAL;
1156 log_debug("%s %s device node \"%s\" %u:%u.",
7a7d5db7 1157 creation_mode_verb_to_string(creation),
582deb84
ZJS
1158 i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
1159 i->path, major(i->mode), minor(i->mode));
468d726b 1160
b705ab6a 1161 r = path_set_perms(i, i->path);
468d726b
LP
1162 if (r < 0)
1163 return r;
1164
1165 break;
1166 }
1167
e73a03e0 1168 case ADJUST_MODE:
777b87e7 1169 case RELABEL_PATH:
b705ab6a 1170 r = glob_item(i, path_set_perms, false);
777b87e7 1171 if (r < 0)
96ca8194 1172 return r;
777b87e7
MS
1173 break;
1174
a8d88783 1175 case RECURSIVE_RELABEL_PATH:
b705ab6a 1176 r = glob_item(i, path_set_perms, true);
a8d88783
MS
1177 if (r < 0)
1178 return r;
ebf4e801 1179 break;
e73a03e0 1180
ebf4e801 1181 case SET_XATTR:
b705ab6a
ZJS
1182 r = glob_item(i, path_set_xattrs, false);
1183 if (r < 0)
1184 return r;
1185 break;
1186
1187 case RECURSIVE_SET_XATTR:
1188 r = glob_item(i, path_set_xattrs, true);
ebf4e801
MW
1189 if (r < 0)
1190 return r;
e73a03e0 1191 break;
f8eeeaf9
ZJS
1192
1193 case SET_ACL:
b705ab6a 1194 r = glob_item(i, path_set_acls, false);
f8eeeaf9
ZJS
1195 if (r < 0)
1196 return r;
b705ab6a
ZJS
1197 break;
1198
1199 case RECURSIVE_SET_ACL:
1200 r = glob_item(i, path_set_acls, true);
1201 if (r < 0)
1202 return r;
1203 break;
3b63d2d3
LP
1204 }
1205
3b63d2d3
LP
1206 log_debug("%s created successfully.", i->path);
1207
f05bc3f7 1208 return 0;
3b63d2d3
LP
1209}
1210
a0896123 1211static int remove_item_instance(Item *i, const char *instance) {
3b63d2d3
LP
1212 int r;
1213
1214 assert(i);
1215
1216 switch (i->type) {
1217
3b63d2d3 1218 case REMOVE_PATH:
4a62c710 1219 if (remove(instance) < 0 && errno != ENOENT)
3f93da98 1220 return log_error_errno(errno, "rm(%s): %m", instance);
3b63d2d3
LP
1221
1222 break;
1223
1224 case TRUNCATE_DIRECTORY:
1225 case RECURSIVE_REMOVE_PATH:
d139b24a
LP
1226 /* FIXME: we probably should use dir_cleanup() here
1227 * instead of rm_rf() so that 'x' is honoured. */
582deb84 1228 log_debug("rm -rf \"%s\"", instance);
f56d5db9 1229 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
f647962d
MS
1230 if (r < 0 && r != -ENOENT)
1231 return log_error_errno(r, "rm_rf(%s): %m", instance);
3b63d2d3
LP
1232
1233 break;
7fcb4b9b
ZJS
1234
1235 default:
1236 assert_not_reached("wut?");
3b63d2d3
LP
1237 }
1238
1239 return 0;
1240}
1241
a0896123 1242static int remove_item(Item *i) {
99e68c0b
MS
1243 int r = 0;
1244
b8bb3e8f
LP
1245 assert(i);
1246
582deb84
ZJS
1247 log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
1248
b8bb3e8f
LP
1249 switch (i->type) {
1250
1251 case CREATE_FILE:
1252 case TRUNCATE_FILE:
1253 case CREATE_DIRECTORY:
d7b8eec7 1254 case CREATE_SUBVOLUME:
ee17ee7c 1255 case CREATE_FIFO:
468d726b
LP
1256 case CREATE_SYMLINK:
1257 case CREATE_CHAR_DEVICE:
1258 case CREATE_BLOCK_DEVICE:
b8bb3e8f 1259 case IGNORE_PATH:
78a92a5a 1260 case IGNORE_DIRECTORY_PATH:
e73a03e0 1261 case ADJUST_MODE:
777b87e7 1262 case RELABEL_PATH:
a8d88783 1263 case RECURSIVE_RELABEL_PATH:
31ed59c5 1264 case WRITE_FILE:
849958d1 1265 case COPY_FILES:
ebf4e801 1266 case SET_XATTR:
b705ab6a 1267 case RECURSIVE_SET_XATTR:
f8eeeaf9 1268 case SET_ACL:
b705ab6a 1269 case RECURSIVE_SET_ACL:
b8bb3e8f
LP
1270 break;
1271
1272 case REMOVE_PATH:
1273 case TRUNCATE_DIRECTORY:
99e68c0b 1274 case RECURSIVE_REMOVE_PATH:
081043cf 1275 r = glob_item(i, remove_item_instance, false);
99e68c0b 1276 break;
b8bb3e8f
LP
1277 }
1278
99e68c0b 1279 return r;
b8bb3e8f
LP
1280}
1281
78a92a5a 1282static int clean_item_instance(Item *i, const char* instance) {
7fd1b19b 1283 _cleanup_closedir_ DIR *d = NULL;
78a92a5a
MS
1284 struct stat s, ps;
1285 bool mountpoint;
78a92a5a 1286 usec_t cutoff, n;
582deb84 1287 char timestamp[FORMAT_TIMESTAMP_MAX];
78a92a5a
MS
1288
1289 assert(i);
1290
1291 if (!i->age_set)
1292 return 0;
1293
1294 n = now(CLOCK_REALTIME);
1295 if (n < i->age)
1296 return 0;
1297
1298 cutoff = n - i->age;
1299
df99a9ef 1300 d = opendir_nomod(instance);
78a92a5a 1301 if (!d) {
582deb84
ZJS
1302 if (errno == ENOENT || errno == ENOTDIR) {
1303 log_debug_errno(errno, "Directory \"%s\": %m", instance);
78a92a5a 1304 return 0;
582deb84 1305 }
78a92a5a 1306
582deb84 1307 log_error_errno(errno, "Failed to open directory %s: %m", instance);
78a92a5a
MS
1308 return -errno;
1309 }
1310
4a62c710
MS
1311 if (fstat(dirfd(d), &s) < 0)
1312 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
78a92a5a
MS
1313
1314 if (!S_ISDIR(s.st_mode)) {
1315 log_error("%s is not a directory.", i->path);
19fbec19 1316 return -ENOTDIR;
78a92a5a
MS
1317 }
1318
4a62c710
MS
1319 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
1320 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
78a92a5a
MS
1321
1322 mountpoint = s.st_dev != ps.st_dev ||
1323 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
1324
582deb84
ZJS
1325 log_debug("Cleanup threshold for %s \"%s\" is %s",
1326 mountpoint ? "mount point" : "directory",
1327 instance,
1328 format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
1329
1330 return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
1331 MAX_DEPTH, i->keep_first_level);
78a92a5a
MS
1332}
1333
1334static int clean_item(Item *i) {
1335 int r = 0;
1336
1337 assert(i);
1338
582deb84
ZJS
1339 log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
1340
78a92a5a
MS
1341 switch (i->type) {
1342 case CREATE_DIRECTORY:
d7b8eec7 1343 case CREATE_SUBVOLUME:
78a92a5a
MS
1344 case TRUNCATE_DIRECTORY:
1345 case IGNORE_PATH:
849958d1 1346 case COPY_FILES:
78a92a5a
MS
1347 clean_item_instance(i, i->path);
1348 break;
1349 case IGNORE_DIRECTORY_PATH:
081043cf 1350 r = glob_item(i, clean_item_instance, false);
78a92a5a
MS
1351 break;
1352 default:
1353 break;
1354 }
1355
1356 return r;
1357}
1358
3f93da98
ZJS
1359static int process_item_array(ItemArray *array);
1360
3b63d2d3 1361static int process_item(Item *i) {
1e95893a 1362 int r, q, p, t = 0;
9348f0e6 1363 _cleanup_free_ char *prefix = NULL;
3b63d2d3
LP
1364
1365 assert(i);
1366
1910cd0e
LP
1367 if (i->done)
1368 return 0;
1369
1370 i->done = true;
1371
9348f0e6
ZJS
1372 prefix = malloc(strlen(i->path) + 1);
1373 if (!prefix)
1374 return log_oom();
1375
1910cd0e 1376 PATH_FOREACH_PREFIX(prefix, i->path) {
3f93da98 1377 ItemArray *j;
1910cd0e
LP
1378
1379 j = hashmap_get(items, prefix);
1db50423
ZJS
1380 if (j) {
1381 int s;
1382
3f93da98 1383 s = process_item_array(j);
1db50423
ZJS
1384 if (s < 0 && t == 0)
1385 t = s;
1386 }
1910cd0e
LP
1387 }
1388
3b63d2d3 1389 r = arg_create ? create_item(i) : 0;
a0896123 1390 q = arg_remove ? remove_item(i) : 0;
3b63d2d3
LP
1391 p = arg_clean ? clean_item(i) : 0;
1392
1db50423
ZJS
1393 return t < 0 ? t :
1394 r < 0 ? r :
1395 q < 0 ? q :
1396 p;
3b63d2d3
LP
1397}
1398
3f93da98
ZJS
1399static int process_item_array(ItemArray *array) {
1400 unsigned n;
1401 int r = 0, k;
753615e8 1402
3f93da98
ZJS
1403 assert(array);
1404
1405 for (n = 0; n < array->count; n++) {
1406 k = process_item(array->items + n);
1407 if (k < 0 && r == 0)
1408 r = k;
1409 }
1410
1411 return r;
1412}
3b63d2d3 1413
3f93da98
ZJS
1414static void item_free_contents(Item *i) {
1415 assert(i);
3b63d2d3 1416 free(i->path);
468d726b 1417 free(i->argument);
ebf4e801 1418 strv_free(i->xattrs);
f8eeeaf9
ZJS
1419
1420#ifdef HAVE_ACL
1421 acl_free(i->acl_access);
1422 acl_free(i->acl_default);
1423#endif
3b63d2d3
LP
1424}
1425
3f93da98
ZJS
1426static void item_array_free(ItemArray *a) {
1427 unsigned n;
1428
1429 if (!a)
1430 return;
1431
1432 for (n = 0; n < a->count; n++)
1433 item_free_contents(a->items + n);
1434 free(a->items);
1435 free(a);
1436}
e2f2fb78 1437
3f93da98 1438static bool item_compatible(Item *a, Item *b) {
bfe95f35
LP
1439 assert(a);
1440 assert(b);
3f93da98 1441 assert(streq(a->path, b->path));
bfe95f35 1442
3f93da98
ZJS
1443 if (takes_ownership(a->type) && takes_ownership(b->type))
1444 /* check if the items are the same */
1445 return streq_ptr(a->argument, b->argument) &&
bfe95f35 1446
3f93da98
ZJS
1447 a->uid_set == b->uid_set &&
1448 a->uid == b->uid &&
bfe95f35 1449
3f93da98
ZJS
1450 a->gid_set == b->gid_set &&
1451 a->gid == b->gid &&
bfe95f35 1452
3f93da98
ZJS
1453 a->mode_set == b->mode_set &&
1454 a->mode == b->mode &&
bfe95f35 1455
3f93da98
ZJS
1456 a->age_set == b->age_set &&
1457 a->age == b->age &&
bfe95f35 1458
3f93da98 1459 a->mask_perms == b->mask_perms &&
bfe95f35 1460
3f93da98 1461 a->keep_first_level == b->keep_first_level &&
468d726b 1462
3f93da98 1463 a->major_minor == b->major_minor;
468d726b 1464
bfe95f35
LP
1465 return true;
1466}
1467
a2aced4a
DR
1468static bool should_include_path(const char *path) {
1469 char **prefix;
1470
abef3f91 1471 STRV_FOREACH(prefix, arg_exclude_prefixes)
582deb84
ZJS
1472 if (path_startswith(path, *prefix)) {
1473 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1474 path, *prefix);
5c795114 1475 return false;
582deb84 1476 }
a2aced4a 1477
abef3f91 1478 STRV_FOREACH(prefix, arg_include_prefixes)
582deb84
ZJS
1479 if (path_startswith(path, *prefix)) {
1480 log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
a2aced4a 1481 return true;
582deb84 1482 }
a2aced4a 1483
5c795114
DR
1484 /* no matches, so we should include this path only if we
1485 * have no whitelist at all */
582deb84
ZJS
1486 if (strv_length(arg_include_prefixes) == 0)
1487 return true;
1488
1489 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
1490 return false;
a2aced4a
DR
1491}
1492
fba6e687 1493static int parse_line(const char *fname, unsigned line, const char *buffer) {
1731e34a
LP
1494
1495 static const Specifier specifier_table[] = {
1496 { 'm', specifier_machine_id, NULL },
1497 { 'b', specifier_boot_id, NULL },
1498 { 'H', specifier_host_name, NULL },
1499 { 'v', specifier_kernel_release, NULL },
1500 {}
1501 };
1502
cde684a2 1503 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
3f93da98
ZJS
1504 _cleanup_(item_free_contents) Item i = {};
1505 ItemArray *existing;
bfe95f35 1506 Hashmap *h;
3f93da98 1507 int r, c = -1, pos;
5f255144 1508 bool force = false, boot = false;
3b63d2d3
LP
1509
1510 assert(fname);
1511 assert(line >= 1);
1512 assert(buffer);
1513
19fbec19 1514 r = sscanf(buffer,
c4708f13
ZJS
1515 "%ms %ms %ms %ms %ms %ms %n",
1516 &action,
1731e34a 1517 &path,
bd40a2d8
LP
1518 &mode,
1519 &user,
1520 &group,
468d726b 1521 &age,
3f93da98 1522 &c);
19fbec19 1523 if (r < 2) {
3b63d2d3 1524 log_error("[%s:%u] Syntax error.", fname, line);
7f2c1f4d 1525 return -EIO;
5008d581
LP
1526 }
1527
2e78fa79
LP
1528 if (isempty(action)) {
1529 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
c4708f13 1530 return -EINVAL;
2e78fa79
LP
1531 }
1532
5f255144
ZJS
1533 for (pos = 1; action[pos]; pos++) {
1534 if (action[pos] == '!' && !boot)
1535 boot = true;
1536 else if (action[pos] == '+' && !force)
1537 force = true;
1538 else {
1539 log_error("[%s:%u] Unknown modifiers in command '%s'",
1540 fname, line, action);
1541 return -EINVAL;
1542 }
2e78fa79
LP
1543 }
1544
582deb84
ZJS
1545 if (boot && !arg_boot) {
1546 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1547 action, path);
c4708f13 1548 return 0;
582deb84 1549 }
c4708f13 1550
3f93da98
ZJS
1551 i.type = action[0];
1552 i.force = force;
2e78fa79 1553
3f93da98 1554 r = specifier_printf(path, specifier_table, NULL, &i.path);
1731e34a
LP
1555 if (r < 0) {
1556 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1557 return r;
1558 }
1559
3f93da98
ZJS
1560 if (c >= 0) {
1561 c += strspn(buffer+c, WHITESPACE);
1562 if (buffer[c] != 0 && (buffer[c] != '-' || buffer[c+1] != 0)) {
1563 i.argument = unquote(buffer+c, "\"");
1564 if (!i.argument)
0d0f0c50 1565 return log_oom();
31ed59c5
LP
1566 }
1567 }
1568
3f93da98 1569 switch (i.type) {
468d726b 1570
777b87e7
MS
1571 case CREATE_FILE:
1572 case TRUNCATE_FILE:
1573 case CREATE_DIRECTORY:
d7b8eec7 1574 case CREATE_SUBVOLUME:
777b87e7
MS
1575 case TRUNCATE_DIRECTORY:
1576 case CREATE_FIFO:
1577 case IGNORE_PATH:
78a92a5a 1578 case IGNORE_DIRECTORY_PATH:
777b87e7
MS
1579 case REMOVE_PATH:
1580 case RECURSIVE_REMOVE_PATH:
e73a03e0 1581 case ADJUST_MODE:
777b87e7
MS
1582 case RELABEL_PATH:
1583 case RECURSIVE_RELABEL_PATH:
1584 break;
468d726b
LP
1585
1586 case CREATE_SYMLINK:
3f93da98
ZJS
1587 if (!i.argument) {
1588 i.argument = strappend("/usr/share/factory/", i.path);
1589 if (!i.argument)
2f3b873a 1590 return log_oom();
468d726b
LP
1591 }
1592 break;
1593
31ed59c5 1594 case WRITE_FILE:
3f93da98 1595 if (!i.argument) {
31ed59c5 1596 log_error("[%s:%u] Write file requires argument.", fname, line);
7f2c1f4d 1597 return -EBADMSG;
31ed59c5
LP
1598 }
1599 break;
1600
849958d1 1601 case COPY_FILES:
3f93da98
ZJS
1602 if (!i.argument) {
1603 i.argument = strappend("/usr/share/factory/", i.path);
1604 if (!i.argument)
2f3b873a 1605 return log_oom();
3f93da98 1606 } else if (!path_is_absolute(i.argument)) {
849958d1
LP
1607 log_error("[%s:%u] Source path is not absolute.", fname, line);
1608 return -EBADMSG;
1609 }
1610
3f93da98 1611 path_kill_slashes(i.argument);
849958d1
LP
1612 break;
1613
468d726b
LP
1614 case CREATE_CHAR_DEVICE:
1615 case CREATE_BLOCK_DEVICE: {
1616 unsigned major, minor;
1617
3f93da98 1618 if (!i.argument) {
468d726b 1619 log_error("[%s:%u] Device file requires argument.", fname, line);
7f2c1f4d 1620 return -EBADMSG;
468d726b
LP
1621 }
1622
3f93da98
ZJS
1623 if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
1624 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
7f2c1f4d 1625 return -EBADMSG;
468d726b
LP
1626 }
1627
3f93da98 1628 i.major_minor = makedev(major, minor);
468d726b
LP
1629 break;
1630 }
1631
ebf4e801 1632 case SET_XATTR:
b705ab6a 1633 case RECURSIVE_SET_XATTR:
3f93da98 1634 if (!i.argument) {
ebf4e801
MW
1635 log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
1636 return -EBADMSG;
1637 }
3f93da98 1638 r = get_xattrs_from_arg(&i);
ebf4e801
MW
1639 if (r < 0)
1640 return r;
1641 break;
1642
f8eeeaf9 1643 case SET_ACL:
b705ab6a 1644 case RECURSIVE_SET_ACL:
f8eeeaf9
ZJS
1645 if (!i.argument) {
1646 log_error("[%s:%u] Set ACLs requires argument.", fname, line);
1647 return -EBADMSG;
1648 }
1649 r = get_acls_from_arg(&i);
1650 if (r < 0)
1651 return r;
1652 break;
1653
777b87e7 1654 default:
582deb84 1655 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
7f2c1f4d 1656 return -EBADMSG;
3b63d2d3 1657 }
468d726b 1658
3f93da98
ZJS
1659 if (!path_is_absolute(i.path)) {
1660 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
7f2c1f4d 1661 return -EBADMSG;
3b63d2d3
LP
1662 }
1663
3f93da98 1664 path_kill_slashes(i.path);
3b63d2d3 1665
3f93da98 1666 if (!should_include_path(i.path))
7f2c1f4d 1667 return 0;
5008d581 1668
cf9a4abd 1669 if (arg_root) {
cde684a2
LP
1670 char *p;
1671
3f93da98 1672 p = strappend(arg_root, i.path);
cf9a4abd
MM
1673 if (!p)
1674 return log_oom();
1675
3f93da98
ZJS
1676 free(i.path);
1677 i.path = p;
cf9a4abd
MM
1678 }
1679
3b63d2d3 1680 if (user && !streq(user, "-")) {
4b67834e
LP
1681 const char *u = user;
1682
3f93da98 1683 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
4b67834e 1684 if (r < 0) {
3b63d2d3 1685 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
7f2c1f4d 1686 return r;
3b63d2d3
LP
1687 }
1688
3f93da98 1689 i.uid_set = true;
3b63d2d3
LP
1690 }
1691
1692 if (group && !streq(group, "-")) {
4b67834e
LP
1693 const char *g = group;
1694
3f93da98 1695 r = get_group_creds(&g, &i.gid);
4b67834e 1696 if (r < 0) {
3b63d2d3 1697 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
7f2c1f4d 1698 return r;
3b63d2d3
LP
1699 }
1700
3f93da98 1701 i.gid_set = true;
3b63d2d3
LP
1702 }
1703
1704 if (mode && !streq(mode, "-")) {
abef3f91 1705 const char *mm = mode;
3b63d2d3
LP
1706 unsigned m;
1707
abef3f91 1708 if (*mm == '~') {
3f93da98 1709 i.mask_perms = true;
abef3f91
LP
1710 mm++;
1711 }
1712
1713 if (sscanf(mm, "%o", &m) != 1) {
3b63d2d3 1714 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
7f2c1f4d 1715 return -ENOENT;
3b63d2d3
LP
1716 }
1717
3f93da98
ZJS
1718 i.mode = m;
1719 i.mode_set = true;
3b63d2d3 1720 } else
3f93da98
ZJS
1721 i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
1722 ? 0755 : 0644;
3b63d2d3
LP
1723
1724 if (age && !streq(age, "-")) {
24f3a374
LP
1725 const char *a = age;
1726
1727 if (*a == '~') {
3f93da98 1728 i.keep_first_level = true;
24f3a374
LP
1729 a++;
1730 }
1731
3f93da98 1732 if (parse_sec(a, &i.age) < 0) {
3b63d2d3 1733 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
7f2c1f4d 1734 return -EBADMSG;
3b63d2d3
LP
1735 }
1736
3f93da98 1737 i.age_set = true;
3b63d2d3
LP
1738 }
1739
3f93da98 1740 h = needs_glob(i.type) ? globs : items;
bfe95f35 1741
3f93da98 1742 existing = hashmap_get(h, i.path);
468d726b 1743 if (existing) {
3f93da98
ZJS
1744 unsigned n;
1745
1746 for (n = 0; n < existing->count; n++) {
1747 if (!item_compatible(existing->items + n, &i))
505ef0e3 1748 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
3f93da98 1749 fname, line, i.path);
ebf4e801
MW
1750 }
1751 } else {
3f93da98
ZJS
1752 existing = new0(ItemArray, 1);
1753 r = hashmap_put(h, i.path, existing);
1754 if (r < 0)
1755 return log_oom();
bfe95f35
LP
1756 }
1757
3f93da98
ZJS
1758 if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
1759 return log_oom();
5008d581 1760
3f93da98
ZJS
1761 memcpy(existing->items + existing->count++, &i, sizeof(i));
1762 zero(i);
7f2c1f4d 1763 return 0;
5008d581
LP
1764}
1765
601185b4 1766static void help(void) {
522d4a49
LP
1767 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1768 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
5c795114 1769 " -h --help Show this help\n"
eb9da376 1770 " --version Show package version\n"
5c795114
DR
1771 " --create Create marked files/directories\n"
1772 " --clean Clean up marked directories\n"
1773 " --remove Remove marked files/directories\n"
81815651 1774 " --boot Execute actions only safe at boot\n"
79ca888f
ZJS
1775 " --prefix=PATH Only apply rules with the specified prefix\n"
1776 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
cf9a4abd 1777 " --root=PATH Operate on an alternate filesystem root\n",
3b63d2d3 1778 program_invocation_short_name);
3b63d2d3
LP
1779}
1780
1781static int parse_argv(int argc, char *argv[]) {
1782
1783 enum {
eb9da376 1784 ARG_VERSION = 0x100,
3b63d2d3
LP
1785 ARG_CREATE,
1786 ARG_CLEAN,
fba6e687 1787 ARG_REMOVE,
81815651 1788 ARG_BOOT,
5c795114
DR
1789 ARG_PREFIX,
1790 ARG_EXCLUDE_PREFIX,
cf9a4abd 1791 ARG_ROOT,
3b63d2d3
LP
1792 };
1793
1794 static const struct option options[] = {
5c795114 1795 { "help", no_argument, NULL, 'h' },
eb9da376 1796 { "version", no_argument, NULL, ARG_VERSION },
5c795114
DR
1797 { "create", no_argument, NULL, ARG_CREATE },
1798 { "clean", no_argument, NULL, ARG_CLEAN },
1799 { "remove", no_argument, NULL, ARG_REMOVE },
81815651 1800 { "boot", no_argument, NULL, ARG_BOOT },
5c795114
DR
1801 { "prefix", required_argument, NULL, ARG_PREFIX },
1802 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
cf9a4abd 1803 { "root", required_argument, NULL, ARG_ROOT },
eb9da376 1804 {}
3b63d2d3
LP
1805 };
1806
1807 int c;
1808
1809 assert(argc >= 0);
1810 assert(argv);
1811
601185b4 1812 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
3b63d2d3
LP
1813
1814 switch (c) {
1815
1816 case 'h':
601185b4
ZJS
1817 help();
1818 return 0;
eb9da376
LP
1819
1820 case ARG_VERSION:
1821 puts(PACKAGE_STRING);
1822 puts(SYSTEMD_FEATURES);
3b63d2d3
LP
1823 return 0;
1824
1825 case ARG_CREATE:
1826 arg_create = true;
1827 break;
1828
1829 case ARG_CLEAN:
1830 arg_clean = true;
1831 break;
1832
1833 case ARG_REMOVE:
1834 arg_remove = true;
1835 break;
1836
81815651
ZJS
1837 case ARG_BOOT:
1838 arg_boot = true;
c4708f13
ZJS
1839 break;
1840
fba6e687 1841 case ARG_PREFIX:
7bc040fa 1842 if (strv_push(&arg_include_prefixes, optarg) < 0)
a2aced4a 1843 return log_oom();
fba6e687
LP
1844 break;
1845
5c795114 1846 case ARG_EXCLUDE_PREFIX:
7bc040fa 1847 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
5c795114
DR
1848 return log_oom();
1849 break;
1850
cf9a4abd 1851 case ARG_ROOT:
753615e8 1852 free(arg_root);
cf9a4abd
MM
1853 arg_root = path_make_absolute_cwd(optarg);
1854 if (!arg_root)
1855 return log_oom();
753615e8 1856
cf9a4abd
MM
1857 path_kill_slashes(arg_root);
1858 break;
1859
3b63d2d3
LP
1860 case '?':
1861 return -EINVAL;
1862
1863 default:
eb9da376 1864 assert_not_reached("Unhandled option");
3b63d2d3 1865 }
3b63d2d3
LP
1866
1867 if (!arg_clean && !arg_create && !arg_remove) {
35b8ca3a 1868 log_error("You need to specify at least one of --clean, --create or --remove.");
3b63d2d3
LP
1869 return -EINVAL;
1870 }
1871
1872 return 1;
1873}
1874
fba6e687 1875static int read_config_file(const char *fn, bool ignore_enoent) {
1731e34a
LP
1876 _cleanup_fclose_ FILE *f = NULL;
1877 char line[LINE_MAX];
78a92a5a 1878 Iterator iterator;
1731e34a 1879 unsigned v = 0;
78a92a5a 1880 Item *i;
1731e34a 1881 int r;
fba6e687
LP
1882
1883 assert(fn);
1884
cf9a4abd 1885 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
fabe5c0e 1886 if (r < 0) {
582deb84
ZJS
1887 if (ignore_enoent && r == -ENOENT) {
1888 log_debug_errno(r, "Failed to open \"%s\": %m", fn);
fba6e687 1889 return 0;
582deb84 1890 }
fba6e687 1891
8d3d7072 1892 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
fba6e687 1893 }
582deb84 1894 log_debug("Reading config file \"%s\".", fn);
fba6e687 1895
1731e34a
LP
1896 FOREACH_LINE(line, f, break) {
1897 char *l;
fba6e687
LP
1898 int k;
1899
fba6e687
LP
1900 v++;
1901
1902 l = strstrip(line);
1903 if (*l == '#' || *l == 0)
1904 continue;
1905
1731e34a
LP
1906 k = parse_line(fn, v, l);
1907 if (k < 0 && r == 0)
1908 r = k;
fba6e687
LP
1909 }
1910
78a92a5a
MS
1911 /* we have to determine age parameter for each entry of type X */
1912 HASHMAP_FOREACH(i, globs, iterator) {
1913 Iterator iter;
1914 Item *j, *candidate_item = NULL;
1915
1916 if (i->type != IGNORE_DIRECTORY_PATH)
1917 continue;
1918
1919 HASHMAP_FOREACH(j, items, iter) {
d7b8eec7 1920 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
78a92a5a
MS
1921 continue;
1922
1923 if (path_equal(j->path, i->path)) {
1924 candidate_item = j;
1925 break;
1926 }
1927
1928 if ((!candidate_item && path_startswith(i->path, j->path)) ||
1929 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
1930 candidate_item = j;
1931 }
1932
9ed2a35e 1933 if (candidate_item && candidate_item->age_set) {
78a92a5a
MS
1934 i->age = candidate_item->age;
1935 i->age_set = true;
1936 }
1937 }
1938
fba6e687 1939 if (ferror(f)) {
56f64d95 1940 log_error_errno(errno, "Failed to read from file %s: %m", fn);
fba6e687
LP
1941 if (r == 0)
1942 r = -EIO;
1943 }
1944
fba6e687
LP
1945 return r;
1946}
1947
5008d581 1948int main(int argc, char *argv[]) {
fabe5c0e 1949 int r, k;
3f93da98 1950 ItemArray *a;
3b63d2d3
LP
1951 Iterator iterator;
1952
fdcad0c2
LP
1953 r = parse_argv(argc, argv);
1954 if (r <= 0)
753615e8 1955 goto finish;
5008d581 1956
eb0ca9eb 1957 log_set_target(LOG_TARGET_AUTO);
5008d581
LP
1958 log_parse_environment();
1959 log_open();
1960
4c12626c
LP
1961 umask(0022);
1962
cc56fafe 1963 mac_selinux_init(NULL);
5008d581 1964
d5099efc
MS
1965 items = hashmap_new(&string_hash_ops);
1966 globs = hashmap_new(&string_hash_ops);
b8bb3e8f
LP
1967
1968 if (!items || !globs) {
fabe5c0e 1969 r = log_oom();
3b63d2d3
LP
1970 goto finish;
1971 }
1972
fabe5c0e 1973 r = 0;
5008d581 1974
fba6e687
LP
1975 if (optind < argc) {
1976 int j;
5008d581 1977
9125670f 1978 for (j = optind; j < argc; j++) {
fabe5c0e
LP
1979 k = read_config_file(argv[j], false);
1980 if (k < 0 && r == 0)
1981 r = k;
9125670f 1982 }
5008d581 1983
fba6e687 1984 } else {
fabe5c0e
LP
1985 _cleanup_strv_free_ char **files = NULL;
1986 char **f;
5008d581 1987
cf9a4abd 1988 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
44143309 1989 if (r < 0) {
da927ba9 1990 log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
44143309
KS
1991 goto finish;
1992 }
3b63d2d3 1993
772f8371 1994 STRV_FOREACH(f, files) {
fabe5c0e
LP
1995 k = read_config_file(*f, true);
1996 if (k < 0 && r == 0)
1997 r = k;
5008d581 1998 }
772f8371 1999 }
5008d581 2000
3f93da98
ZJS
2001 HASHMAP_FOREACH(a, globs, iterator) {
2002 k = process_item_array(a);
1db50423
ZJS
2003 if (k < 0 && r == 0)
2004 r = k;
2005 }
b8bb3e8f 2006
3f93da98
ZJS
2007 HASHMAP_FOREACH(a, items, iterator) {
2008 k = process_item_array(a);
1db50423
ZJS
2009 if (k < 0 && r == 0)
2010 r = k;
2011 }
3b63d2d3 2012
5008d581 2013finish:
3f93da98
ZJS
2014 while ((a = hashmap_steal_first(items)))
2015 item_array_free(a);
3b63d2d3 2016
3f93da98
ZJS
2017 while ((a = hashmap_steal_first(globs)))
2018 item_array_free(a);
17b90525 2019
3b63d2d3 2020 hashmap_free(items);
b8bb3e8f 2021 hashmap_free(globs);
5008d581 2022
7bc040fa
LP
2023 free(arg_include_prefixes);
2024 free(arg_exclude_prefixes);
cf9a4abd 2025 free(arg_root);
a2aced4a 2026
17b90525
LP
2027 set_free_free(unix_sockets);
2028
cc56fafe 2029 mac_selinux_finish();
29003cff 2030
fabe5c0e 2031 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
5008d581 2032}