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