blob: 96adbff42e9dcab8613e1a957add1074cef6c199 [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
Lennart Poettering5430f7f2012-04-12 00:20:58 +02009 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
Lennart Poettering5008d582010-09-28 02:34:02 +020011 (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
Lennart Poettering5430f7f2012-04-12 00:20:58 +020016 Lesser General Public License for more details.
Lennart Poettering5008d582010-09-28 02:34:02 +020017
Lennart Poettering5430f7f2012-04-12 00:20:58 +020018 You should have received a copy of the GNU Lesser General Public License
Lennart Poettering5008d582010-09-28 02:34:02 +020019 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 Poetteringcb7ed9d2012-09-05 23:39:55 -070041#include <sys/capability.h>
Lennart Poettering5008d582010-09-28 02:34:02 +020042
43#include "log.h"
44#include "util.h"
Dave Reisner54693d92012-09-15 12:58:49 -040045#include "macro.h"
Kay Sievers49e942b2012-04-10 21:54:31 +020046#include "mkdir.h"
Kay Sievers9eb977d2012-05-07 21:36:12 +020047#include "path-util.h"
Lennart Poettering5008d582010-09-28 02:34:02 +020048#include "strv.h"
49#include "label.h"
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020050#include "set.h"
Kay Sievers2c210442012-05-07 18:55:45 +020051#include "conf-files.h"
Lennart Poetteringcb7ed9d2012-09-05 23:39:55 -070052#include "capability.h"
Lennart Poettering5008d582010-09-28 02:34:02 +020053
Andreas Jaeger01000472010-09-29 10:08:24 +020054/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
Lennart Poettering5008d582010-09-28 02:34:02 +020055 * them in the file system. This is intended to be used to create
Kay Sieversdb019b82011-04-04 15:33:00 +020056 * properly owned directories beneath /tmp, /var/tmp, /run, which are
57 * volatile and hence need to be recreated on bootup. */
Lennart Poettering5008d582010-09-28 02:34:02 +020058
Michal Schmidt66ccd032011-12-15 21:31:14 +010059typedef enum ItemType {
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010060 /* These ones take file names */
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020061 CREATE_FILE = 'f',
62 TRUNCATE_FILE = 'F',
Lennart Poettering31ed59c2012-01-18 16:39:04 +010063 WRITE_FILE = 'w',
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020064 CREATE_DIRECTORY = 'd',
65 TRUNCATE_DIRECTORY = 'D',
Lennart Poetteringee17ee72011-07-12 03:56:56 +020066 CREATE_FIFO = 'p',
Lennart Poettering468d7262012-01-17 15:04:12 +010067 CREATE_SYMLINK = 'L',
68 CREATE_CHAR_DEVICE = 'c',
69 CREATE_BLOCK_DEVICE = 'b',
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +010070
71 /* These ones take globs */
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020072 IGNORE_PATH = 'x',
Michal Sekletar78a92a52013-01-18 16:13:08 +010073 IGNORE_DIRECTORY_PATH = 'X',
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020074 REMOVE_PATH = 'r',
Michal Schmidta8d88782011-12-15 23:11:07 +010075 RECURSIVE_REMOVE_PATH = 'R',
Michal Schmidt777b87e2011-12-16 18:27:35 +010076 RELABEL_PATH = 'z',
Michal Schmidta8d88782011-12-15 23:11:07 +010077 RECURSIVE_RELABEL_PATH = 'Z'
Michal Schmidt66ccd032011-12-15 21:31:14 +010078} ItemType;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020079
80typedef struct Item {
Michal Schmidt66ccd032011-12-15 21:31:14 +010081 ItemType type;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020082
83 char *path;
Lennart Poettering468d7262012-01-17 15:04:12 +010084 char *argument;
Lennart Poettering5008d582010-09-28 02:34:02 +020085 uid_t uid;
86 gid_t gid;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020087 mode_t mode;
88 usec_t age;
89
Lennart Poettering468d7262012-01-17 15:04:12 +010090 dev_t major_minor;
91
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020092 bool uid_set:1;
93 bool gid_set:1;
94 bool mode_set:1;
95 bool age_set:1;
Lennart Poettering24f3a372012-06-20 09:05:50 +020096
97 bool keep_first_level:1;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +020098} Item;
99
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100100static Hashmap *items = NULL, *globs = NULL;
Lennart Poettering17b90522011-02-14 21:55:06 +0100101static Set *unix_sockets = NULL;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200102
103static bool arg_create = false;
104static bool arg_clean = false;
105static bool arg_remove = false;
106
Lennart Poetteringfba6e682011-02-13 14:00:54 +0100107static const char *arg_prefix = NULL;
108
Lennart Poettering24f3a372012-06-20 09:05:50 +0200109static const char * const conf_file_dirs[] = {
Dave Reisner91256702012-06-08 22:31:19 -0400110 "/etc/tmpfiles.d",
111 "/run/tmpfiles.d",
112 "/usr/local/lib/tmpfiles.d",
113 "/usr/lib/tmpfiles.d",
Lennart Poettering3f2afb22012-07-20 16:24:55 +0200114#ifdef HAVE_SPLIT_USR
115 "/lib/tmpfiles.d",
116#endif
Dave Reisner91256702012-06-08 22:31:19 -0400117 NULL
118};
119
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200120#define MAX_DEPTH 256
121
Michal Schmidt66ccd032011-12-15 21:31:14 +0100122static bool needs_glob(ItemType t) {
Michal Sekletar78a92a52013-01-18 16:13:08 +0100123 return t == IGNORE_PATH || t == IGNORE_DIRECTORY_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100124}
125
126static struct Item* find_glob(Hashmap *h, const char *match) {
127 Item *j;
128 Iterator i;
129
130 HASHMAP_FOREACH(j, h, i)
131 if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
132 return j;
133
134 return NULL;
135}
136
Lennart Poettering17b90522011-02-14 21:55:06 +0100137static void load_unix_sockets(void) {
138 FILE *f = NULL;
139 char line[LINE_MAX];
140
141 if (unix_sockets)
142 return;
143
144 /* We maintain a cache of the sockets we found in
145 * /proc/net/unix to speed things up a little. */
146
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100147 unix_sockets = set_new(string_hash_func, string_compare_func);
148 if (!unix_sockets)
Lennart Poettering17b90522011-02-14 21:55:06 +0100149 return;
150
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100151 f = fopen("/proc/net/unix", "re");
152 if (!f)
Lennart Poettering17b90522011-02-14 21:55:06 +0100153 return;
154
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100155 /* Skip header */
156 if (!fgets(line, sizeof(line), f))
Lennart Poettering17b90522011-02-14 21:55:06 +0100157 goto fail;
158
159 for (;;) {
160 char *p, *s;
161 int k;
162
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100163 if (!fgets(line, sizeof(line), f))
Lennart Poettering17b90522011-02-14 21:55:06 +0100164 break;
165
166 truncate_nl(line);
167
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100168 p = strchr(line, ':');
169 if (!p)
Lennart Poettering17b90522011-02-14 21:55:06 +0100170 continue;
171
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100172 if (strlen(p) < 37)
173 continue;
174
175 p += 37;
Lennart Poettering17b90522011-02-14 21:55:06 +0100176 p += strspn(p, WHITESPACE);
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100177 p += strcspn(p, WHITESPACE); /* skip one more word */
Lennart Poettering17b90522011-02-14 21:55:06 +0100178 p += strspn(p, WHITESPACE);
179
180 if (*p != '/')
181 continue;
182
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100183 s = strdup(p);
184 if (!s)
Lennart Poettering17b90522011-02-14 21:55:06 +0100185 goto fail;
186
Lennart Poettering4ff21d82011-02-17 13:13:34 +0100187 path_kill_slashes(s);
188
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +0100189 k = set_put(unix_sockets, s);
190 if (k < 0) {
Lennart Poettering17b90522011-02-14 21:55:06 +0100191 free(s);
192
193 if (k != -EEXIST)
194 goto fail;
195 }
196 }
197
Thomas Jarosch10d975f2011-10-05 22:30:49 +0200198 fclose(f);
Lennart Poettering17b90522011-02-14 21:55:06 +0100199 return;
200
201fail:
202 set_free_free(unix_sockets);
203 unix_sockets = NULL;
204
205 if (f)
206 fclose(f);
207}
208
209static bool unix_socket_alive(const char *fn) {
210 assert(fn);
211
212 load_unix_sockets();
213
214 if (unix_sockets)
215 return !!set_get(unix_sockets, (char*) fn);
216
217 /* We don't know, so assume yes */
218 return true;
219}
220
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200221static int dir_cleanup(
Michal Sekletar78a92a52013-01-18 16:13:08 +0100222 Item *i,
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200223 const char *p,
224 DIR *d,
225 const struct stat *ds,
226 usec_t cutoff,
227 dev_t rootdev,
228 bool mountpoint,
Lennart Poettering24f3a372012-06-20 09:05:50 +0200229 int maxdepth,
230 bool keep_this_level)
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200231{
232 struct dirent *dent;
233 struct timespec times[2];
234 bool deleted = false;
235 char *sub_path = NULL;
236 int r = 0;
237
238 while ((dent = readdir(d))) {
239 struct stat s;
240 usec_t age;
241
242 if (streq(dent->d_name, ".") ||
243 streq(dent->d_name, ".."))
244 continue;
245
246 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
247
248 if (errno != ENOENT) {
249 log_error("stat(%s/%s) failed: %m", p, dent->d_name);
250 r = -errno;
251 }
252
253 continue;
254 }
255
256 /* Stay on the same filesystem */
257 if (s.st_dev != rootdev)
258 continue;
259
260 /* Do not delete read-only files owned by root */
261 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
262 continue;
263
264 free(sub_path);
265 sub_path = NULL;
266
267 if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
Shawn Landden0d0f0c52012-07-25 14:55:59 -0700268 r = log_oom();
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200269 goto finish;
270 }
271
272 /* Is there an item configured for this path? */
273 if (hashmap_get(items, sub_path))
274 continue;
275
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100276 if (find_glob(globs, sub_path))
277 continue;
278
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200279 if (S_ISDIR(s.st_mode)) {
280
281 if (mountpoint &&
282 streq(dent->d_name, "lost+found") &&
283 s.st_uid == 0)
284 continue;
285
286 if (maxdepth <= 0)
287 log_warning("Reached max depth on %s.", sub_path);
288 else {
289 DIR *sub_dir;
290 int q;
291
Kay Sieverse5f3d1b2012-04-11 21:33:12 +0200292 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200293 if (sub_dir == NULL) {
294 if (errno != ENOENT) {
295 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
296 r = -errno;
297 }
298
299 continue;
300 }
301
Michal Sekletar78a92a52013-01-18 16:13:08 +0100302 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200303 closedir(sub_dir);
304
305 if (q < 0)
306 r = q;
307 }
308
Lennart Poettering24f3a372012-06-20 09:05:50 +0200309 /* Note: if you are wondering why we don't
310 * support the sticky bit for excluding
311 * directories from cleaning like we do it for
312 * other file system objects: well, the sticky
313 * bit already has a meaning for directories,
314 * so we don't want to overload that. */
315
316 if (keep_this_level)
317 continue;
318
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200319 /* Ignore ctime, we change it when deleting */
320 age = MAX(timespec_load(&s.st_mtim),
321 timespec_load(&s.st_atim));
322 if (age >= cutoff)
323 continue;
324
Michal Sekletar78a92a52013-01-18 16:13:08 +0100325 if (!i->type == IGNORE_DIRECTORY_PATH || !streq(dent->d_name, p)) {
326 log_debug("rmdir '%s'\n", sub_path);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200327
Michal Sekletar78a92a52013-01-18 16:13:08 +0100328 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
329 if (errno != ENOENT && errno != ENOTEMPTY) {
330 log_error("rmdir(%s): %m", sub_path);
331 r = -errno;
332 }
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200333 }
334 }
335
336 } else {
Lennart Poettering9c737362010-11-14 20:12:51 +0100337 /* Skip files for which the sticky bit is
338 * set. These are semantics we define, and are
339 * unknown elsewhere. See XDG_RUNTIME_DIR
340 * specification for details. */
341 if (s.st_mode & S_ISVTX)
342 continue;
343
Lennart Poettering17b90522011-02-14 21:55:06 +0100344 if (mountpoint && S_ISREG(s.st_mode)) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200345 if (streq(dent->d_name, ".journal") &&
346 s.st_uid == 0)
347 continue;
348
349 if (streq(dent->d_name, "aquota.user") ||
350 streq(dent->d_name, "aquota.group"))
351 continue;
352 }
353
Lennart Poettering17b90522011-02-14 21:55:06 +0100354 /* Ignore sockets that are listed in /proc/net/unix */
355 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
356 continue;
357
Lennart Poettering78ab08e2011-02-19 14:20:16 +0100358 /* Ignore device nodes */
359 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
360 continue;
361
Lennart Poettering24f3a372012-06-20 09:05:50 +0200362 /* Keep files on this level around if this is
363 * requested */
364 if (keep_this_level)
365 continue;
366
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200367 age = MAX3(timespec_load(&s.st_mtim),
368 timespec_load(&s.st_atim),
369 timespec_load(&s.st_ctim));
370
371 if (age >= cutoff)
372 continue;
373
374 log_debug("unlink '%s'\n", sub_path);
375
376 if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
377 if (errno != ENOENT) {
378 log_error("unlink(%s): %m", sub_path);
379 r = -errno;
380 }
381 }
382
383 deleted = true;
384 }
385 }
386
387finish:
388 if (deleted) {
389 /* Restore original directory timestamps */
390 times[0] = ds->st_atim;
391 times[1] = ds->st_mtim;
392
393 if (futimens(dirfd(d), times) < 0)
394 log_error("utimensat(%s): %m", p);
395 }
396
397 free(sub_path);
398
399 return r;
400}
401
Michal Schmidt062e01b2011-12-16 18:00:11 +0100402static int item_set_perms(Item *i, const char *path) {
403 /* not using i->path directly because it may be a glob */
404 if (i->mode_set)
405 if (chmod(path, i->mode) < 0) {
406 log_error("chmod(%s) failed: %m", path);
407 return -errno;
408 }
409
410 if (i->uid_set || i->gid_set)
411 if (chown(path,
412 i->uid_set ? i->uid : (uid_t) -1,
413 i->gid_set ? i->gid : (gid_t) -1) < 0) {
414
415 log_error("chown(%s) failed: %m", path);
416 return -errno;
417 }
418
Lennart Poetteringc9bc0762012-07-03 16:25:50 +0200419 return label_fix(path, false, false);
Michal Schmidt062e01b2011-12-16 18:00:11 +0100420}
421
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400422static int write_one_file(Item *i, const char *path) {
423 int r, e, fd, flags;
424 struct stat st;
425 mode_t u;
426
427 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
428 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
429
430 u = umask(0);
431 label_context_set(path, S_IFREG);
432 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
433 e = errno;
434 label_context_clear();
435 umask(u);
436 errno = e;
437
438 if (fd < 0) {
439 if (i->type == WRITE_FILE && errno == ENOENT)
440 return 0;
441
442 log_error("Failed to create file %s: %m", path);
443 return -errno;
444 }
445
446 if (i->argument) {
447 ssize_t n;
448 size_t l;
Dave Reisner54693d92012-09-15 12:58:49 -0400449 _cleanup_free_ char *unescaped;
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400450
Dave Reisner54693d92012-09-15 12:58:49 -0400451 unescaped = cunescape(i->argument);
Thomas Jarosch3785ba62012-12-25 13:46:46 +0100452 if (unescaped == NULL) {
453 close_nointr_nofail(fd);
Dave Reisner54693d92012-09-15 12:58:49 -0400454 return log_oom();
Thomas Jarosch3785ba62012-12-25 13:46:46 +0100455 }
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400456
Dave Reisner54693d92012-09-15 12:58:49 -0400457 l = strlen(unescaped);
458 n = write(fd, unescaped, l);
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400459
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400460 if (n < 0 || (size_t) n < l) {
461 log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write");
462 close_nointr_nofail(fd);
463 return n < 0 ? n : -EIO;
464 }
465 }
466
Dave Reisner3612fbc2012-09-12 16:21:00 -0400467 close_nointr_nofail(fd);
468
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400469 if (stat(path, &st) < 0) {
470 log_error("stat(%s) failed: %m", path);
471 return -errno;
472 }
473
474 if (!S_ISREG(st.st_mode)) {
475 log_error("%s is not a file.", path);
476 return -EEXIST;
477 }
478
479 r = item_set_perms(i, path);
480 if (r < 0)
481 return r;
482
483 return 0;
484}
485
Michal Schmidt062e01b2011-12-16 18:00:11 +0100486static int recursive_relabel_children(Item *i, const char *path) {
Michal Schmidta8d88782011-12-15 23:11:07 +0100487 DIR *d;
488 int ret = 0;
489
490 /* This returns the first error we run into, but nevertheless
491 * tries to go on */
492
493 d = opendir(path);
494 if (!d)
495 return errno == ENOENT ? 0 : -errno;
496
497 for (;;) {
Lennart Poettering7d5e9c02012-09-19 22:21:09 +0200498 struct dirent *de;
499 union dirent_storage buf;
Michal Schmidta8d88782011-12-15 23:11:07 +0100500 bool is_dir;
501 int r;
502 char *entry_path;
503
Lennart Poettering7d5e9c02012-09-19 22:21:09 +0200504 r = readdir_r(d, &buf.de, &de);
Michal Schmidta8d88782011-12-15 23:11:07 +0100505 if (r != 0) {
506 if (ret == 0)
507 ret = -r;
508 break;
509 }
510
511 if (!de)
512 break;
513
514 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
515 continue;
516
517 if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
518 if (ret == 0)
519 ret = -ENOMEM;
520 continue;
521 }
522
523 if (de->d_type == DT_UNKNOWN) {
524 struct stat st;
525
526 if (lstat(entry_path, &st) < 0) {
527 if (ret == 0 && errno != ENOENT)
528 ret = -errno;
529 free(entry_path);
530 continue;
531 }
532
533 is_dir = S_ISDIR(st.st_mode);
534
535 } else
536 is_dir = de->d_type == DT_DIR;
537
Michal Schmidt062e01b2011-12-16 18:00:11 +0100538 r = item_set_perms(i, entry_path);
Michal Schmidta8d88782011-12-15 23:11:07 +0100539 if (r < 0) {
540 if (ret == 0 && r != -ENOENT)
541 ret = r;
542 free(entry_path);
543 continue;
544 }
545
546 if (is_dir) {
Michal Schmidt062e01b2011-12-16 18:00:11 +0100547 r = recursive_relabel_children(i, entry_path);
Michal Schmidta8d88782011-12-15 23:11:07 +0100548 if (r < 0 && ret == 0)
549 ret = r;
550 }
551
552 free(entry_path);
553 }
554
555 closedir(d);
556
557 return ret;
558}
559
560static int recursive_relabel(Item *i, const char *path) {
561 int r;
562 struct stat st;
563
Michal Schmidt062e01b2011-12-16 18:00:11 +0100564 r = item_set_perms(i, path);
Michal Schmidta8d88782011-12-15 23:11:07 +0100565 if (r < 0)
566 return r;
567
568 if (lstat(path, &st) < 0)
569 return -errno;
570
571 if (S_ISDIR(st.st_mode))
Michal Schmidt062e01b2011-12-16 18:00:11 +0100572 r = recursive_relabel_children(i, path);
Michal Schmidta8d88782011-12-15 23:11:07 +0100573
574 return r;
575}
576
Michal Schmidt99e68c02011-12-15 23:45:26 +0100577static int glob_item(Item *i, int (*action)(Item *, const char *)) {
578 int r = 0, k;
579 glob_t g;
580 char **fn;
581
582 zero(g);
583
584 errno = 0;
585 if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
586
587 if (k != GLOB_NOMATCH) {
588 if (errno != 0)
589 errno = EIO;
590
591 log_error("glob(%s) failed: %m", i->path);
592 return -errno;
593 }
594 }
595
596 STRV_FOREACH(fn, g.gl_pathv)
597 if ((k = action(i, *fn)) < 0)
598 r = k;
599
600 globfree(&g);
601 return r;
602}
603
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200604static int create_item(Item *i) {
Kay Sieverse9a5ef72012-04-17 16:05:03 +0200605 int r, e;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200606 mode_t u;
607 struct stat st;
608
609 assert(i);
610
611 switch (i->type) {
612
613 case IGNORE_PATH:
Michal Sekletar78a92a52013-01-18 16:13:08 +0100614 case IGNORE_DIRECTORY_PATH:
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200615 case REMOVE_PATH:
616 case RECURSIVE_REMOVE_PATH:
617 return 0;
618
619 case CREATE_FILE:
Lennart Poettering31ed59c2012-01-18 16:39:04 +0100620 case TRUNCATE_FILE:
Dave Reisner1845fdd2012-09-27 20:48:13 -0400621 r = write_one_file(i, i->path);
622 if (r < 0)
623 return r;
624 break;
Dave Reisnerd4e9eb92012-09-03 17:13:18 -0400625 case WRITE_FILE:
626 r = glob_item(i, write_one_file);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100627 if (r < 0)
628 return r;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200629
630 break;
631
632 case TRUNCATE_DIRECTORY:
633 case CREATE_DIRECTORY:
634
635 u = umask(0);
Kay Sieversd2e54fa2012-05-31 12:40:20 +0200636 mkdir_parents_label(i->path, 0755);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200637 r = mkdir(i->path, i->mode);
638 umask(u);
639
640 if (r < 0 && errno != EEXIST) {
641 log_error("Failed to create directory %s: %m", i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100642 return -errno;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200643 }
644
645 if (stat(i->path, &st) < 0) {
646 log_error("stat(%s) failed: %m", i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100647 return -errno;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200648 }
649
650 if (!S_ISDIR(st.st_mode)) {
651 log_error("%s is not a directory.", i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100652 return -EEXIST;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200653 }
654
Michal Schmidt062e01b2011-12-16 18:00:11 +0100655 r = item_set_perms(i, i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100656 if (r < 0)
657 return r;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200658
659 break;
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200660
661 case CREATE_FIFO:
662
663 u = umask(0);
664 r = mkfifo(i->path, i->mode);
665 umask(u);
666
667 if (r < 0 && errno != EEXIST) {
668 log_error("Failed to create fifo %s: %m", i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100669 return -errno;
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200670 }
671
672 if (stat(i->path, &st) < 0) {
673 log_error("stat(%s) failed: %m", i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100674 return -errno;
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200675 }
676
677 if (!S_ISFIFO(st.st_mode)) {
678 log_error("%s is not a fifo.", i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100679 return -EEXIST;
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200680 }
681
Michal Schmidt062e01b2011-12-16 18:00:11 +0100682 r = item_set_perms(i, i->path);
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100683 if (r < 0)
684 return r;
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200685
686 break;
Michal Schmidta8d88782011-12-15 23:11:07 +0100687
Lennart Poettering468d7262012-01-17 15:04:12 +0100688 case CREATE_SYMLINK: {
689 char *x;
690
Kay Sieverse9a5ef72012-04-17 16:05:03 +0200691 label_context_set(i->path, S_IFLNK);
Lennart Poettering468d7262012-01-17 15:04:12 +0100692 r = symlink(i->argument, i->path);
Kay Sieverse9a5ef72012-04-17 16:05:03 +0200693 e = errno;
694 label_context_clear();
695 errno = e;
696
Lennart Poettering468d7262012-01-17 15:04:12 +0100697 if (r < 0 && errno != EEXIST) {
698 log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
699 return -errno;
700 }
701
702 r = readlink_malloc(i->path, &x);
703 if (r < 0) {
704 log_error("readlink(%s) failed: %s", i->path, strerror(-r));
705 return -errno;
706 }
707
708 if (!streq(i->argument, x)) {
709 free(x);
710 log_error("%s is not the right symlinks.", i->path);
711 return -EEXIST;
712 }
713
714 free(x);
715 break;
716 }
717
718 case CREATE_BLOCK_DEVICE:
719 case CREATE_CHAR_DEVICE: {
Lennart Poetteringcb7ed9d2012-09-05 23:39:55 -0700720 mode_t file_type;
721
722 if (have_effective_cap(CAP_MKNOD) == 0) {
723 /* In a container we lack CAP_MKNOD. We
724 shouldnt attempt to create the device node in
725 that case to avoid noise, and we don't support
726 virtualized devices in containers anyway. */
727
728 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
729 return 0;
730 }
731
732 file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
Lennart Poettering468d7262012-01-17 15:04:12 +0100733
734 u = umask(0);
Michal Schmidte7aee752012-06-14 16:01:19 +0200735 label_context_set(i->path, file_type);
736 r = mknod(i->path, i->mode | file_type, i->major_minor);
Kay Sieverse9a5ef72012-04-17 16:05:03 +0200737 e = errno;
738 label_context_clear();
Lennart Poettering468d7262012-01-17 15:04:12 +0100739 umask(u);
Kay Sieverse9a5ef72012-04-17 16:05:03 +0200740 errno = e;
Lennart Poettering468d7262012-01-17 15:04:12 +0100741
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
Michal Schmidte7aee752012-06-14 16:01:19 +0200752 if ((st.st_mode & S_IFMT) != file_type) {
Lennart Poettering468d7262012-01-17 15:04:12 +0100753 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
Michal Schmidt777b87e2011-12-16 18:27:35 +0100764 case RELABEL_PATH:
765
766 r = glob_item(i, item_set_perms);
767 if (r < 0)
768 return 0;
769 break;
770
Michal Schmidta8d88782011-12-15 23:11:07 +0100771 case RECURSIVE_RELABEL_PATH:
772
773 r = glob_item(i, recursive_relabel);
774 if (r < 0)
775 return r;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200776 }
777
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200778 log_debug("%s created successfully.", i->path);
779
Michal Schmidtf05bc3f2011-12-15 23:44:23 +0100780 return 0;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200781}
782
Michal Schmidta0896122011-12-15 21:32:50 +0100783static int remove_item_instance(Item *i, const char *instance) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200784 int r;
785
786 assert(i);
787
788 switch (i->type) {
789
790 case CREATE_FILE:
791 case TRUNCATE_FILE:
792 case CREATE_DIRECTORY:
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200793 case CREATE_FIFO:
Lennart Poettering468d7262012-01-17 15:04:12 +0100794 case CREATE_SYMLINK:
795 case CREATE_BLOCK_DEVICE:
796 case CREATE_CHAR_DEVICE:
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200797 case IGNORE_PATH:
Michal Sekletar78a92a52013-01-18 16:13:08 +0100798 case IGNORE_DIRECTORY_PATH:
Michal Schmidt777b87e2011-12-16 18:27:35 +0100799 case RELABEL_PATH:
Michal Schmidta8d88782011-12-15 23:11:07 +0100800 case RECURSIVE_RELABEL_PATH:
Lennart Poettering31ed59c2012-01-18 16:39:04 +0100801 case WRITE_FILE:
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200802 break;
803
804 case REMOVE_PATH:
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100805 if (remove(instance) < 0 && errno != ENOENT) {
806 log_error("remove(%s): %m", instance);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200807 return -errno;
808 }
809
810 break;
811
812 case TRUNCATE_DIRECTORY:
813 case RECURSIVE_REMOVE_PATH:
Lennart Poetteringd139b242012-06-20 14:31:00 +0200814 /* FIXME: we probably should use dir_cleanup() here
815 * instead of rm_rf() so that 'x' is honoured. */
Lennart Poetteringf56d5db2012-07-10 19:05:58 +0200816 r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
Lennart Poettering468d7262012-01-17 15:04:12 +0100817 if (r < 0 && r != -ENOENT) {
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100818 log_error("rm_rf(%s): %s", instance, strerror(-r));
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200819 return r;
820 }
821
822 break;
823 }
824
825 return 0;
826}
827
Michal Schmidta0896122011-12-15 21:32:50 +0100828static int remove_item(Item *i) {
Michal Schmidt99e68c02011-12-15 23:45:26 +0100829 int r = 0;
830
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100831 assert(i);
832
833 switch (i->type) {
834
835 case CREATE_FILE:
836 case TRUNCATE_FILE:
837 case CREATE_DIRECTORY:
Lennart Poetteringee17ee72011-07-12 03:56:56 +0200838 case CREATE_FIFO:
Lennart Poettering468d7262012-01-17 15:04:12 +0100839 case CREATE_SYMLINK:
840 case CREATE_CHAR_DEVICE:
841 case CREATE_BLOCK_DEVICE:
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100842 case IGNORE_PATH:
Michal Sekletar78a92a52013-01-18 16:13:08 +0100843 case IGNORE_DIRECTORY_PATH:
Michal Schmidt777b87e2011-12-16 18:27:35 +0100844 case RELABEL_PATH:
Michal Schmidta8d88782011-12-15 23:11:07 +0100845 case RECURSIVE_RELABEL_PATH:
Lennart Poettering31ed59c2012-01-18 16:39:04 +0100846 case WRITE_FILE:
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100847 break;
848
849 case REMOVE_PATH:
850 case TRUNCATE_DIRECTORY:
Michal Schmidt99e68c02011-12-15 23:45:26 +0100851 case RECURSIVE_REMOVE_PATH:
852 r = glob_item(i, remove_item_instance);
853 break;
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100854 }
855
Michal Schmidt99e68c02011-12-15 23:45:26 +0100856 return r;
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +0100857}
858
Michal Sekletar78a92a52013-01-18 16:13:08 +0100859static int clean_item_instance(Item *i, const char* instance) {
860 DIR *d;
861 struct stat s, ps;
862 bool mountpoint;
863 int r;
864 usec_t cutoff, n;
865
866 assert(i);
867
868 if (!i->age_set)
869 return 0;
870
871 n = now(CLOCK_REALTIME);
872 if (n < i->age)
873 return 0;
874
875 cutoff = n - i->age;
876
877 d = opendir(instance);
878 if (!d) {
879 if (errno == ENOENT || errno == ENOTDIR)
880 return 0;
881
882 log_error("Failed to open directory %s: %m", i->path);
883 return -errno;
884 }
885
886 if (fstat(dirfd(d), &s) < 0) {
887 log_error("stat(%s) failed: %m", i->path);
888 r = -errno;
889 goto finish;
890 }
891
892 if (!S_ISDIR(s.st_mode)) {
893 log_error("%s is not a directory.", i->path);
894 r = -ENOTDIR;
895 goto finish;
896 }
897
898 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
899 log_error("stat(%s/..) failed: %m", i->path);
900 r = -errno;
901 goto finish;
902 }
903
904 mountpoint = s.st_dev != ps.st_dev ||
905 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
906
907 r = dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH, i->keep_first_level);
908
909finish:
910 if (d)
911 closedir(d);
912
913 return r;
914}
915
916static int clean_item(Item *i) {
917 int r = 0;
918
919 assert(i);
920
921 switch (i->type) {
922 case CREATE_DIRECTORY:
923 case TRUNCATE_DIRECTORY:
924 case IGNORE_PATH:
925 clean_item_instance(i, i->path);
926 break;
927 case IGNORE_DIRECTORY_PATH:
928 r = glob_item(i, clean_item_instance);
929 break;
930 default:
931 break;
932 }
933
934 return r;
935}
936
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200937static int process_item(Item *i) {
938 int r, q, p;
939
940 assert(i);
941
942 r = arg_create ? create_item(i) : 0;
Michal Schmidta0896122011-12-15 21:32:50 +0100943 q = arg_remove ? remove_item(i) : 0;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200944 p = arg_clean ? clean_item(i) : 0;
945
946 if (r < 0)
947 return r;
948
949 if (q < 0)
950 return q;
951
952 return p;
953}
954
955static void item_free(Item *i) {
956 assert(i);
957
958 free(i->path);
Lennart Poettering468d7262012-01-17 15:04:12 +0100959 free(i->argument);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +0200960 free(i);
961}
962
Lennart Poetteringbfe95f32011-04-08 04:49:43 +0200963static bool item_equal(Item *a, Item *b) {
964 assert(a);
965 assert(b);
966
967 if (!streq_ptr(a->path, b->path))
968 return false;
969
970 if (a->type != b->type)
971 return false;
972
973 if (a->uid_set != b->uid_set ||
974 (a->uid_set && a->uid != b->uid))
975 return false;
976
977 if (a->gid_set != b->gid_set ||
978 (a->gid_set && a->gid != b->gid))
979 return false;
980
981 if (a->mode_set != b->mode_set ||
982 (a->mode_set && a->mode != b->mode))
983 return false;
984
985 if (a->age_set != b->age_set ||
986 (a->age_set && a->age != b->age))
987 return false;
988
Lennart Poettering31ed59c2012-01-18 16:39:04 +0100989 if ((a->type == CREATE_FILE ||
990 a->type == TRUNCATE_FILE ||
991 a->type == WRITE_FILE ||
992 a->type == CREATE_SYMLINK) &&
Lennart Poettering1733ca52012-01-22 18:19:24 +0100993 !streq_ptr(a->argument, b->argument))
Lennart Poettering468d7262012-01-17 15:04:12 +0100994 return false;
995
996 if ((a->type == CREATE_CHAR_DEVICE ||
997 a->type == CREATE_BLOCK_DEVICE) &&
998 a->major_minor != b->major_minor)
999 return false;
1000
Lennart Poetteringbfe95f32011-04-08 04:49:43 +02001001 return true;
1002}
1003
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001004static int parse_line(const char *fname, unsigned line, const char *buffer) {
Lennart Poetteringbfe95f32011-04-08 04:49:43 +02001005 Item *i, *existing;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001006 char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
Michal Schmidt66ccd032011-12-15 21:31:14 +01001007 char type;
Lennart Poetteringbfe95f32011-04-08 04:49:43 +02001008 Hashmap *h;
Lennart Poettering31ed59c2012-01-18 16:39:04 +01001009 int r, n = -1;
Lennart Poettering5008d582010-09-28 02:34:02 +02001010
1011 assert(fname);
1012 assert(line >= 1);
1013 assert(buffer);
1014
Lennart Poettering468d7262012-01-17 15:04:12 +01001015 i = new0(Item, 1);
Shawn Landden0d0f0c52012-07-25 14:55:59 -07001016 if (!i)
1017 return log_oom();
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001018
Lennart Poetteringbd40a2d2011-01-22 02:18:59 +01001019 if (sscanf(buffer,
1020 "%c "
1021 "%ms "
1022 "%ms "
1023 "%ms "
1024 "%ms "
Lennart Poettering468d7262012-01-17 15:04:12 +01001025 "%ms "
Lennart Poettering31ed59c2012-01-18 16:39:04 +01001026 "%n",
Michal Schmidt66ccd032011-12-15 21:31:14 +01001027 &type,
Lennart Poetteringbd40a2d2011-01-22 02:18:59 +01001028 &i->path,
1029 &mode,
1030 &user,
1031 &group,
Lennart Poettering468d7262012-01-17 15:04:12 +01001032 &age,
Lennart Poettering31ed59c2012-01-18 16:39:04 +01001033 &n) < 2) {
Lennart Poettering5008d582010-09-28 02:34:02 +02001034 log_error("[%s:%u] Syntax error.", fname, line);
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001035 r = -EIO;
Lennart Poettering5008d582010-09-28 02:34:02 +02001036 goto finish;
1037 }
1038
Lennart Poettering31ed59c2012-01-18 16:39:04 +01001039 if (n >= 0) {
1040 n += strspn(buffer+n, WHITESPACE);
1041 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1042 i->argument = unquote(buffer+n, "\"");
Shawn Landden0d0f0c52012-07-25 14:55:59 -07001043 if (!i->argument)
1044 return log_oom();
Lennart Poettering31ed59c2012-01-18 16:39:04 +01001045 }
1046 }
1047
Michal Schmidt777b87e2011-12-16 18:27:35 +01001048 switch(type) {
Lennart Poettering468d7262012-01-17 15:04:12 +01001049
Michal Schmidt777b87e2011-12-16 18:27:35 +01001050 case CREATE_FILE:
1051 case TRUNCATE_FILE:
1052 case CREATE_DIRECTORY:
1053 case TRUNCATE_DIRECTORY:
1054 case CREATE_FIFO:
1055 case IGNORE_PATH:
Michal Sekletar78a92a52013-01-18 16:13:08 +01001056 case IGNORE_DIRECTORY_PATH:
Michal Schmidt777b87e2011-12-16 18:27:35 +01001057 case REMOVE_PATH:
1058 case RECURSIVE_REMOVE_PATH:
1059 case RELABEL_PATH:
1060 case RECURSIVE_RELABEL_PATH:
1061 break;
Lennart Poettering468d7262012-01-17 15:04:12 +01001062
1063 case CREATE_SYMLINK:
1064 if (!i->argument) {
1065 log_error("[%s:%u] Symlink file requires argument.", fname, line);
1066 r = -EBADMSG;
1067 goto finish;
1068 }
1069 break;
1070
Lennart Poettering31ed59c2012-01-18 16:39:04 +01001071 case WRITE_FILE:
1072 if (!i->argument) {
1073 log_error("[%s:%u] Write file requires argument.", fname, line);
1074 r = -EBADMSG;
1075 goto finish;
1076 }
1077 break;
1078
Lennart Poettering468d7262012-01-17 15:04:12 +01001079 case CREATE_CHAR_DEVICE:
1080 case CREATE_BLOCK_DEVICE: {
1081 unsigned major, minor;
1082
1083 if (!i->argument) {
1084 log_error("[%s:%u] Device file requires argument.", fname, line);
1085 r = -EBADMSG;
1086 goto finish;
1087 }
1088
1089 if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
1090 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
1091 r = -EBADMSG;
1092 goto finish;
1093 }
1094
1095 i->major_minor = makedev(major, minor);
1096 break;
1097 }
1098
Michal Schmidt777b87e2011-12-16 18:27:35 +01001099 default:
Michal Schmidta8d88782011-12-15 23:11:07 +01001100 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001101 r = -EBADMSG;
Lennart Poettering5008d582010-09-28 02:34:02 +02001102 goto finish;
1103 }
Lennart Poettering468d7262012-01-17 15:04:12 +01001104
Michal Schmidta8d88782011-12-15 23:11:07 +01001105 i->type = type;
Lennart Poettering5008d582010-09-28 02:34:02 +02001106
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001107 if (!path_is_absolute(i->path)) {
1108 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
1109 r = -EBADMSG;
1110 goto finish;
1111 }
1112
1113 path_kill_slashes(i->path);
1114
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001115 if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001116 r = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +02001117 goto finish;
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001118 }
Lennart Poettering5008d582010-09-28 02:34:02 +02001119
1120 if (user && !streq(user, "-")) {
Lennart Poettering4b678342011-07-23 01:17:59 +02001121 const char *u = user;
Lennart Poettering5008d582010-09-28 02:34:02 +02001122
Lennart Poetteringd05c5032012-07-16 12:34:54 +02001123 r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
Lennart Poettering4b678342011-07-23 01:17:59 +02001124 if (r < 0) {
Lennart Poettering5008d582010-09-28 02:34:02 +02001125 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1126 goto finish;
1127 }
1128
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001129 i->uid_set = true;
Lennart Poettering5008d582010-09-28 02:34:02 +02001130 }
1131
1132 if (group && !streq(group, "-")) {
Lennart Poettering4b678342011-07-23 01:17:59 +02001133 const char *g = group;
Lennart Poettering5008d582010-09-28 02:34:02 +02001134
Lennart Poettering4b678342011-07-23 01:17:59 +02001135 r = get_group_creds(&g, &i->gid);
1136 if (r < 0) {
Lennart Poettering5008d582010-09-28 02:34:02 +02001137 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1138 goto finish;
1139 }
1140
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001141 i->gid_set = true;
Lennart Poettering5008d582010-09-28 02:34:02 +02001142 }
1143
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001144 if (mode && !streq(mode, "-")) {
1145 unsigned m;
Lennart Poettering5008d582010-09-28 02:34:02 +02001146
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001147 if (sscanf(mode, "%o", &m) != 1) {
1148 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
1149 r = -ENOENT;
Lennart Poettering5008d582010-09-28 02:34:02 +02001150 goto finish;
1151 }
1152
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001153 i->mode = m;
1154 i->mode_set = true;
1155 } else
Lennart Poettering468d7262012-01-17 15:04:12 +01001156 i->mode =
1157 i->type == CREATE_DIRECTORY ||
1158 i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001159
1160 if (age && !streq(age, "-")) {
Lennart Poettering24f3a372012-06-20 09:05:50 +02001161 const char *a = age;
1162
1163 if (*a == '~') {
1164 i->keep_first_level = true;
1165 a++;
1166 }
1167
1168 if (parse_usec(a, &i->age) < 0) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001169 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
1170 r = -EBADMSG;
Lennart Poettering5008d582010-09-28 02:34:02 +02001171 goto finish;
1172 }
1173
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001174 i->age_set = true;
Lennart Poettering5008d582010-09-28 02:34:02 +02001175 }
1176
Lennart Poetteringbfe95f32011-04-08 04:49:43 +02001177 h = needs_glob(i->type) ? globs : items;
Lennart Poettering022707d2011-01-05 16:11:15 +01001178
Lennart Poettering468d7262012-01-17 15:04:12 +01001179 existing = hashmap_get(h, i->path);
1180 if (existing) {
Lennart Poetteringbfe95f32011-04-08 04:49:43 +02001181
1182 /* Two identical items are fine */
1183 if (!item_equal(existing, i))
1184 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
1185
1186 r = 0;
1187 goto finish;
1188 }
1189
Lennart Poettering468d7262012-01-17 15:04:12 +01001190 r = hashmap_put(h, i->path, i);
1191 if (r < 0) {
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001192 log_error("Failed to insert item %s: %s", i->path, strerror(-r));
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001193 goto finish;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001194 }
Lennart Poettering5008d582010-09-28 02:34:02 +02001195
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001196 i = NULL;
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001197 r = 0;
Lennart Poettering5008d582010-09-28 02:34:02 +02001198
1199finish:
Lennart Poettering5008d582010-09-28 02:34:02 +02001200 free(user);
1201 free(group);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001202 free(mode);
1203 free(age);
Lennart Poettering5008d582010-09-28 02:34:02 +02001204
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001205 if (i)
1206 item_free(i);
Lennart Poettering4aa8b152010-09-28 22:32:05 +02001207
1208 return r;
Lennart Poettering5008d582010-09-28 02:34:02 +02001209}
1210
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001211static int help(void) {
1212
Lennart Poettering522d4a42011-02-13 15:08:15 +01001213 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1214 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001215 " -h --help Show this help\n"
1216 " --create Create marked files/directories\n"
1217 " --clean Clean up marked directories\n"
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001218 " --remove Remove marked files/directories\n"
Lennart Poettering522d4a42011-02-13 15:08:15 +01001219 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001220 program_invocation_short_name);
1221
1222 return 0;
1223}
1224
1225static int parse_argv(int argc, char *argv[]) {
1226
1227 enum {
1228 ARG_CREATE,
1229 ARG_CLEAN,
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001230 ARG_REMOVE,
1231 ARG_PREFIX
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001232 };
1233
1234 static const struct option options[] = {
1235 { "help", no_argument, NULL, 'h' },
1236 { "create", no_argument, NULL, ARG_CREATE },
1237 { "clean", no_argument, NULL, ARG_CLEAN },
1238 { "remove", no_argument, NULL, ARG_REMOVE },
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001239 { "prefix", required_argument, NULL, ARG_PREFIX },
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001240 { NULL, 0, NULL, 0 }
1241 };
1242
1243 int c;
1244
1245 assert(argc >= 0);
1246 assert(argv);
1247
1248 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1249
1250 switch (c) {
1251
1252 case 'h':
1253 help();
1254 return 0;
1255
1256 case ARG_CREATE:
1257 arg_create = true;
1258 break;
1259
1260 case ARG_CLEAN:
1261 arg_clean = true;
1262 break;
1263
1264 case ARG_REMOVE:
1265 arg_remove = true;
1266 break;
1267
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001268 case ARG_PREFIX:
1269 arg_prefix = optarg;
1270 break;
1271
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001272 case '?':
1273 return -EINVAL;
1274
1275 default:
1276 log_error("Unknown option code %c", c);
1277 return -EINVAL;
1278 }
1279 }
1280
1281 if (!arg_clean && !arg_create && !arg_remove) {
Harald Hoyer35b8ca32011-02-21 15:32:17 +01001282 log_error("You need to specify at least one of --clean, --create or --remove.");
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001283 return -EINVAL;
1284 }
1285
1286 return 1;
1287}
1288
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001289static int read_config_file(const char *fn, bool ignore_enoent) {
1290 FILE *f;
1291 unsigned v = 0;
1292 int r = 0;
Michal Sekletar78a92a52013-01-18 16:13:08 +01001293 Iterator iterator;
1294 Item *i;
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001295
1296 assert(fn);
1297
Lennart Poettering468d7262012-01-17 15:04:12 +01001298 f = fopen(fn, "re");
1299 if (!f) {
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001300
1301 if (ignore_enoent && errno == ENOENT)
1302 return 0;
1303
1304 log_error("Failed to open %s: %m", fn);
1305 return -errno;
1306 }
1307
Kay Sievers772f8372011-04-25 21:38:21 +02001308 log_debug("apply: %s\n", fn);
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001309 for (;;) {
1310 char line[LINE_MAX], *l;
1311 int k;
1312
1313 if (!(fgets(line, sizeof(line), f)))
1314 break;
1315
1316 v++;
1317
1318 l = strstrip(line);
1319 if (*l == '#' || *l == 0)
1320 continue;
1321
1322 if ((k = parse_line(fn, v, l)) < 0)
1323 if (r == 0)
1324 r = k;
1325 }
1326
Michal Sekletar78a92a52013-01-18 16:13:08 +01001327 /* we have to determine age parameter for each entry of type X */
1328 HASHMAP_FOREACH(i, globs, iterator) {
1329 Iterator iter;
1330 Item *j, *candidate_item = NULL;
1331
1332 if (i->type != IGNORE_DIRECTORY_PATH)
1333 continue;
1334
1335 HASHMAP_FOREACH(j, items, iter) {
1336 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
1337 continue;
1338
1339 if (path_equal(j->path, i->path)) {
1340 candidate_item = j;
1341 break;
1342 }
1343
1344 if ((!candidate_item && path_startswith(i->path, j->path)) ||
1345 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
1346 candidate_item = j;
1347 }
1348
1349 if (candidate_item) {
1350 i->age = candidate_item->age;
1351 i->age_set = true;
1352 }
1353 }
1354
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001355 if (ferror(f)) {
1356 log_error("Failed to read from file %s: %m", fn);
1357 if (r == 0)
1358 r = -EIO;
1359 }
1360
1361 fclose(f);
1362
1363 return r;
1364}
1365
Dave Reisner91256702012-06-08 22:31:19 -04001366static char *resolve_fragment(const char *fragment, const char **search_paths) {
1367 const char **p;
1368 char *resolved_path;
1369
1370 if (is_path(fragment))
1371 return strdup(fragment);
1372
1373 STRV_FOREACH(p, search_paths) {
Lennart Poetteringb7def682012-07-13 13:41:01 +02001374 resolved_path = strjoin(*p, "/", fragment, NULL);
Dave Reisner91256702012-06-08 22:31:19 -04001375 if (resolved_path == NULL) {
Shawn Landden0d0f0c52012-07-25 14:55:59 -07001376 log_oom();
Dave Reisner91256702012-06-08 22:31:19 -04001377 return NULL;
1378 }
1379
1380 if (access(resolved_path, F_OK) == 0)
1381 return resolved_path;
1382
1383 free(resolved_path);
1384 }
1385
Kay Sieversca2e8942012-06-10 19:21:50 +02001386 errno = ENOENT;
Dave Reisner91256702012-06-08 22:31:19 -04001387 return NULL;
1388}
1389
Lennart Poettering5008d582010-09-28 02:34:02 +02001390int main(int argc, char *argv[]) {
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001391 int r;
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001392 Item *i;
1393 Iterator iterator;
Lennart Poettering5008d582010-09-28 02:34:02 +02001394
Lennart Poetteringfdcad0c2012-01-11 22:07:35 +01001395 r = parse_argv(argc, argv);
1396 if (r <= 0)
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001397 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1398
Lennart Poetteringeb0ca9e2011-02-12 09:31:38 +01001399 log_set_target(LOG_TARGET_AUTO);
Lennart Poettering5008d582010-09-28 02:34:02 +02001400 log_parse_environment();
1401 log_open();
1402
Lennart Poettering4c126262011-08-01 20:52:18 +02001403 umask(0022);
1404
Kay Sieverse9a5ef72012-04-17 16:05:03 +02001405 label_init(NULL);
Lennart Poettering5008d582010-09-28 02:34:02 +02001406
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +01001407 items = hashmap_new(string_hash_func, string_compare_func);
1408 globs = hashmap_new(string_hash_func, string_compare_func);
1409
1410 if (!items || !globs) {
Shawn Landden0d0f0c52012-07-25 14:55:59 -07001411 log_oom();
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001412 r = EXIT_FAILURE;
1413 goto finish;
1414 }
1415
Lennart Poettering5008d582010-09-28 02:34:02 +02001416 r = EXIT_SUCCESS;
1417
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001418 if (optind < argc) {
1419 int j;
Lennart Poettering5008d582010-09-28 02:34:02 +02001420
Dave Reisner91256702012-06-08 22:31:19 -04001421 for (j = optind; j < argc; j++) {
Kay Sieversca2e8942012-06-10 19:21:50 +02001422 char *fragment;
1423
Kay Sievers796b06c2012-10-22 18:23:08 +02001424 fragment = resolve_fragment(argv[j], (const char **)conf_file_dirs);
Kay Sieversca2e8942012-06-10 19:21:50 +02001425 if (!fragment) {
Kay Sievers94f7a712012-06-10 19:31:39 +02001426 log_error("Failed to find a %s file: %m", argv[j]);
Kay Sieversca2e8942012-06-10 19:21:50 +02001427 r = EXIT_FAILURE;
1428 goto finish;
1429 }
Dave Reisner91256702012-06-08 22:31:19 -04001430 if (read_config_file(fragment, false) < 0)
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001431 r = EXIT_FAILURE;
Dave Reisner91256702012-06-08 22:31:19 -04001432 free(fragment);
1433 }
Lennart Poettering5008d582010-09-28 02:34:02 +02001434
Lennart Poetteringfba6e682011-02-13 14:00:54 +01001435 } else {
Kay Sievers772f8372011-04-25 21:38:21 +02001436 char **files, **f;
Lennart Poettering5008d582010-09-28 02:34:02 +02001437
Kay Sievers7850b3b2013-02-08 10:22:02 +01001438 r = conf_files_list_strv(&files, ".conf", NULL, (const char **)conf_file_dirs);
Kay Sievers44143302011-04-28 23:51:24 +02001439 if (r < 0) {
Kay Sievers44143302011-04-28 23:51:24 +02001440 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
Michal Schmidta48f3d12012-04-17 22:53:35 +02001441 r = EXIT_FAILURE;
Kay Sievers44143302011-04-28 23:51:24 +02001442 goto finish;
1443 }
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001444
Kay Sievers772f8372011-04-25 21:38:21 +02001445 STRV_FOREACH(f, files) {
1446 if (read_config_file(*f, true) < 0)
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001447 r = EXIT_FAILURE;
Lennart Poettering5008d582010-09-28 02:34:02 +02001448 }
1449
Kay Sievers772f8372011-04-25 21:38:21 +02001450 strv_free(files);
Lennart Poettering5008d582010-09-28 02:34:02 +02001451 }
1452
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +01001453 HASHMAP_FOREACH(i, globs, iterator)
Lennart Poettering21bdae12011-07-02 01:44:49 +02001454 process_item(i);
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +01001455
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001456 HASHMAP_FOREACH(i, items, iterator)
Lennart Poettering21bdae12011-07-02 01:44:49 +02001457 process_item(i);
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001458
Lennart Poettering5008d582010-09-28 02:34:02 +02001459finish:
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001460 while ((i = hashmap_steal_first(items)))
1461 item_free(i);
1462
Lennart Poettering17b90522011-02-14 21:55:06 +01001463 while ((i = hashmap_steal_first(globs)))
1464 item_free(i);
1465
Lennart Poettering3b63d2d2010-10-18 22:38:41 +02001466 hashmap_free(items);
Lennart Poetteringb8bb3e82011-02-12 09:31:25 +01001467 hashmap_free(globs);
Lennart Poettering5008d582010-09-28 02:34:02 +02001468
Lennart Poettering17b90522011-02-14 21:55:06 +01001469 set_free_free(unix_sockets);
1470
Lennart Poettering29003cf2010-10-19 19:36:45 +02001471 label_finish();
1472
Lennart Poettering5008d582010-09-28 02:34:02 +02001473 return r;
1474}