blob: c5397ef846ca2cdcabbc945ee7efd0c2f37910e9 [file] [log] [blame]
Lennart Poettering5008d582010-09-28 02:34:02 +02001/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02006 Copyright 2010 Lennart Poettering, Kay Sievers
Lennart Poettering5008d582010-09-28 02:34:02 +02007
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>
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020031#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>
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010039#include <glob.h>
40#include <fnmatch.h>
Lennart Poettering5008d582010-09-28 02:34:02 +020041
42#include "log.h"
43#include "util.h"
44#include "strv.h"
45#include "label.h"
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020046#include "set.h"
Lennart Poettering5008d582010-09-28 02:34:02 +020047
Andreas Jaeger01000472010-09-29 10:08:24 +020048/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
Lennart Poettering5008d582010-09-28 02:34:02 +020049 * 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
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020054enum {
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010055 /* These ones take file names */
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020056 CREATE_FILE = 'f',
57 TRUNCATE_FILE = 'F',
58 CREATE_DIRECTORY = 'd',
59 TRUNCATE_DIRECTORY = 'D',
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010060
61 /* These ones take globs */
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020062 IGNORE_PATH = 'x',
63 REMOVE_PATH = 'r',
64 RECURSIVE_REMOVE_PATH = 'R'
65};
66
67typedef struct Item {
Lennart Poettering5008d582010-09-28 02:34:02 +020068 char type;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020069
70 char *path;
Lennart Poettering5008d582010-09-28 02:34:02 +020071 uid_t uid;
72 gid_t gid;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020073 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
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010082static Hashmap *items = NULL, *globs = NULL;
Lennart Poettering17b90522011-02-14 21:55:06 +010083static Set *unix_sockets = NULL;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020084
85static bool arg_create = false;
86static bool arg_clean = false;
87static bool arg_remove = false;
88
Lennart Poetteringfba6e682011-02-13 14:00:54 +010089static const char *arg_prefix = NULL;
90
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020091#define MAX_DEPTH 256
92
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010093static bool needs_glob(int t) {
94 return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH;
95}
96
97static struct Item* find_glob(Hashmap *h, const char *match) {
98 Item *j;
99 Iterator i;
100
101 HASHMAP_FOREACH(j, h, i)
102 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
103 return j;
104
105 return NULL;
106}
107
Lennart Poettering17b90522011-02-14 21:55:06 +0100108static void load_unix_sockets(void) {
109 FILE *f = NULL;
110 char line[LINE_MAX];
111
112 if (unix_sockets)
113 return;
114
115 /* We maintain a cache of the sockets we found in
116 * /proc/net/unix to speed things up a little. */
117
118 if (!(unix_sockets = set_new(string_hash_func, string_compare_func)))
119 return;
120
121 if (!(f = fopen("/proc/net/unix", "re")))
122 return;
123
124 if (!(fgets(line, sizeof(line), f)))
125 goto fail;
126
127 for (;;) {
128 char *p, *s;
129 int k;
130
131 if (!(fgets(line, sizeof(line), f)))
132 break;
133
134 truncate_nl(line);
135
136 if (strlen(line) < 53)
137 continue;
138
139 p = line + 53;
140 p += strspn(p, WHITESPACE);
141 p += strcspn(p, WHITESPACE);
142 p += strspn(p, WHITESPACE);
143
144 if (*p != '/')
145 continue;
146
147 if (!(s = strdup(p)))
148 goto fail;
149
Lennart Poettering4ff21d82011-02-17 13:13:34 +0100150 path_kill_slashes(s);
151
Lennart Poettering17b90522011-02-14 21:55:06 +0100152 if ((k = set_put(unix_sockets, s)) < 0) {
153 free(s);
154
155 if (k != -EEXIST)
156 goto fail;
157 }
158 }
159
160 return;
161
162fail:
163 set_free_free(unix_sockets);
164 unix_sockets = NULL;
165
166 if (f)
167 fclose(f);
168}
169
170static bool unix_socket_alive(const char *fn) {
171 assert(fn);
172
173 load_unix_sockets();
174
175 if (unix_sockets)
176 return !!set_get(unix_sockets, (char*) fn);
177
178 /* We don't know, so assume yes */
179 return true;
180}
181
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200182static int dir_cleanup(
183 const char *p,
184 DIR *d,
185 const struct stat *ds,
186 usec_t cutoff,
187 dev_t rootdev,
188 bool mountpoint,
189 int maxdepth)
190{
191 struct dirent *dent;
192 struct timespec times[2];
193 bool deleted = false;
194 char *sub_path = NULL;
195 int r = 0;
196
197 while ((dent = readdir(d))) {
198 struct stat s;
199 usec_t age;
200
201 if (streq(dent->d_name, ".") ||
202 streq(dent->d_name, ".."))
203 continue;
204
205 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
206
207 if (errno != ENOENT) {
208 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
209 r = -errno;
210 }
211
212 continue;
213 }
214
215 /* Stay on the same filesystem */
216 if (s.st_dev != rootdev)
217 continue;
218
219 /* Do not delete read-only files owned by root */
220 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
221 continue;
222
223 free(sub_path);
224 sub_path = NULL;
225
226 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
227 log_error("Out of memory");
228 r = -ENOMEM;
229 goto finish;
230 }
231
232 /* Is there an item configured for this path? */
233 if (hashmap_get(items, sub_path))
234 continue;
235
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100236 if (find_glob(globs, sub_path))
237 continue;
238
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200239 if (S_ISDIR(s.st_mode)) {
240
241 if (mountpoint &&
242 streq(dent->d_name, "lost+found") &&
243 s.st_uid == 0)
244 continue;
245
246 if (maxdepth <= 0)
247 log_warning("Reached max depth on %s.", sub_path);
248 else {
249 DIR *sub_dir;
250 int q;
251
Lennart Poetteringa2477552010-12-28 14:20:21 +0100252 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200253 if (sub_dir == NULL) {
254 if (errno != ENOENT) {
255 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
256 r = -errno;
257 }
258
259 continue;
260 }
261
262 q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
263 closedir(sub_dir);
264
265 if (q < 0)
266 r = q;
267 }
268
269 /* Ignore ctime, we change it when deleting */
270 age = MAX(timespec_load(&s.st_mtim),
271 timespec_load(&s.st_atim));
272 if (age >= cutoff)
273 continue;
274
275 log_debug("rmdir '%s'\n", sub_path);
276
277 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
278 if (errno != ENOENT && errno != ENOTEMPTY) {
279 log_error("rmdir(%s): %m", sub_path);
280 r = -errno;
281 }
282 }
283
284 } else {
Lennart Poettering9c737362010-11-14 20:12:51 +0100285 /* Skip files for which the sticky bit is
286 * set. These are semantics we define, and are
287 * unknown elsewhere. See XDG_RUNTIME_DIR
288 * specification for details. */
289 if (s.st_mode & S_ISVTX)
290 continue;
291
Lennart Poettering17b90522011-02-14 21:55:06 +0100292 if (mountpoint && S_ISREG(s.st_mode)) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200293 if (streq(dent->d_name, ".journal") &&
294 s.st_uid == 0)
295 continue;
296
297 if (streq(dent->d_name, "aquota.user") ||
298 streq(dent->d_name, "aquota.group"))
299 continue;
300 }
301
Lennart Poettering17b90522011-02-14 21:55:06 +0100302 /* Ignore sockets that are listed in /proc/net/unix */
303 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
304 continue;
305
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200306 age = MAX3(timespec_load(&s.st_mtim),
307 timespec_load(&s.st_atim),
308 timespec_load(&s.st_ctim));
309
310 if (age >= cutoff)
311 continue;
312
313 log_debug("unlink '%s'\n", sub_path);
314
315 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
316 if (errno != ENOENT) {
317 log_error("unlink(%s): %m", sub_path);
318 r = -errno;
319 }
320 }
321
322 deleted = true;
323 }
324 }
325
326finish:
327 if (deleted) {
328 /* Restore original directory timestamps */
329 times[0] = ds->st_atim;
330 times[1] = ds->st_mtim;
331
332 if (futimens(dirfd(d), times) < 0)
333 log_error("utimensat(%s): %m", p);
334 }
335
336 free(sub_path);
337
338 return r;
339}
340
341static int clean_item(Item *i) {
342 DIR *d;
343 struct stat s, ps;
344 bool mountpoint;
345 int r;
346 usec_t cutoff, n;
347
348 assert(i);
349
350 if (i->type != CREATE_DIRECTORY &&
351 i->type != TRUNCATE_DIRECTORY &&
352 i->type != IGNORE_PATH)
353 return 0;
354
355 if (!i->age_set || i->age <= 0)
356 return 0;
357
358 n = now(CLOCK_REALTIME);
359 if (n < i->age)
360 return 0;
361
362 cutoff = n - i->age;
363
364 d = opendir(i->path);
365 if (!d) {
366 if (errno == ENOENT)
367 return 0;
368
369 log_error("Failed to open directory %s: %m", i->path);
370 return -errno;
371 }
372
373 if (fstat(dirfd(d), &s) < 0) {
374 log_error("stat(%s) failed: %m", i->path);
375 r = -errno;
376 goto finish;
377 }
378
379 if (!S_ISDIR(s.st_mode)) {
380 log_error("%s is not a directory.", i->path);
381 r = -ENOTDIR;
382 goto finish;
383 }
384
385 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
386 log_error("stat(%s/..) failed: %m", i->path);
387 r = -errno;
388 goto finish;
389 }
390
391 mountpoint = s.st_dev != ps.st_dev ||
392 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
393
394 r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
395
396finish:
397 if (d)
398 closedir(d);
399
400 return r;
401}
402
403static int create_item(Item *i) {
404 int fd = -1, r;
405 mode_t u;
406 struct stat st;
407
408 assert(i);
409
410 switch (i->type) {
411
412 case IGNORE_PATH:
413 case REMOVE_PATH:
414 case RECURSIVE_REMOVE_PATH:
415 return 0;
416
417 case CREATE_FILE:
418 case TRUNCATE_FILE:
419
420 u = umask(0);
421 fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW|
422 (i->type == TRUNCATE_FILE ? O_TRUNC : 0), i->mode);
423 umask(u);
424
425 if (fd < 0) {
426 log_error("Failed to create file %s: %m", i->path);
427 r = -errno;
428 goto finish;
429 }
430
431 if (fstat(fd, &st) < 0) {
432 log_error("stat(%s) failed: %m", i->path);
433 r = -errno;
434 goto finish;
435 }
436
437 if (!S_ISREG(st.st_mode)) {
438 log_error("%s is not a file.", i->path);
439 r = -EEXIST;
440 goto finish;
441 }
442
443 if (i->mode_set)
444 if (fchmod(fd, i->mode) < 0) {
445 log_error("chmod(%s) failed: %m", i->path);
446 r = -errno;
447 goto finish;
448 }
449
450 if (i->uid_set || i->gid_set)
451 if (fchown(fd,
452 i->uid_set ? i->uid : (uid_t) -1,
453 i->gid_set ? i->gid : (gid_t) -1) < 0) {
454 log_error("chown(%s) failed: %m", i->path);
455 r = -errno;
456 goto finish;
457 }
458
459 break;
460
461 case TRUNCATE_DIRECTORY:
462 case CREATE_DIRECTORY:
463
464 u = umask(0);
465 r = mkdir(i->path, i->mode);
466 umask(u);
467
468 if (r < 0 && errno != EEXIST) {
469 log_error("Failed to create directory %s: %m", i->path);
470 r = -errno;
471 goto finish;
472 }
473
474 if (stat(i->path, &st) < 0) {
475 log_error("stat(%s) failed: %m", i->path);
476 r = -errno;
477 goto finish;
478 }
479
480 if (!S_ISDIR(st.st_mode)) {
481 log_error("%s is not a directory.", i->path);
482 r = -EEXIST;
483 goto finish;
484 }
485
486 if (i->mode_set)
487 if (chmod(i->path, i->mode) < 0) {
488 log_error("chmod(%s) failed: %m", i->path);
489 r = -errno;
490 goto finish;
491 }
492
493 if (i->uid_set || i->gid_set)
494 if (chown(i->path,
495 i->uid_set ? i->uid : (uid_t) -1,
496 i->gid_set ? i->gid : (gid_t) -1) < 0) {
497
498 log_error("chown(%s) failed: %m", i->path);
499 r = -errno;
500 goto finish;
501 }
502
503 break;
504 }
505
506 if ((r = label_fix(i->path)) < 0)
507 goto finish;
508
509 log_debug("%s created successfully.", i->path);
510
511finish:
512 if (fd >= 0)
513 close_nointr_nofail(fd);
514
515 return r;
516}
517
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100518static int remove_item(Item *i, const char *instance) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200519 int r;
520
521 assert(i);
522
523 switch (i->type) {
524
525 case CREATE_FILE:
526 case TRUNCATE_FILE:
527 case CREATE_DIRECTORY:
528 case IGNORE_PATH:
529 break;
530
531 case REMOVE_PATH:
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100532 if (remove(instance) < 0 && errno != ENOENT) {
533 log_error("remove(%s): %m", instance);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200534 return -errno;
535 }
536
537 break;
538
539 case TRUNCATE_DIRECTORY:
540 case RECURSIVE_REMOVE_PATH:
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100541 if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 &&
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200542 r != -ENOENT) {
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100543 log_error("rm_rf(%s): %s", instance, strerror(-r));
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200544 return r;
545 }
546
547 break;
548 }
549
550 return 0;
551}
552
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100553static int remove_item_glob(Item *i) {
554 assert(i);
555
556 switch (i->type) {
557
558 case CREATE_FILE:
559 case TRUNCATE_FILE:
560 case CREATE_DIRECTORY:
561 case IGNORE_PATH:
562 break;
563
564 case REMOVE_PATH:
565 case TRUNCATE_DIRECTORY:
566 case RECURSIVE_REMOVE_PATH: {
567 int r = 0, k;
568 glob_t g;
569 char **fn;
570
571 zero(g);
572
573 errno = 0;
574 if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
575
576 if (k != GLOB_NOMATCH) {
577 if (errno != 0)
578 errno = EIO;
579
580 log_error("glob(%s) failed: %m", i->path);
581 return -errno;
582 }
583 }
584
585 STRV_FOREACH(fn, g.gl_pathv)
586 if ((k = remove_item(i, *fn)) < 0)
587 r = k;
588
589 globfree(&g);
590 return r;
591 }
592 }
593
594 return 0;
595}
596
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200597static int process_item(Item *i) {
598 int r, q, p;
599
600 assert(i);
601
602 r = arg_create ? create_item(i) : 0;
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100603 q = arg_remove ? remove_item_glob(i) : 0;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200604 p = arg_clean ? clean_item(i) : 0;
605
606 if (r < 0)
607 return r;
608
609 if (q < 0)
610 return q;
611
612 return p;
613}
614
615static void item_free(Item *i) {
616 assert(i);
617
618 free(i->path);
619 free(i);
620}
621
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100622static int parse_line(const char *fname, unsigned line, const char *buffer) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200623 Item *i;
624 char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
Lennart Poetteringbd40a2d2011-01-22 02:18:59 +0100625 int r;
Lennart Poettering5008d582010-09-28 02:34:02 +0200626
627 assert(fname);
628 assert(line >= 1);
629 assert(buffer);
630
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200631 if (!(i = new0(Item, 1))) {
632 log_error("Out of memory");
633 return -ENOMEM;
634 }
635
Lennart Poetteringbd40a2d2011-01-22 02:18:59 +0100636 if (sscanf(buffer,
637 "%c "
638 "%ms "
639 "%ms "
640 "%ms "
641 "%ms "
642 "%ms",
643 &i->type,
644 &i->path,
645 &mode,
646 &user,
647 &group,
648 &age) < 2) {
Lennart Poettering5008d582010-09-28 02:34:02 +0200649 log_error("[%s:%u] Syntax error.", fname, line);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200650 r = -EIO;
Lennart Poettering5008d582010-09-28 02:34:02 +0200651 goto finish;
652 }
653
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200654 if (i->type != CREATE_FILE &&
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200655 i->type != TRUNCATE_FILE &&
Gustavo Sverzut Barbieriabe35cc2010-10-19 23:06:34 -0200656 i->type != CREATE_DIRECTORY &&
657 i->type != TRUNCATE_DIRECTORY &&
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200658 i->type != IGNORE_PATH &&
659 i->type != REMOVE_PATH &&
660 i->type != RECURSIVE_REMOVE_PATH) {
661 log_error("[%s:%u] Unknown file type '%c'.", fname, line, i->type);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200662 r = -EBADMSG;
Lennart Poettering5008d582010-09-28 02:34:02 +0200663 goto finish;
664 }
665
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200666 if (!path_is_absolute(i->path)) {
667 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
668 r = -EBADMSG;
669 goto finish;
670 }
671
672 path_kill_slashes(i->path);
673
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100674 if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200675 r = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +0200676 goto finish;
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200677 }
Lennart Poettering5008d582010-09-28 02:34:02 +0200678
679 if (user && !streq(user, "-")) {
680 unsigned long lu;
681 struct passwd *p;
682
683 if (streq(user, "root") || streq(user, "0"))
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200684 i->uid = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +0200685 else if (safe_atolu(user, &lu) >= 0)
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200686 i->uid = (uid_t) lu;
Lennart Poettering5008d582010-09-28 02:34:02 +0200687 else if ((p = getpwnam(user)))
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200688 i->uid = p->pw_uid;
Lennart Poettering5008d582010-09-28 02:34:02 +0200689 else {
690 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200691 r = -ENOENT;
Lennart Poettering5008d582010-09-28 02:34:02 +0200692 goto finish;
693 }
694
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200695 i->uid_set = true;
Lennart Poettering5008d582010-09-28 02:34:02 +0200696 }
697
698 if (group && !streq(group, "-")) {
699 unsigned long lu;
700 struct group *g;
701
702 if (streq(group, "root") || streq(group, "0"))
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200703 i->gid = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +0200704 else if (safe_atolu(group, &lu) >= 0)
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200705 i->gid = (gid_t) lu;
Lennart Poettering5008d582010-09-28 02:34:02 +0200706 else if ((g = getgrnam(group)))
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200707 i->gid = g->gr_gid;
Lennart Poettering5008d582010-09-28 02:34:02 +0200708 else {
709 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200710 r = -ENOENT;
Lennart Poettering5008d582010-09-28 02:34:02 +0200711 goto finish;
712 }
713
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200714 i->gid_set = true;
Lennart Poettering5008d582010-09-28 02:34:02 +0200715 }
716
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200717 if (mode && !streq(mode, "-")) {
718 unsigned m;
Lennart Poettering5008d582010-09-28 02:34:02 +0200719
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200720 if (sscanf(mode, "%o", &m) != 1) {
721 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
722 r = -ENOENT;
Lennart Poettering5008d582010-09-28 02:34:02 +0200723 goto finish;
724 }
725
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200726 i->mode = m;
727 i->mode_set = true;
728 } else
729 i->mode = i->type == CREATE_DIRECTORY ? 0755 : 0644;
730
731 if (age && !streq(age, "-")) {
732 if (parse_usec(age, &i->age) < 0) {
733 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
734 r = -EBADMSG;
Lennart Poettering5008d582010-09-28 02:34:02 +0200735 goto finish;
736 }
737
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200738 i->age_set = true;
Lennart Poettering5008d582010-09-28 02:34:02 +0200739 }
740
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100741 if ((r = hashmap_put(needs_glob(i->type) ? globs : items, i->path, i)) < 0) {
Lennart Poettering022707d2011-01-05 16:11:15 +0100742 if (r == -EEXIST) {
743 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
744 r = 0;
745 goto finish;
746 }
747
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200748 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200749 goto finish;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200750 }
Lennart Poettering5008d582010-09-28 02:34:02 +0200751
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200752 i = NULL;
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200753 r = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +0200754
755finish:
Lennart Poettering5008d582010-09-28 02:34:02 +0200756 free(user);
757 free(group);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200758 free(mode);
759 free(age);
Lennart Poettering5008d582010-09-28 02:34:02 +0200760
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200761 if (i)
762 item_free(i);
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200763
764 return r;
Lennart Poettering5008d582010-09-28 02:34:02 +0200765}
766
767static int scandir_filter(const struct dirent *d) {
768 assert(d);
769
770 if (ignore_file(d->d_name))
771 return 0;
772
773 if (d->d_type != DT_REG &&
774 d->d_type != DT_LNK)
775 return 0;
776
777 return endswith(d->d_name, ".conf");
778}
779
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200780static int help(void) {
781
Lennart Poettering522d4a42011-02-13 15:08:15 +0100782 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
783 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200784 " -h --help Show this help\n"
785 " --create Create marked files/directories\n"
786 " --clean Clean up marked directories\n"
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100787 " --remove Remove marked files/directories\n"
Lennart Poettering522d4a42011-02-13 15:08:15 +0100788 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200789 program_invocation_short_name);
790
791 return 0;
792}
793
794static int parse_argv(int argc, char *argv[]) {
795
796 enum {
797 ARG_CREATE,
798 ARG_CLEAN,
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100799 ARG_REMOVE,
800 ARG_PREFIX
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200801 };
802
803 static const struct option options[] = {
804 { "help", no_argument, NULL, 'h' },
805 { "create", no_argument, NULL, ARG_CREATE },
806 { "clean", no_argument, NULL, ARG_CLEAN },
807 { "remove", no_argument, NULL, ARG_REMOVE },
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100808 { "prefix", required_argument, NULL, ARG_PREFIX },
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200809 { NULL, 0, NULL, 0 }
810 };
811
812 int c;
813
814 assert(argc >= 0);
815 assert(argv);
816
817 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
818
819 switch (c) {
820
821 case 'h':
822 help();
823 return 0;
824
825 case ARG_CREATE:
826 arg_create = true;
827 break;
828
829 case ARG_CLEAN:
830 arg_clean = true;
831 break;
832
833 case ARG_REMOVE:
834 arg_remove = true;
835 break;
836
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100837 case ARG_PREFIX:
838 arg_prefix = optarg;
839 break;
840
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200841 case '?':
842 return -EINVAL;
843
844 default:
845 log_error("Unknown option code %c", c);
846 return -EINVAL;
847 }
848 }
849
850 if (!arg_clean && !arg_create && !arg_remove) {
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100851 log_error("You need to specify at leat one of --clean, --create or --remove.");
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200852 return -EINVAL;
853 }
854
855 return 1;
856}
857
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100858static int read_config_file(const char *fn, bool ignore_enoent) {
859 FILE *f;
860 unsigned v = 0;
861 int r = 0;
862
863 assert(fn);
864
865 if (!(f = fopen(fn, "re"))) {
866
867 if (ignore_enoent && errno == ENOENT)
868 return 0;
869
870 log_error("Failed to open %s: %m", fn);
871 return -errno;
872 }
873
874 for (;;) {
875 char line[LINE_MAX], *l;
876 int k;
877
878 if (!(fgets(line, sizeof(line), f)))
879 break;
880
881 v++;
882
883 l = strstrip(line);
884 if (*l == '#' || *l == 0)
885 continue;
886
887 if ((k = parse_line(fn, v, l)) < 0)
888 if (r == 0)
889 r = k;
890 }
891
892 if (ferror(f)) {
893 log_error("Failed to read from file %s: %m", fn);
894 if (r == 0)
895 r = -EIO;
896 }
897
898 fclose(f);
899
900 return r;
901}
902
Lennart Poettering5008d582010-09-28 02:34:02 +0200903int main(int argc, char *argv[]) {
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100904 int r;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200905 Item *i;
906 Iterator iterator;
Lennart Poettering5008d582010-09-28 02:34:02 +0200907
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200908 if ((r = parse_argv(argc, argv)) <= 0)
909 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
910
Lennart Poetteringeb0ca9e2011-02-12 09:31:38 +0100911 log_set_target(LOG_TARGET_AUTO);
Lennart Poettering5008d582010-09-28 02:34:02 +0200912 log_parse_environment();
913 log_open();
914
915 label_init();
916
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100917 items = hashmap_new(string_hash_func, string_compare_func);
918 globs = hashmap_new(string_hash_func, string_compare_func);
919
920 if (!items || !globs) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200921 log_error("Out of memory");
922 r = EXIT_FAILURE;
923 goto finish;
924 }
925
Lennart Poettering5008d582010-09-28 02:34:02 +0200926 r = EXIT_SUCCESS;
927
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100928 if (optind < argc) {
929 int j;
Lennart Poettering5008d582010-09-28 02:34:02 +0200930
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100931 for (j = optind; j < argc; j++)
932 if (read_config_file(argv[j], false) < 0)
933 r = EXIT_FAILURE;
Lennart Poettering5008d582010-09-28 02:34:02 +0200934
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100935 } else {
936 int n, j;
937 struct dirent **de = NULL;
Lennart Poettering5008d582010-09-28 02:34:02 +0200938
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100939 if ((n = scandir("/etc/tmpfiles.d/", &de, scandir_filter, alphasort)) < 0) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200940
941 if (errno != ENOENT) {
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100942 log_error("Failed to enumerate /etc/tmpfiles.d/ files: %m");
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200943 r = EXIT_FAILURE;
944 }
945
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100946 goto finish;
Lennart Poettering5008d582010-09-28 02:34:02 +0200947 }
948
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100949 for (j = 0; j < n; j++) {
950 int k;
951 char *fn;
Lennart Poettering5008d582010-09-28 02:34:02 +0200952
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100953 k = asprintf(&fn, "/etc/tmpfiles.d/%s", de[j]->d_name);
954 free(de[j]);
Lennart Poettering5008d582010-09-28 02:34:02 +0200955
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100956 if (k < 0) {
957 log_error("Failed to allocate file name.");
Lennart Poettering4aa8b152010-09-28 22:32:05 +0200958 r = EXIT_FAILURE;
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100959 continue;
960 }
961
962 if (read_config_file(fn, true) < 0)
963 r = EXIT_FAILURE;
964
965 free(fn);
Lennart Poettering5008d582010-09-28 02:34:02 +0200966 }
967
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100968 free(de);
Lennart Poettering5008d582010-09-28 02:34:02 +0200969 }
970
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100971 HASHMAP_FOREACH(i, globs, iterator)
972 if (process_item(i) < 0)
973 r = EXIT_FAILURE;
974
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200975 HASHMAP_FOREACH(i, items, iterator)
976 if (process_item(i) < 0)
977 r = EXIT_FAILURE;
978
Lennart Poettering5008d582010-09-28 02:34:02 +0200979finish:
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200980 while ((i = hashmap_steal_first(items)))
981 item_free(i);
982
Lennart Poettering17b90522011-02-14 21:55:06 +0100983 while ((i = hashmap_steal_first(globs)))
984 item_free(i);
985
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200986 hashmap_free(items);
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100987 hashmap_free(globs);
Lennart Poettering5008d582010-09-28 02:34:02 +0200988
Lennart Poettering17b90522011-02-14 21:55:06 +0100989 set_free_free(unix_sockets);
990
Lennart Poettering29003cf2010-10-19 19:36:45 +0200991 label_finish();
992
Lennart Poettering5008d582010-09-28 02:34:02 +0200993 return r;
994}