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