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