]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tmpfiles/tmpfiles.c
readahead: don't complain that we cannot precache symlinks
[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"
49e942b2 46#include "mkdir.h"
9eb977db 47#include "path-util.h"
5008d581
LP
48#include "strv.h"
49#include "label.h"
3b63d2d3 50#include "set.h"
2c21044f 51#include "conf-files.h"
cb7ed9df 52#include "capability.h"
5008d581 53
01000479 54/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
5008d581 55 * them in the file system. This is intended to be used to create
db019b8d
KS
56 * properly owned directories beneath /tmp, /var/tmp, /run, which are
57 * volatile and hence need to be recreated on bootup. */
5008d581 58
66ccd038 59typedef enum ItemType {
b8bb3e8f 60 /* These ones take file names */
3b63d2d3
LP
61 CREATE_FILE = 'f',
62 TRUNCATE_FILE = 'F',
31ed59c5 63 WRITE_FILE = 'w',
3b63d2d3
LP
64 CREATE_DIRECTORY = 'd',
65 TRUNCATE_DIRECTORY = 'D',
ee17ee7c 66 CREATE_FIFO = 'p',
468d726b
LP
67 CREATE_SYMLINK = 'L',
68 CREATE_CHAR_DEVICE = 'c',
69 CREATE_BLOCK_DEVICE = 'b',
b8bb3e8f
LP
70
71 /* These ones take globs */
3b63d2d3
LP
72 IGNORE_PATH = 'x',
73 REMOVE_PATH = 'r',
a8d88783 74 RECURSIVE_REMOVE_PATH = 'R',
777b87e7 75 RELABEL_PATH = 'z',
a8d88783 76 RECURSIVE_RELABEL_PATH = 'Z'
66ccd038 77} ItemType;
3b63d2d3
LP
78
79typedef struct Item {
66ccd038 80 ItemType type;
3b63d2d3
LP
81
82 char *path;
468d726b 83 char *argument;
5008d581
LP
84 uid_t uid;
85 gid_t gid;
3b63d2d3
LP
86 mode_t mode;
87 usec_t age;
88
468d726b
LP
89 dev_t major_minor;
90
3b63d2d3
LP
91 bool uid_set:1;
92 bool gid_set:1;
93 bool mode_set:1;
94 bool age_set:1;
24f3a374
LP
95
96 bool keep_first_level:1;
3b63d2d3
LP
97} Item;
98
b8bb3e8f 99static Hashmap *items = NULL, *globs = NULL;
17b90525 100static Set *unix_sockets = NULL;
3b63d2d3
LP
101
102static bool arg_create = false;
103static bool arg_clean = false;
104static bool arg_remove = false;
105
fba6e687
LP
106static const char *arg_prefix = NULL;
107
24f3a374 108static const char * const conf_file_dirs[] = {
9125670f
DR
109 "/etc/tmpfiles.d",
110 "/run/tmpfiles.d",
111 "/usr/local/lib/tmpfiles.d",
112 "/usr/lib/tmpfiles.d",
3f2afb29
LP
113#ifdef HAVE_SPLIT_USR
114 "/lib/tmpfiles.d",
115#endif
9125670f
DR
116 NULL
117};
118
3b63d2d3
LP
119#define MAX_DEPTH 256
120
66ccd038 121static bool needs_glob(ItemType t) {
777b87e7 122 return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
b8bb3e8f
LP
123}
124
125static struct Item* find_glob(Hashmap *h, const char *match) {
126 Item *j;
127 Iterator i;
128
129 HASHMAP_FOREACH(j, h, i)
130 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
131 return j;
132
133 return NULL;
134}
135
17b90525
LP
136static void load_unix_sockets(void) {
137 FILE *f = NULL;
138 char line[LINE_MAX];
139
140 if (unix_sockets)
141 return;
142
143 /* We maintain a cache of the sockets we found in
144 * /proc/net/unix to speed things up a little. */
145
fdcad0c2
LP
146 unix_sockets = set_new(string_hash_func, string_compare_func);
147 if (!unix_sockets)
17b90525
LP
148 return;
149
fdcad0c2
LP
150 f = fopen("/proc/net/unix", "re");
151 if (!f)
17b90525
LP
152 return;
153
fdcad0c2
LP
154 /* Skip header */
155 if (!fgets(line, sizeof(line), f))
17b90525
LP
156 goto fail;
157
158 for (;;) {
159 char *p, *s;
160 int k;
161
fdcad0c2 162 if (!fgets(line, sizeof(line), f))
17b90525
LP
163 break;
164
165 truncate_nl(line);
166
fdcad0c2
LP
167 p = strchr(line, ':');
168 if (!p)
169 continue;
170
171 if (strlen(p) < 37)
17b90525
LP
172 continue;
173
fdcad0c2 174 p += 37;
17b90525 175 p += strspn(p, WHITESPACE);
fdcad0c2 176 p += strcspn(p, WHITESPACE); /* skip one more word */
17b90525
LP
177 p += strspn(p, WHITESPACE);
178
179 if (*p != '/')
180 continue;
181
fdcad0c2
LP
182 s = strdup(p);
183 if (!s)
17b90525
LP
184 goto fail;
185
4ff21d85
LP
186 path_kill_slashes(s);
187
fdcad0c2
LP
188 k = set_put(unix_sockets, s);
189 if (k < 0) {
17b90525
LP
190 free(s);
191
192 if (k != -EEXIST)
193 goto fail;
194 }
195 }
196
10d975f5 197 fclose(f);
17b90525
LP
198 return;
199
200fail:
201 set_free_free(unix_sockets);
202 unix_sockets = NULL;
203
204 if (f)
205 fclose(f);
206}
207
208static bool unix_socket_alive(const char *fn) {
209 assert(fn);
210
211 load_unix_sockets();
212
213 if (unix_sockets)
214 return !!set_get(unix_sockets, (char*) fn);
215
216 /* We don't know, so assume yes */
217 return true;
218}
219
3b63d2d3
LP
220static int dir_cleanup(
221 const char *p,
222 DIR *d,
223 const struct stat *ds,
224 usec_t cutoff,
225 dev_t rootdev,
226 bool mountpoint,
24f3a374
LP
227 int maxdepth,
228 bool keep_this_level)
3b63d2d3
LP
229{
230 struct dirent *dent;
231 struct timespec times[2];
232 bool deleted = false;
233 char *sub_path = NULL;
234 int r = 0;
235
236 while ((dent = readdir(d))) {
237 struct stat s;
238 usec_t age;
239
240 if (streq(dent->d_name, ".") ||
241 streq(dent->d_name, ".."))
242 continue;
5008d581 243
3b63d2d3 244 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
5008d581 245
3b63d2d3
LP
246 if (errno != ENOENT) {
247 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
248 r = -errno;
249 }
250
251 continue;
252 }
253
254 /* Stay on the same filesystem */
255 if (s.st_dev != rootdev)
256 continue;
257
258 /* Do not delete read-only files owned by root */
259 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
260 continue;
261
262 free(sub_path);
263 sub_path = NULL;
264
265 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
0d0f0c50 266 r = log_oom();
3b63d2d3
LP
267 goto finish;
268 }
269
270 /* Is there an item configured for this path? */
271 if (hashmap_get(items, sub_path))
272 continue;
273
b8bb3e8f
LP
274 if (find_glob(globs, sub_path))
275 continue;
276
3b63d2d3
LP
277 if (S_ISDIR(s.st_mode)) {
278
279 if (mountpoint &&
280 streq(dent->d_name, "lost+found") &&
281 s.st_uid == 0)
282 continue;
283
284 if (maxdepth <= 0)
285 log_warning("Reached max depth on %s.", sub_path);
286 else {
287 DIR *sub_dir;
288 int q;
289
e5f3d1ba 290 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
3b63d2d3
LP
291 if (sub_dir == NULL) {
292 if (errno != ENOENT) {
293 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
294 r = -errno;
295 }
296
297 continue;
298 }
299
24f3a374 300 q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
3b63d2d3
LP
301 closedir(sub_dir);
302
303 if (q < 0)
304 r = q;
305 }
306
24f3a374
LP
307 /* Note: if you are wondering why we don't
308 * support the sticky bit for excluding
309 * directories from cleaning like we do it for
310 * other file system objects: well, the sticky
311 * bit already has a meaning for directories,
312 * so we don't want to overload that. */
313
314 if (keep_this_level)
315 continue;
316
3b63d2d3
LP
317 /* Ignore ctime, we change it when deleting */
318 age = MAX(timespec_load(&s.st_mtim),
319 timespec_load(&s.st_atim));
320 if (age >= cutoff)
321 continue;
322
323 log_debug("rmdir '%s'\n", sub_path);
324
325 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
326 if (errno != ENOENT && errno != ENOTEMPTY) {
327 log_error("rmdir(%s): %m", sub_path);
328 r = -errno;
329 }
330 }
331
332 } else {
9c73736d
LP
333 /* Skip files for which the sticky bit is
334 * set. These are semantics we define, and are
335 * unknown elsewhere. See XDG_RUNTIME_DIR
336 * specification for details. */
337 if (s.st_mode & S_ISVTX)
338 continue;
339
17b90525 340 if (mountpoint && S_ISREG(s.st_mode)) {
3b63d2d3
LP
341 if (streq(dent->d_name, ".journal") &&
342 s.st_uid == 0)
343 continue;
344
345 if (streq(dent->d_name, "aquota.user") ||
346 streq(dent->d_name, "aquota.group"))
347 continue;
348 }
349
17b90525
LP
350 /* Ignore sockets that are listed in /proc/net/unix */
351 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
352 continue;
353
78ab08eb
LP
354 /* Ignore device nodes */
355 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
356 continue;
357
24f3a374
LP
358 /* Keep files on this level around if this is
359 * requested */
360 if (keep_this_level)
361 continue;
362
3b63d2d3
LP
363 age = MAX3(timespec_load(&s.st_mtim),
364 timespec_load(&s.st_atim),
365 timespec_load(&s.st_ctim));
366
367 if (age >= cutoff)
368 continue;
369
370 log_debug("unlink '%s'\n", sub_path);
371
372 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
373 if (errno != ENOENT) {
374 log_error("unlink(%s): %m", sub_path);
375 r = -errno;
376 }
377 }
378
379 deleted = true;
380 }
381 }
382
383finish:
384 if (deleted) {
385 /* Restore original directory timestamps */
386 times[0] = ds->st_atim;
387 times[1] = ds->st_mtim;
388
389 if (futimens(dirfd(d), times) < 0)
390 log_error("utimensat(%s): %m", p);
391 }
392
393 free(sub_path);
394
395 return r;
396}
397
398static int clean_item(Item *i) {
399 DIR *d;
400 struct stat s, ps;
401 bool mountpoint;
402 int r;
403 usec_t cutoff, n;
404
405 assert(i);
406
407 if (i->type != CREATE_DIRECTORY &&
408 i->type != TRUNCATE_DIRECTORY &&
409 i->type != IGNORE_PATH)
410 return 0;
411
a2558205 412 if (!i->age_set)
3b63d2d3
LP
413 return 0;
414
415 n = now(CLOCK_REALTIME);
416 if (n < i->age)
417 return 0;
418
419 cutoff = n - i->age;
420
421 d = opendir(i->path);
422 if (!d) {
423 if (errno == ENOENT)
424 return 0;
425
426 log_error("Failed to open directory %s: %m", i->path);
427 return -errno;
428 }
429
430 if (fstat(dirfd(d), &s) < 0) {
431 log_error("stat(%s) failed: %m", i->path);
432 r = -errno;
5008d581
LP
433 goto finish;
434 }
435
3b63d2d3
LP
436 if (!S_ISDIR(s.st_mode)) {
437 log_error("%s is not a directory.", i->path);
438 r = -ENOTDIR;
5008d581
LP
439 goto finish;
440 }
441
3b63d2d3
LP
442 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
443 log_error("stat(%s/..) failed: %m", i->path);
444 r = -errno;
5008d581 445 goto finish;
4aa8b15b 446 }
5008d581 447
3b63d2d3
LP
448 mountpoint = s.st_dev != ps.st_dev ||
449 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
5008d581 450
24f3a374 451 r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH, i->keep_first_level);
5008d581 452
3b63d2d3
LP
453finish:
454 if (d)
455 closedir(d);
5008d581 456
3b63d2d3
LP
457 return r;
458}
5008d581 459
062e01bb
MS
460static int item_set_perms(Item *i, const char *path) {
461 /* not using i->path directly because it may be a glob */
462 if (i->mode_set)
463 if (chmod(path, i->mode) < 0) {
464 log_error("chmod(%s) failed: %m", path);
465 return -errno;
466 }
467
468 if (i->uid_set || i->gid_set)
469 if (chown(path,
470 i->uid_set ? i->uid : (uid_t) -1,
471 i->gid_set ? i->gid : (gid_t) -1) < 0) {
472
473 log_error("chown(%s) failed: %m", path);
474 return -errno;
475 }
476
c9bc0764 477 return label_fix(path, false, false);
062e01bb
MS
478}
479
d4e9eb91
DR
480static int write_one_file(Item *i, const char *path) {
481 int r, e, fd, flags;
482 struct stat st;
483 mode_t u;
484
485 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
486 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
487
488 u = umask(0);
489 label_context_set(path, S_IFREG);
490 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
491 e = errno;
492 label_context_clear();
493 umask(u);
494 errno = e;
495
496 if (fd < 0) {
497 if (i->type == WRITE_FILE && errno == ENOENT)
498 return 0;
499
500 log_error("Failed to create file %s: %m", path);
501 return -errno;
502 }
503
504 if (i->argument) {
505 ssize_t n;
506 size_t l;
54693d9b 507 _cleanup_free_ char *unescaped;
d4e9eb91 508
54693d9b
DR
509 unescaped = cunescape(i->argument);
510 if (unescaped == NULL)
511 return log_oom();
d4e9eb91 512
54693d9b
DR
513 l = strlen(unescaped);
514 n = write(fd, unescaped, l);
d4e9eb91 515
d4e9eb91
DR
516 if (n < 0 || (size_t) n < l) {
517 log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
518 close_nointr_nofail(fd);
519 return n < 0 ? n : -EIO;
520 }
521 }
522
3612fbc1
DR
523 close_nointr_nofail(fd);
524
d4e9eb91
DR
525 if (stat(path, &st) < 0) {
526 log_error("stat(%s) failed: %m", path);
527 return -errno;
528 }
529
530 if (!S_ISREG(st.st_mode)) {
531 log_error("%s is not a file.", path);
532 return -EEXIST;
533 }
534
535 r = item_set_perms(i, path);
536 if (r < 0)
537 return r;
538
539 return 0;
540}
541
062e01bb 542static int recursive_relabel_children(Item *i, const char *path) {
a8d88783
MS
543 DIR *d;
544 int ret = 0;
545
546 /* This returns the first error we run into, but nevertheless
547 * tries to go on */
548
549 d = opendir(path);
550 if (!d)
551 return errno == ENOENT ? 0 : -errno;
552
553 for (;;) {
7d5e9c0f
LP
554 struct dirent *de;
555 union dirent_storage buf;
a8d88783
MS
556 bool is_dir;
557 int r;
558 char *entry_path;
559
7d5e9c0f 560 r = readdir_r(d, &buf.de, &de);
a8d88783
MS
561 if (r != 0) {
562 if (ret == 0)
563 ret = -r;
564 break;
565 }
566
567 if (!de)
568 break;
569
570 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
571 continue;
572
573 if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
574 if (ret == 0)
575 ret = -ENOMEM;
576 continue;
577 }
578
579 if (de->d_type == DT_UNKNOWN) {
580 struct stat st;
581
582 if (lstat(entry_path, &st) < 0) {
583 if (ret == 0 && errno != ENOENT)
584 ret = -errno;
585 free(entry_path);
586 continue;
587 }
588
589 is_dir = S_ISDIR(st.st_mode);
590
591 } else
592 is_dir = de->d_type == DT_DIR;
593
062e01bb 594 r = item_set_perms(i, entry_path);
a8d88783
MS
595 if (r < 0) {
596 if (ret == 0 && r != -ENOENT)
597 ret = r;
598 free(entry_path);
599 continue;
600 }
601
602 if (is_dir) {
062e01bb 603 r = recursive_relabel_children(i, entry_path);
a8d88783
MS
604 if (r < 0 && ret == 0)
605 ret = r;
606 }
607
608 free(entry_path);
609 }
610
611 closedir(d);
612
613 return ret;
614}
615
616static int recursive_relabel(Item *i, const char *path) {
617 int r;
618 struct stat st;
619
062e01bb 620 r = item_set_perms(i, path);
a8d88783
MS
621 if (r < 0)
622 return r;
623
624 if (lstat(path, &st) < 0)
625 return -errno;
626
627 if (S_ISDIR(st.st_mode))
062e01bb 628 r = recursive_relabel_children(i, path);
a8d88783
MS
629
630 return r;
631}
632
99e68c0b
MS
633static int glob_item(Item *i, int (*action)(Item *, const char *)) {
634 int r = 0, k;
635 glob_t g;
636 char **fn;
637
638 zero(g);
639
640 errno = 0;
641 if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
642
643 if (k != GLOB_NOMATCH) {
644 if (errno != 0)
645 errno = EIO;
646
647 log_error("glob(%s) failed: %m", i->path);
648 return -errno;
649 }
650 }
651
652 STRV_FOREACH(fn, g.gl_pathv)
653 if ((k = action(i, *fn)) < 0)
654 r = k;
655
656 globfree(&g);
657 return r;
658}
659
3b63d2d3 660static int create_item(Item *i) {
e9a5ef7c 661 int r, e;
3b63d2d3
LP
662 mode_t u;
663 struct stat st;
5008d581 664
3b63d2d3 665 assert(i);
5008d581 666
3b63d2d3
LP
667 switch (i->type) {
668
669 case IGNORE_PATH:
670 case REMOVE_PATH:
671 case RECURSIVE_REMOVE_PATH:
672 return 0;
5008d581 673
3b63d2d3 674 case CREATE_FILE:
31ed59c5 675 case TRUNCATE_FILE:
1845fdd9
DR
676 r = write_one_file(i, i->path);
677 if (r < 0)
678 return r;
679 break;
d4e9eb91
DR
680 case WRITE_FILE:
681 r = glob_item(i, write_one_file);
f05bc3f7
MS
682 if (r < 0)
683 return r;
5008d581 684
3b63d2d3
LP
685 break;
686
687 case TRUNCATE_DIRECTORY:
688 case CREATE_DIRECTORY:
5008d581
LP
689
690 u = umask(0);
d2e54fae 691 mkdir_parents_label(i->path, 0755);
3b63d2d3 692 r = mkdir(i->path, i->mode);
5008d581
LP
693 umask(u);
694
695 if (r < 0 && errno != EEXIST) {
3b63d2d3 696 log_error("Failed to create directory %s: %m", i->path);
f05bc3f7 697 return -errno;
5008d581
LP
698 }
699
3b63d2d3
LP
700 if (stat(i->path, &st) < 0) {
701 log_error("stat(%s) failed: %m", i->path);
f05bc3f7 702 return -errno;
5008d581
LP
703 }
704
705 if (!S_ISDIR(st.st_mode)) {
3b63d2d3 706 log_error("%s is not a directory.", i->path);
f05bc3f7 707 return -EEXIST;
5008d581
LP
708 }
709
062e01bb 710 r = item_set_perms(i, i->path);
f05bc3f7
MS
711 if (r < 0)
712 return r;
3b63d2d3
LP
713
714 break;
ee17ee7c
LP
715
716 case CREATE_FIFO:
717
718 u = umask(0);
719 r = mkfifo(i->path, i->mode);
720 umask(u);
721
722 if (r < 0 && errno != EEXIST) {
723 log_error("Failed to create fifo %s: %m", i->path);
f05bc3f7 724 return -errno;
ee17ee7c
LP
725 }
726
727 if (stat(i->path, &st) < 0) {
728 log_error("stat(%s) failed: %m", i->path);
f05bc3f7 729 return -errno;
ee17ee7c
LP
730 }
731
732 if (!S_ISFIFO(st.st_mode)) {
733 log_error("%s is not a fifo.", i->path);
f05bc3f7 734 return -EEXIST;
ee17ee7c
LP
735 }
736
062e01bb 737 r = item_set_perms(i, i->path);
f05bc3f7
MS
738 if (r < 0)
739 return r;
ee17ee7c
LP
740
741 break;
a8d88783 742
468d726b
LP
743 case CREATE_SYMLINK: {
744 char *x;
745
e9a5ef7c 746 label_context_set(i->path, S_IFLNK);
468d726b 747 r = symlink(i->argument, i->path);
e9a5ef7c
KS
748 e = errno;
749 label_context_clear();
750 errno = e;
751
468d726b
LP
752 if (r < 0 && errno != EEXIST) {
753 log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
754 return -errno;
755 }
756
757 r = readlink_malloc(i->path, &x);
758 if (r < 0) {
759 log_error("readlink(%s) failed: %s", i->path, strerror(-r));
760 return -errno;
761 }
762
763 if (!streq(i->argument, x)) {
764 free(x);
765 log_error("%s is not the right symlinks.", i->path);
766 return -EEXIST;
767 }
768
769 free(x);
770 break;
771 }
772
773 case CREATE_BLOCK_DEVICE:
774 case CREATE_CHAR_DEVICE: {
cb7ed9df
LP
775 mode_t file_type;
776
777 if (have_effective_cap(CAP_MKNOD) == 0) {
778 /* In a container we lack CAP_MKNOD. We
779 shouldnt attempt to create the device node in
780 that case to avoid noise, and we don't support
781 virtualized devices in containers anyway. */
782
783 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
784 return 0;
785 }
786
787 file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
468d726b
LP
788
789 u = umask(0);
e7aee759
MS
790 label_context_set(i->path, file_type);
791 r = mknod(i->path, i->mode | file_type, i->major_minor);
e9a5ef7c
KS
792 e = errno;
793 label_context_clear();
468d726b 794 umask(u);
e9a5ef7c 795 errno = e;
468d726b
LP
796
797 if (r < 0 && errno != EEXIST) {
798 log_error("Failed to create device node %s: %m", i->path);
799 return -errno;
800 }
801
802 if (stat(i->path, &st) < 0) {
803 log_error("stat(%s) failed: %m", i->path);
804 return -errno;
805 }
806
e7aee759 807 if ((st.st_mode & S_IFMT) != file_type) {
468d726b
LP
808 log_error("%s is not a device node.", i->path);
809 return -EEXIST;
810 }
811
812 r = item_set_perms(i, i->path);
813 if (r < 0)
814 return r;
815
816 break;
817 }
818
777b87e7
MS
819 case RELABEL_PATH:
820
821 r = glob_item(i, item_set_perms);
822 if (r < 0)
823 return 0;
824 break;
825
a8d88783
MS
826 case RECURSIVE_RELABEL_PATH:
827
828 r = glob_item(i, recursive_relabel);
829 if (r < 0)
830 return r;
3b63d2d3
LP
831 }
832
3b63d2d3
LP
833 log_debug("%s created successfully.", i->path);
834
f05bc3f7 835 return 0;
3b63d2d3
LP
836}
837
a0896123 838static int remove_item_instance(Item *i, const char *instance) {
3b63d2d3
LP
839 int r;
840
841 assert(i);
842
843 switch (i->type) {
844
845 case CREATE_FILE:
846 case TRUNCATE_FILE:
847 case CREATE_DIRECTORY:
ee17ee7c 848 case CREATE_FIFO:
468d726b
LP
849 case CREATE_SYMLINK:
850 case CREATE_BLOCK_DEVICE:
851 case CREATE_CHAR_DEVICE:
3b63d2d3 852 case IGNORE_PATH:
777b87e7 853 case RELABEL_PATH:
a8d88783 854 case RECURSIVE_RELABEL_PATH:
31ed59c5 855 case WRITE_FILE:
3b63d2d3
LP
856 break;
857
858 case REMOVE_PATH:
b8bb3e8f
LP
859 if (remove(instance) < 0 && errno != ENOENT) {
860 log_error("remove(%s): %m", instance);
3b63d2d3 861 return -errno;
5008d581 862 }
3b63d2d3
LP
863
864 break;
865
866 case TRUNCATE_DIRECTORY:
867 case RECURSIVE_REMOVE_PATH:
d139b24a
LP
868 /* FIXME: we probably should use dir_cleanup() here
869 * instead of rm_rf() so that 'x' is honoured. */
f56d5db9 870 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
468d726b 871 if (r < 0 && r != -ENOENT) {
b8bb3e8f 872 log_error("rm_rf(%s): %s", instance, strerror(-r));
3b63d2d3
LP
873 return r;
874 }
875
876 break;
877 }
878
879 return 0;
880}
881
a0896123 882static int remove_item(Item *i) {
99e68c0b
MS
883 int r = 0;
884
b8bb3e8f
LP
885 assert(i);
886
887 switch (i->type) {
888
889 case CREATE_FILE:
890 case TRUNCATE_FILE:
891 case CREATE_DIRECTORY:
ee17ee7c 892 case CREATE_FIFO:
468d726b
LP
893 case CREATE_SYMLINK:
894 case CREATE_CHAR_DEVICE:
895 case CREATE_BLOCK_DEVICE:
b8bb3e8f 896 case IGNORE_PATH:
777b87e7 897 case RELABEL_PATH:
a8d88783 898 case RECURSIVE_RELABEL_PATH:
31ed59c5 899 case WRITE_FILE:
b8bb3e8f
LP
900 break;
901
902 case REMOVE_PATH:
903 case TRUNCATE_DIRECTORY:
99e68c0b
MS
904 case RECURSIVE_REMOVE_PATH:
905 r = glob_item(i, remove_item_instance);
906 break;
b8bb3e8f
LP
907 }
908
99e68c0b 909 return r;
b8bb3e8f
LP
910}
911
3b63d2d3
LP
912static int process_item(Item *i) {
913 int r, q, p;
914
915 assert(i);
916
917 r = arg_create ? create_item(i) : 0;
a0896123 918 q = arg_remove ? remove_item(i) : 0;
3b63d2d3
LP
919 p = arg_clean ? clean_item(i) : 0;
920
921 if (r < 0)
922 return r;
923
924 if (q < 0)
925 return q;
926
927 return p;
928}
929
930static void item_free(Item *i) {
931 assert(i);
932
933 free(i->path);
468d726b 934 free(i->argument);
3b63d2d3
LP
935 free(i);
936}
937
bfe95f35
LP
938static bool item_equal(Item *a, Item *b) {
939 assert(a);
940 assert(b);
941
942 if (!streq_ptr(a->path, b->path))
943 return false;
944
945 if (a->type != b->type)
946 return false;
947
948 if (a->uid_set != b->uid_set ||
949 (a->uid_set && a->uid != b->uid))
950 return false;
951
952 if (a->gid_set != b->gid_set ||
953 (a->gid_set && a->gid != b->gid))
954 return false;
955
956 if (a->mode_set != b->mode_set ||
957 (a->mode_set && a->mode != b->mode))
958 return false;
959
960 if (a->age_set != b->age_set ||
961 (a->age_set && a->age != b->age))
962 return false;
963
31ed59c5
LP
964 if ((a->type == CREATE_FILE ||
965 a->type == TRUNCATE_FILE ||
966 a->type == WRITE_FILE ||
967 a->type == CREATE_SYMLINK) &&
1733ca54 968 !streq_ptr(a->argument, b->argument))
468d726b
LP
969 return false;
970
971 if ((a->type == CREATE_CHAR_DEVICE ||
972 a->type == CREATE_BLOCK_DEVICE) &&
973 a->major_minor != b->major_minor)
974 return false;
975
bfe95f35
LP
976 return true;
977}
978
fba6e687 979static int parse_line(const char *fname, unsigned line, const char *buffer) {
bfe95f35 980 Item *i, *existing;
3b63d2d3 981 char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
66ccd038 982 char type;
bfe95f35 983 Hashmap *h;
31ed59c5 984 int r, n = -1;
3b63d2d3
LP
985
986 assert(fname);
987 assert(line >= 1);
988 assert(buffer);
989
468d726b 990 i = new0(Item, 1);
0d0f0c50
SL
991 if (!i)
992 return log_oom();
3b63d2d3 993
bd40a2d8
LP
994 if (sscanf(buffer,
995 "%c "
996 "%ms "
997 "%ms "
998 "%ms "
999 "%ms "
468d726b 1000 "%ms "
31ed59c5 1001 "%n",
66ccd038 1002 &type,
bd40a2d8
LP
1003 &i->path,
1004 &mode,
1005 &user,
1006 &group,
468d726b 1007 &age,
31ed59c5 1008 &n) < 2) {
3b63d2d3
LP
1009 log_error("[%s:%u] Syntax error.", fname, line);
1010 r = -EIO;
1011 goto finish;
5008d581
LP
1012 }
1013
31ed59c5
LP
1014 if (n >= 0) {
1015 n += strspn(buffer+n, WHITESPACE);
1016 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1017 i->argument = unquote(buffer+n, "\"");
0d0f0c50
SL
1018 if (!i->argument)
1019 return log_oom();
31ed59c5
LP
1020 }
1021 }
1022
777b87e7 1023 switch(type) {
468d726b 1024
777b87e7
MS
1025 case CREATE_FILE:
1026 case TRUNCATE_FILE:
1027 case CREATE_DIRECTORY:
1028 case TRUNCATE_DIRECTORY:
1029 case CREATE_FIFO:
1030 case IGNORE_PATH:
1031 case REMOVE_PATH:
1032 case RECURSIVE_REMOVE_PATH:
1033 case RELABEL_PATH:
1034 case RECURSIVE_RELABEL_PATH:
1035 break;
468d726b
LP
1036
1037 case CREATE_SYMLINK:
1038 if (!i->argument) {
1039 log_error("[%s:%u] Symlink file requires argument.", fname, line);
1040 r = -EBADMSG;
1041 goto finish;
1042 }
1043 break;
1044
31ed59c5
LP
1045 case WRITE_FILE:
1046 if (!i->argument) {
1047 log_error("[%s:%u] Write file requires argument.", fname, line);
1048 r = -EBADMSG;
1049 goto finish;
1050 }
1051 break;
1052
468d726b
LP
1053 case CREATE_CHAR_DEVICE:
1054 case CREATE_BLOCK_DEVICE: {
1055 unsigned major, minor;
1056
1057 if (!i->argument) {
1058 log_error("[%s:%u] Device file requires argument.", fname, line);
1059 r = -EBADMSG;
1060 goto finish;
1061 }
1062
1063 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
1064 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
1065 r = -EBADMSG;
1066 goto finish;
1067 }
1068
1069 i->major_minor = makedev(major, minor);
1070 break;
1071 }
1072
777b87e7 1073 default:
a8d88783 1074 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
3b63d2d3 1075 r = -EBADMSG;
4aa8b15b 1076 goto finish;
3b63d2d3 1077 }
468d726b 1078
a8d88783 1079 i->type = type;
3b63d2d3
LP
1080
1081 if (!path_is_absolute(i->path)) {
1082 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
1083 r = -EBADMSG;
1084 goto finish;
1085 }
1086
1087 path_kill_slashes(i->path);
1088
fba6e687 1089 if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
3b63d2d3
LP
1090 r = 0;
1091 goto finish;
1092 }
5008d581 1093
3b63d2d3 1094 if (user && !streq(user, "-")) {
4b67834e
LP
1095 const char *u = user;
1096
d05c5031 1097 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
4b67834e 1098 if (r < 0) {
3b63d2d3 1099 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
3b63d2d3
LP
1100 goto finish;
1101 }
1102
1103 i->uid_set = true;
1104 }
1105
1106 if (group && !streq(group, "-")) {
4b67834e
LP
1107 const char *g = group;
1108
1109 r = get_group_creds(&g, &i->gid);
1110 if (r < 0) {
3b63d2d3 1111 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
3b63d2d3
LP
1112 goto finish;
1113 }
1114
1115 i->gid_set = true;
1116 }
1117
1118 if (mode && !streq(mode, "-")) {
1119 unsigned m;
1120
1121 if (sscanf(mode, "%o", &m) != 1) {
1122 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1123 r = -ENOENT;
1124 goto finish;
1125 }
1126
1127 i->mode = m;
1128 i->mode_set = true;
1129 } else
468d726b
LP
1130 i->mode =
1131 i->type == CREATE_DIRECTORY ||
1132 i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
3b63d2d3
LP
1133
1134 if (age && !streq(age, "-")) {
24f3a374
LP
1135 const char *a = age;
1136
1137 if (*a == '~') {
1138 i->keep_first_level = true;
1139 a++;
1140 }
1141
1142 if (parse_usec(a, &i->age) < 0) {
3b63d2d3
LP
1143 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1144 r = -EBADMSG;
1145 goto finish;
1146 }
1147
1148 i->age_set = true;
1149 }
1150
bfe95f35
LP
1151 h = needs_glob(i->type) ? globs : items;
1152
468d726b
LP
1153 existing = hashmap_get(h, i->path);
1154 if (existing) {
bfe95f35
LP
1155
1156 /* Two identical items are fine */
1157 if (!item_equal(existing, i))
022707d9 1158 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
022707d9 1159
bfe95f35
LP
1160 r = 0;
1161 goto finish;
1162 }
1163
468d726b
LP
1164 r = hashmap_put(h, i->path, i);
1165 if (r < 0) {
3b63d2d3
LP
1166 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
1167 goto finish;
1168 }
1169
1170 i = NULL;
4aa8b15b 1171 r = 0;
5008d581
LP
1172
1173finish:
5008d581
LP
1174 free(user);
1175 free(group);
3b63d2d3
LP
1176 free(mode);
1177 free(age);
5008d581 1178
3b63d2d3
LP
1179 if (i)
1180 item_free(i);
4aa8b15b
LP
1181
1182 return r;
5008d581
LP
1183}
1184
3b63d2d3
LP
1185static int help(void) {
1186
522d4a49
LP
1187 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1188 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
3b63d2d3
LP
1189 " -h --help Show this help\n"
1190 " --create Create marked files/directories\n"
1191 " --clean Clean up marked directories\n"
fba6e687 1192 " --remove Remove marked files/directories\n"
522d4a49 1193 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
3b63d2d3
LP
1194 program_invocation_short_name);
1195
1196 return 0;
1197}
1198
1199static int parse_argv(int argc, char *argv[]) {
1200
1201 enum {
1202 ARG_CREATE,
1203 ARG_CLEAN,
fba6e687
LP
1204 ARG_REMOVE,
1205 ARG_PREFIX
3b63d2d3
LP
1206 };
1207
1208 static const struct option options[] = {
1209 { "help", no_argument, NULL, 'h' },
1210 { "create", no_argument, NULL, ARG_CREATE },
1211 { "clean", no_argument, NULL, ARG_CLEAN },
1212 { "remove", no_argument, NULL, ARG_REMOVE },
fba6e687 1213 { "prefix", required_argument, NULL, ARG_PREFIX },
3b63d2d3
LP
1214 { NULL, 0, NULL, 0 }
1215 };
1216
1217 int c;
1218
1219 assert(argc >= 0);
1220 assert(argv);
1221
1222 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1223
1224 switch (c) {
1225
1226 case 'h':
1227 help();
1228 return 0;
1229
1230 case ARG_CREATE:
1231 arg_create = true;
1232 break;
1233
1234 case ARG_CLEAN:
1235 arg_clean = true;
1236 break;
1237
1238 case ARG_REMOVE:
1239 arg_remove = true;
1240 break;
1241
fba6e687
LP
1242 case ARG_PREFIX:
1243 arg_prefix = optarg;
1244 break;
1245
3b63d2d3
LP
1246 case '?':
1247 return -EINVAL;
1248
1249 default:
1250 log_error("Unknown option code %c", c);
1251 return -EINVAL;
1252 }
1253 }
1254
1255 if (!arg_clean && !arg_create && !arg_remove) {
35b8ca3a 1256 log_error("You need to specify at least one of --clean, --create or --remove.");
3b63d2d3
LP
1257 return -EINVAL;
1258 }
1259
1260 return 1;
1261}
1262
fba6e687
LP
1263static int read_config_file(const char *fn, bool ignore_enoent) {
1264 FILE *f;
1265 unsigned v = 0;
1266 int r = 0;
1267
1268 assert(fn);
1269
468d726b
LP
1270 f = fopen(fn, "re");
1271 if (!f) {
fba6e687
LP
1272
1273 if (ignore_enoent && errno == ENOENT)
1274 return 0;
1275
1276 log_error("Failed to open %s: %m", fn);
1277 return -errno;
1278 }
1279
772f8371 1280 log_debug("apply: %s\n", fn);
fba6e687
LP
1281 for (;;) {
1282 char line[LINE_MAX], *l;
1283 int k;
1284
1285 if (!(fgets(line, sizeof(line), f)))
1286 break;
1287
1288 v++;
1289
1290 l = strstrip(line);
1291 if (*l == '#' || *l == 0)
1292 continue;
1293
1294 if ((k = parse_line(fn, v, l)) < 0)
1295 if (r == 0)
1296 r = k;
1297 }
1298
1299 if (ferror(f)) {
1300 log_error("Failed to read from file %s: %m", fn);
1301 if (r == 0)
1302 r = -EIO;
1303 }
1304
1305 fclose(f);
1306
1307 return r;
1308}
1309
9125670f
DR
1310static char *resolve_fragment(const char *fragment, const char **search_paths) {
1311 const char **p;
1312 char *resolved_path;
1313
1314 if (is_path(fragment))
1315 return strdup(fragment);
1316
1317 STRV_FOREACH(p, search_paths) {
b7def684 1318 resolved_path = strjoin(*p, "/", fragment, NULL);
9125670f 1319 if (resolved_path == NULL) {
0d0f0c50 1320 log_oom();
9125670f
DR
1321 return NULL;
1322 }
1323
1324 if (access(resolved_path, F_OK) == 0)
1325 return resolved_path;
1326
1327 free(resolved_path);
1328 }
1329
ca2e894b 1330 errno = ENOENT;
9125670f
DR
1331 return NULL;
1332}
1333
5008d581 1334int main(int argc, char *argv[]) {
fba6e687 1335 int r;
3b63d2d3
LP
1336 Item *i;
1337 Iterator iterator;
1338
fdcad0c2
LP
1339 r = parse_argv(argc, argv);
1340 if (r <= 0)
3b63d2d3 1341 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
5008d581 1342
eb0ca9eb 1343 log_set_target(LOG_TARGET_AUTO);
5008d581
LP
1344 log_parse_environment();
1345 log_open();
1346
4c12626c
LP
1347 umask(0022);
1348
e9a5ef7c 1349 label_init(NULL);
5008d581 1350
b8bb3e8f
LP
1351 items = hashmap_new(string_hash_func, string_compare_func);
1352 globs = hashmap_new(string_hash_func, string_compare_func);
1353
1354 if (!items || !globs) {
0d0f0c50 1355 log_oom();
3b63d2d3
LP
1356 r = EXIT_FAILURE;
1357 goto finish;
1358 }
1359
5008d581
LP
1360 r = EXIT_SUCCESS;
1361
fba6e687
LP
1362 if (optind < argc) {
1363 int j;
5008d581 1364
9125670f 1365 for (j = optind; j < argc; j++) {
ca2e894b
KS
1366 char *fragment;
1367
796b06c2 1368 fragment = resolve_fragment(argv[j], (const char **)conf_file_dirs);
ca2e894b 1369 if (!fragment) {
94f7a714 1370 log_error("Failed to find a %s file: %m", argv[j]);
ca2e894b
KS
1371 r = EXIT_FAILURE;
1372 goto finish;
1373 }
9125670f 1374 if (read_config_file(fragment, false) < 0)
fba6e687 1375 r = EXIT_FAILURE;
9125670f
DR
1376 free(fragment);
1377 }
5008d581 1378
fba6e687 1379 } else {
772f8371 1380 char **files, **f;
5008d581 1381
796b06c2 1382 r = conf_files_list_strv(&files, ".conf", (const char **)conf_file_dirs);
44143309 1383 if (r < 0) {
44143309 1384 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
a48f3d15 1385 r = EXIT_FAILURE;
44143309
KS
1386 goto finish;
1387 }
3b63d2d3 1388
772f8371
KS
1389 STRV_FOREACH(f, files) {
1390 if (read_config_file(*f, true) < 0)
3b63d2d3 1391 r = EXIT_FAILURE;
5008d581
LP
1392 }
1393
772f8371
KS
1394 strv_free(files);
1395 }
5008d581 1396
b8bb3e8f 1397 HASHMAP_FOREACH(i, globs, iterator)
21bdae12 1398 process_item(i);
b8bb3e8f 1399
3b63d2d3 1400 HASHMAP_FOREACH(i, items, iterator)
21bdae12 1401 process_item(i);
3b63d2d3 1402
5008d581 1403finish:
3b63d2d3
LP
1404 while ((i = hashmap_steal_first(items)))
1405 item_free(i);
1406
17b90525
LP
1407 while ((i = hashmap_steal_first(globs)))
1408 item_free(i);
1409
3b63d2d3 1410 hashmap_free(items);
b8bb3e8f 1411 hashmap_free(globs);
5008d581 1412
17b90525
LP
1413 set_free_free(unix_sockets);
1414
29003cff
LP
1415 label_finish();
1416
5008d581
LP
1417 return r;
1418}