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