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