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