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