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