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