]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tmpfiles.c
tmpfiles: move binary to /bin to make it publicly available
[thirdparty/systemd.git] / src / tmpfiles.c
CommitLineData
5008d581
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
3b63d2d3 6 Copyright 2010 Lennart Poettering, Kay Sievers
5008d581
LP
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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>
3b63d2d3
LP
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>
b8bb3e8f
LP
39#include <glob.h>
40#include <fnmatch.h>
5008d581
LP
41
42#include "log.h"
43#include "util.h"
44#include "strv.h"
45#include "label.h"
3b63d2d3 46#include "set.h"
5008d581 47
01000479 48/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
5008d581
LP
49 * them in the file system. This is intended to be used to create
50 * properly owned directories beneath /tmp, /var/tmp, /var/run and
51 * /var/lock which are volatile and hence need to be recreated on
52 * bootup. */
53
3b63d2d3 54enum {
b8bb3e8f 55 /* These ones take file names */
3b63d2d3
LP
56 CREATE_FILE = 'f',
57 TRUNCATE_FILE = 'F',
58 CREATE_DIRECTORY = 'd',
59 TRUNCATE_DIRECTORY = 'D',
b8bb3e8f
LP
60
61 /* These ones take globs */
3b63d2d3
LP
62 IGNORE_PATH = 'x',
63 REMOVE_PATH = 'r',
64 RECURSIVE_REMOVE_PATH = 'R'
65};
66
67typedef struct Item {
5008d581 68 char type;
3b63d2d3
LP
69
70 char *path;
5008d581
LP
71 uid_t uid;
72 gid_t gid;
3b63d2d3
LP
73 mode_t mode;
74 usec_t age;
75
76 bool uid_set:1;
77 bool gid_set:1;
78 bool mode_set:1;
79 bool age_set:1;
80} Item;
81
b8bb3e8f 82static Hashmap *items = NULL, *globs = NULL;
3b63d2d3
LP
83
84static bool arg_create = false;
85static bool arg_clean = false;
86static bool arg_remove = false;
87
fba6e687
LP
88static const char *arg_prefix = NULL;
89
3b63d2d3
LP
90#define MAX_DEPTH 256
91
b8bb3e8f
LP
92static bool needs_glob(int t) {
93 return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH;
94}
95
96static struct Item* find_glob(Hashmap *h, const char *match) {
97 Item *j;
98 Iterator i;
99
100 HASHMAP_FOREACH(j, h, i)
101 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
102 return j;
103
104 return NULL;
105}
106
3b63d2d3
LP
107static int dir_cleanup(
108 const char *p,
109 DIR *d,
110 const struct stat *ds,
111 usec_t cutoff,
112 dev_t rootdev,
113 bool mountpoint,
114 int maxdepth)
115{
116 struct dirent *dent;
117 struct timespec times[2];
118 bool deleted = false;
119 char *sub_path = NULL;
120 int r = 0;
121
122 while ((dent = readdir(d))) {
123 struct stat s;
124 usec_t age;
125
126 if (streq(dent->d_name, ".") ||
127 streq(dent->d_name, ".."))
128 continue;
5008d581 129
3b63d2d3 130 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
5008d581 131
3b63d2d3
LP
132 if (errno != ENOENT) {
133 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
134 r = -errno;
135 }
136
137 continue;
138 }
139
140 /* Stay on the same filesystem */
141 if (s.st_dev != rootdev)
142 continue;
143
144 /* Do not delete read-only files owned by root */
145 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
146 continue;
147
148 free(sub_path);
149 sub_path = NULL;
150
151 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
152 log_error("Out of memory");
153 r = -ENOMEM;
154 goto finish;
155 }
156
157 /* Is there an item configured for this path? */
158 if (hashmap_get(items, sub_path))
159 continue;
160
b8bb3e8f
LP
161 if (find_glob(globs, sub_path))
162 continue;
163
3b63d2d3
LP
164 if (S_ISDIR(s.st_mode)) {
165
166 if (mountpoint &&
167 streq(dent->d_name, "lost+found") &&
168 s.st_uid == 0)
169 continue;
170
171 if (maxdepth <= 0)
172 log_warning("Reached max depth on %s.", sub_path);
173 else {
174 DIR *sub_dir;
175 int q;
176
a247755d 177 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW);
3b63d2d3
LP
178 if (sub_dir == NULL) {
179 if (errno != ENOENT) {
180 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
181 r = -errno;
182 }
183
184 continue;
185 }
186
187 q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
188 closedir(sub_dir);
189
190 if (q < 0)
191 r = q;
192 }
193
194 /* Ignore ctime, we change it when deleting */
195 age = MAX(timespec_load(&s.st_mtim),
196 timespec_load(&s.st_atim));
197 if (age >= cutoff)
198 continue;
199
200 log_debug("rmdir '%s'\n", sub_path);
201
202 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
203 if (errno != ENOENT && errno != ENOTEMPTY) {
204 log_error("rmdir(%s): %m", sub_path);
205 r = -errno;
206 }
207 }
208
209 } else {
9c73736d
LP
210 /* Skip files for which the sticky bit is
211 * set. These are semantics we define, and are
212 * unknown elsewhere. See XDG_RUNTIME_DIR
213 * specification for details. */
214 if (s.st_mode & S_ISVTX)
215 continue;
216
3b63d2d3
LP
217 if (mountpoint) {
218 if (streq(dent->d_name, ".journal") &&
219 s.st_uid == 0)
220 continue;
221
222 if (streq(dent->d_name, "aquota.user") ||
223 streq(dent->d_name, "aquota.group"))
224 continue;
225 }
226
227 age = MAX3(timespec_load(&s.st_mtim),
228 timespec_load(&s.st_atim),
229 timespec_load(&s.st_ctim));
230
231 if (age >= cutoff)
232 continue;
233
234 log_debug("unlink '%s'\n", sub_path);
235
236 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
237 if (errno != ENOENT) {
238 log_error("unlink(%s): %m", sub_path);
239 r = -errno;
240 }
241 }
242
243 deleted = true;
244 }
245 }
246
247finish:
248 if (deleted) {
249 /* Restore original directory timestamps */
250 times[0] = ds->st_atim;
251 times[1] = ds->st_mtim;
252
253 if (futimens(dirfd(d), times) < 0)
254 log_error("utimensat(%s): %m", p);
255 }
256
257 free(sub_path);
258
259 return r;
260}
261
262static int clean_item(Item *i) {
263 DIR *d;
264 struct stat s, ps;
265 bool mountpoint;
266 int r;
267 usec_t cutoff, n;
268
269 assert(i);
270
271 if (i->type != CREATE_DIRECTORY &&
272 i->type != TRUNCATE_DIRECTORY &&
273 i->type != IGNORE_PATH)
274 return 0;
275
276 if (!i->age_set || i->age <= 0)
277 return 0;
278
279 n = now(CLOCK_REALTIME);
280 if (n < i->age)
281 return 0;
282
283 cutoff = n - i->age;
284
285 d = opendir(i->path);
286 if (!d) {
287 if (errno == ENOENT)
288 return 0;
289
290 log_error("Failed to open directory %s: %m", i->path);
291 return -errno;
292 }
293
294 if (fstat(dirfd(d), &s) < 0) {
295 log_error("stat(%s) failed: %m", i->path);
296 r = -errno;
5008d581
LP
297 goto finish;
298 }
299
3b63d2d3
LP
300 if (!S_ISDIR(s.st_mode)) {
301 log_error("%s is not a directory.", i->path);
302 r = -ENOTDIR;
5008d581
LP
303 goto finish;
304 }
305
3b63d2d3
LP
306 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
307 log_error("stat(%s/..) failed: %m", i->path);
308 r = -errno;
5008d581 309 goto finish;
4aa8b15b 310 }
5008d581 311
3b63d2d3
LP
312 mountpoint = s.st_dev != ps.st_dev ||
313 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
5008d581 314
3b63d2d3 315 r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
5008d581 316
3b63d2d3
LP
317finish:
318 if (d)
319 closedir(d);
5008d581 320
3b63d2d3
LP
321 return r;
322}
5008d581 323
3b63d2d3
LP
324static int create_item(Item *i) {
325 int fd = -1, r;
326 mode_t u;
327 struct stat st;
5008d581 328
3b63d2d3 329 assert(i);
5008d581 330
3b63d2d3
LP
331 switch (i->type) {
332
333 case IGNORE_PATH:
334 case REMOVE_PATH:
335 case RECURSIVE_REMOVE_PATH:
336 return 0;
5008d581 337
3b63d2d3
LP
338 case CREATE_FILE:
339 case TRUNCATE_FILE:
5008d581
LP
340
341 u = umask(0);
3b63d2d3
LP
342 fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW|
343 (i->type == TRUNCATE_FILE ? O_TRUNC : 0), i->mode);
5008d581
LP
344 umask(u);
345
346 if (fd < 0) {
3b63d2d3 347 log_error("Failed to create file %s: %m", i->path);
4aa8b15b 348 r = -errno;
5008d581
LP
349 goto finish;
350 }
351
352 if (fstat(fd, &st) < 0) {
3b63d2d3 353 log_error("stat(%s) failed: %m", i->path);
4aa8b15b 354 r = -errno;
5008d581
LP
355 goto finish;
356 }
357
358 if (!S_ISREG(st.st_mode)) {
3b63d2d3 359 log_error("%s is not a file.", i->path);
4aa8b15b 360 r = -EEXIST;
5008d581
LP
361 goto finish;
362 }
363
3b63d2d3
LP
364 if (i->mode_set)
365 if (fchmod(fd, i->mode) < 0) {
366 log_error("chmod(%s) failed: %m", i->path);
367 r = -errno;
368 goto finish;
369 }
5008d581 370
3b63d2d3 371 if (i->uid_set || i->gid_set)
5008d581 372 if (fchown(fd,
3b63d2d3
LP
373 i->uid_set ? i->uid : (uid_t) -1,
374 i->gid_set ? i->gid : (gid_t) -1) < 0) {
375 log_error("chown(%s) failed: %m", i->path);
4aa8b15b 376 r = -errno;
5008d581
LP
377 goto finish;
378 }
5008d581 379
3b63d2d3
LP
380 break;
381
382 case TRUNCATE_DIRECTORY:
383 case CREATE_DIRECTORY:
5008d581
LP
384
385 u = umask(0);
3b63d2d3 386 r = mkdir(i->path, i->mode);
5008d581
LP
387 umask(u);
388
389 if (r < 0 && errno != EEXIST) {
3b63d2d3 390 log_error("Failed to create directory %s: %m", i->path);
4aa8b15b 391 r = -errno;
5008d581
LP
392 goto finish;
393 }
394
3b63d2d3
LP
395 if (stat(i->path, &st) < 0) {
396 log_error("stat(%s) failed: %m", i->path);
4aa8b15b 397 r = -errno;
5008d581
LP
398 goto finish;
399 }
400
401 if (!S_ISDIR(st.st_mode)) {
3b63d2d3 402 log_error("%s is not a directory.", i->path);
4aa8b15b 403 r = -EEXIST;
5008d581
LP
404 goto finish;
405 }
406
3b63d2d3
LP
407 if (i->mode_set)
408 if (chmod(i->path, i->mode) < 0) {
409 log_error("chmod(%s) failed: %m", i->path);
410 r = -errno;
411 goto finish;
412 }
5008d581 413
3b63d2d3
LP
414 if (i->uid_set || i->gid_set)
415 if (chown(i->path,
416 i->uid_set ? i->uid : (uid_t) -1,
417 i->gid_set ? i->gid : (gid_t) -1) < 0) {
5008d581 418
3b63d2d3 419 log_error("chown(%s) failed: %m", i->path);
4aa8b15b 420 r = -errno;
5008d581
LP
421 goto finish;
422 }
3b63d2d3
LP
423
424 break;
425 }
426
427 if ((r = label_fix(i->path)) < 0)
428 goto finish;
429
430 log_debug("%s created successfully.", i->path);
431
432finish:
433 if (fd >= 0)
434 close_nointr_nofail(fd);
435
436 return r;
437}
438
b8bb3e8f 439static int remove_item(Item *i, const char *instance) {
3b63d2d3
LP
440 int r;
441
442 assert(i);
443
444 switch (i->type) {
445
446 case CREATE_FILE:
447 case TRUNCATE_FILE:
448 case CREATE_DIRECTORY:
449 case IGNORE_PATH:
450 break;
451
452 case REMOVE_PATH:
b8bb3e8f
LP
453 if (remove(instance) < 0 && errno != ENOENT) {
454 log_error("remove(%s): %m", instance);
3b63d2d3 455 return -errno;
5008d581 456 }
3b63d2d3
LP
457
458 break;
459
460 case TRUNCATE_DIRECTORY:
461 case RECURSIVE_REMOVE_PATH:
b8bb3e8f 462 if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 &&
3b63d2d3 463 r != -ENOENT) {
b8bb3e8f 464 log_error("rm_rf(%s): %s", instance, strerror(-r));
3b63d2d3
LP
465 return r;
466 }
467
468 break;
469 }
470
471 return 0;
472}
473
b8bb3e8f
LP
474static int remove_item_glob(Item *i) {
475 assert(i);
476
477 switch (i->type) {
478
479 case CREATE_FILE:
480 case TRUNCATE_FILE:
481 case CREATE_DIRECTORY:
482 case IGNORE_PATH:
483 break;
484
485 case REMOVE_PATH:
486 case TRUNCATE_DIRECTORY:
487 case RECURSIVE_REMOVE_PATH: {
488 int r = 0, k;
489 glob_t g;
490 char **fn;
491
492 zero(g);
493
494 errno = 0;
495 if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
496
497 if (k != GLOB_NOMATCH) {
498 if (errno != 0)
499 errno = EIO;
500
501 log_error("glob(%s) failed: %m", i->path);
502 return -errno;
503 }
504 }
505
506 STRV_FOREACH(fn, g.gl_pathv)
507 if ((k = remove_item(i, *fn)) < 0)
508 r = k;
509
510 globfree(&g);
511 return r;
512 }
513 }
514
515 return 0;
516}
517
3b63d2d3
LP
518static int process_item(Item *i) {
519 int r, q, p;
520
521 assert(i);
522
523 r = arg_create ? create_item(i) : 0;
b8bb3e8f 524 q = arg_remove ? remove_item_glob(i) : 0;
3b63d2d3
LP
525 p = arg_clean ? clean_item(i) : 0;
526
527 if (r < 0)
528 return r;
529
530 if (q < 0)
531 return q;
532
533 return p;
534}
535
536static void item_free(Item *i) {
537 assert(i);
538
539 free(i->path);
540 free(i);
541}
542
fba6e687 543static int parse_line(const char *fname, unsigned line, const char *buffer) {
3b63d2d3
LP
544 Item *i;
545 char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
bd40a2d8 546 int r;
3b63d2d3
LP
547
548 assert(fname);
549 assert(line >= 1);
550 assert(buffer);
551
552 if (!(i = new0(Item, 1))) {
553 log_error("Out of memory");
554 return -ENOMEM;
555 }
556
bd40a2d8
LP
557 if (sscanf(buffer,
558 "%c "
559 "%ms "
560 "%ms "
561 "%ms "
562 "%ms "
563 "%ms",
564 &i->type,
565 &i->path,
566 &mode,
567 &user,
568 &group,
569 &age) < 2) {
3b63d2d3
LP
570 log_error("[%s:%u] Syntax error.", fname, line);
571 r = -EIO;
572 goto finish;
5008d581
LP
573 }
574
3b63d2d3 575 if (i->type != CREATE_FILE &&
3b63d2d3 576 i->type != TRUNCATE_FILE &&
abe35cc2
GSB
577 i->type != CREATE_DIRECTORY &&
578 i->type != TRUNCATE_DIRECTORY &&
3b63d2d3
LP
579 i->type != IGNORE_PATH &&
580 i->type != REMOVE_PATH &&
581 i->type != RECURSIVE_REMOVE_PATH) {
582 log_error("[%s:%u] Unknown file type '%c'.", fname, line, i->type);
583 r = -EBADMSG;
4aa8b15b 584 goto finish;
3b63d2d3
LP
585 }
586
587 if (!path_is_absolute(i->path)) {
588 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
589 r = -EBADMSG;
590 goto finish;
591 }
592
593 path_kill_slashes(i->path);
594
fba6e687 595 if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
3b63d2d3
LP
596 r = 0;
597 goto finish;
598 }
5008d581 599
3b63d2d3
LP
600 if (user && !streq(user, "-")) {
601 unsigned long lu;
602 struct passwd *p;
603
604 if (streq(user, "root") || streq(user, "0"))
605 i->uid = 0;
606 else if (safe_atolu(user, &lu) >= 0)
607 i->uid = (uid_t) lu;
608 else if ((p = getpwnam(user)))
609 i->uid = p->pw_uid;
610 else {
611 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
612 r = -ENOENT;
613 goto finish;
614 }
615
616 i->uid_set = true;
617 }
618
619 if (group && !streq(group, "-")) {
620 unsigned long lu;
621 struct group *g;
622
623 if (streq(group, "root") || streq(group, "0"))
624 i->gid = 0;
625 else if (safe_atolu(group, &lu) >= 0)
626 i->gid = (gid_t) lu;
627 else if ((g = getgrnam(group)))
628 i->gid = g->gr_gid;
629 else {
630 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
631 r = -ENOENT;
632 goto finish;
633 }
634
635 i->gid_set = true;
636 }
637
638 if (mode && !streq(mode, "-")) {
639 unsigned m;
640
641 if (sscanf(mode, "%o", &m) != 1) {
642 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
643 r = -ENOENT;
644 goto finish;
645 }
646
647 i->mode = m;
648 i->mode_set = true;
649 } else
650 i->mode = i->type == CREATE_DIRECTORY ? 0755 : 0644;
651
652 if (age && !streq(age, "-")) {
653 if (parse_usec(age, &i->age) < 0) {
654 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
655 r = -EBADMSG;
656 goto finish;
657 }
658
659 i->age_set = true;
660 }
661
b8bb3e8f 662 if ((r = hashmap_put(needs_glob(i->type) ? globs : items, i->path, i)) < 0) {
022707d9
LP
663 if (r == -EEXIST) {
664 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
665 r = 0;
666 goto finish;
667 }
668
3b63d2d3
LP
669 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
670 goto finish;
671 }
672
673 i = NULL;
4aa8b15b 674 r = 0;
5008d581
LP
675
676finish:
5008d581
LP
677 free(user);
678 free(group);
3b63d2d3
LP
679 free(mode);
680 free(age);
5008d581 681
3b63d2d3
LP
682 if (i)
683 item_free(i);
4aa8b15b
LP
684
685 return r;
5008d581
LP
686}
687
688static int scandir_filter(const struct dirent *d) {
689 assert(d);
690
691 if (ignore_file(d->d_name))
692 return 0;
693
694 if (d->d_type != DT_REG &&
695 d->d_type != DT_LNK)
696 return 0;
697
698 return endswith(d->d_name, ".conf");
699}
700
3b63d2d3
LP
701static int help(void) {
702
fba6e687
LP
703 printf("%s [OPTIONS...] [CONFIGURATION FILE]\n\n"
704 "Create and clean up temporary files and directories.\n\n"
3b63d2d3
LP
705 " -h --help Show this help\n"
706 " --create Create marked files/directories\n"
707 " --clean Clean up marked directories\n"
fba6e687
LP
708 " --remove Remove marked files/directories\n"
709 " --prefix=PATH Only apply rules that apply to paths\n",
3b63d2d3
LP
710 program_invocation_short_name);
711
712 return 0;
713}
714
715static int parse_argv(int argc, char *argv[]) {
716
717 enum {
718 ARG_CREATE,
719 ARG_CLEAN,
fba6e687
LP
720 ARG_REMOVE,
721 ARG_PREFIX
3b63d2d3
LP
722 };
723
724 static const struct option options[] = {
725 { "help", no_argument, NULL, 'h' },
726 { "create", no_argument, NULL, ARG_CREATE },
727 { "clean", no_argument, NULL, ARG_CLEAN },
728 { "remove", no_argument, NULL, ARG_REMOVE },
fba6e687 729 { "prefix", required_argument, NULL, ARG_PREFIX },
3b63d2d3
LP
730 { NULL, 0, NULL, 0 }
731 };
732
733 int c;
734
735 assert(argc >= 0);
736 assert(argv);
737
738 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
739
740 switch (c) {
741
742 case 'h':
743 help();
744 return 0;
745
746 case ARG_CREATE:
747 arg_create = true;
748 break;
749
750 case ARG_CLEAN:
751 arg_clean = true;
752 break;
753
754 case ARG_REMOVE:
755 arg_remove = true;
756 break;
757
fba6e687
LP
758 case ARG_PREFIX:
759 arg_prefix = optarg;
760 break;
761
3b63d2d3
LP
762 case '?':
763 return -EINVAL;
764
765 default:
766 log_error("Unknown option code %c", c);
767 return -EINVAL;
768 }
769 }
770
771 if (!arg_clean && !arg_create && !arg_remove) {
fba6e687 772 log_error("You need to specify at leat one of --clean, --create or --remove.");
3b63d2d3
LP
773 return -EINVAL;
774 }
775
776 return 1;
777}
778
fba6e687
LP
779static int read_config_file(const char *fn, bool ignore_enoent) {
780 FILE *f;
781 unsigned v = 0;
782 int r = 0;
783
784 assert(fn);
785
786 if (!(f = fopen(fn, "re"))) {
787
788 if (ignore_enoent && errno == ENOENT)
789 return 0;
790
791 log_error("Failed to open %s: %m", fn);
792 return -errno;
793 }
794
795 for (;;) {
796 char line[LINE_MAX], *l;
797 int k;
798
799 if (!(fgets(line, sizeof(line), f)))
800 break;
801
802 v++;
803
804 l = strstrip(line);
805 if (*l == '#' || *l == 0)
806 continue;
807
808 if ((k = parse_line(fn, v, l)) < 0)
809 if (r == 0)
810 r = k;
811 }
812
813 if (ferror(f)) {
814 log_error("Failed to read from file %s: %m", fn);
815 if (r == 0)
816 r = -EIO;
817 }
818
819 fclose(f);
820
821 return r;
822}
823
5008d581 824int main(int argc, char *argv[]) {
fba6e687 825 int r;
3b63d2d3
LP
826 Item *i;
827 Iterator iterator;
828
829 if ((r = parse_argv(argc, argv)) <= 0)
830 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
5008d581 831
eb0ca9eb 832 log_set_target(LOG_TARGET_AUTO);
5008d581
LP
833 log_parse_environment();
834 log_open();
835
836 label_init();
837
b8bb3e8f
LP
838 items = hashmap_new(string_hash_func, string_compare_func);
839 globs = hashmap_new(string_hash_func, string_compare_func);
840
841 if (!items || !globs) {
3b63d2d3
LP
842 log_error("Out of memory");
843 r = EXIT_FAILURE;
844 goto finish;
845 }
846
5008d581
LP
847 r = EXIT_SUCCESS;
848
fba6e687
LP
849 if (optind < argc) {
850 int j;
5008d581 851
fba6e687
LP
852 for (j = optind; j < argc; j++)
853 if (read_config_file(argv[j], false) < 0)
854 r = EXIT_FAILURE;
5008d581 855
fba6e687
LP
856 } else {
857 int n, j;
858 struct dirent **de = NULL;
5008d581 859
fba6e687 860 if ((n = scandir("/etc/tmpfiles.d/", &de, scandir_filter, alphasort)) < 0) {
3b63d2d3
LP
861
862 if (errno != ENOENT) {
fba6e687 863 log_error("Failed to enumerate /etc/tmpfiles.d/ files: %m");
3b63d2d3
LP
864 r = EXIT_FAILURE;
865 }
866
fba6e687 867 goto finish;
5008d581
LP
868 }
869
fba6e687
LP
870 for (j = 0; j < n; j++) {
871 int k;
872 char *fn;
5008d581 873
fba6e687
LP
874 k = asprintf(&fn, "/etc/tmpfiles.d/%s", de[j]->d_name);
875 free(de[j]);
5008d581 876
fba6e687
LP
877 if (k < 0) {
878 log_error("Failed to allocate file name.");
879 r = EXIT_FAILURE;
5008d581 880 continue;
fba6e687 881 }
5008d581 882
fba6e687 883 if (read_config_file(fn, true) < 0)
4aa8b15b 884 r = EXIT_FAILURE;
5008d581 885
fba6e687 886 free(fn);
5008d581
LP
887 }
888
fba6e687 889 free(de);
5008d581
LP
890 }
891
b8bb3e8f
LP
892 HASHMAP_FOREACH(i, globs, iterator)
893 if (process_item(i) < 0)
894 r = EXIT_FAILURE;
895
3b63d2d3
LP
896 HASHMAP_FOREACH(i, items, iterator)
897 if (process_item(i) < 0)
898 r = EXIT_FAILURE;
899
5008d581 900finish:
3b63d2d3
LP
901 while ((i = hashmap_steal_first(items)))
902 item_free(i);
903
904 hashmap_free(items);
b8bb3e8f 905 hashmap_free(globs);
5008d581 906
29003cff
LP
907 label_finish();
908
5008d581
LP
909 return r;
910}