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