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