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