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