From: Lennart Poettering Date: Sat, 12 Feb 2011 08:31:25 +0000 (+0100) Subject: tmpfiles: support globs X-Git-Tag: v18~39 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=b8bb3e8f346468e61dcc7a6aba5e7ac9c623d964;hp=72f576bdd6a292bcc1b82145c181cf8244e75aef;ds=sidebyside tmpfiles: support globs --- diff --git a/TODO b/TODO index 66da130f9..8aac19b15 100644 --- a/TODO +++ b/TODO @@ -9,6 +9,8 @@ Bugs: Features: +* perhaps add "systemctl reenable" as combination of "systemctl disable" and "systemctl enable" + * tty name lock for password agent is broken, since it will always lock "/dev/tty" since we now reattach the agent process when forking it off systemctl * need a way to apply mount options of api vfs from systemd unit files instead of fstab @@ -50,8 +52,6 @@ Features: * load EnvironmentFile= when starting services, not when reloading configuration https://bugzilla.redhat.com/show_bug.cgi?id=661282 -* support globs in systemd-tmpfiles - * drop IN_ATTRIB from inotify watches for .path units where possible to avoid lots of wakeups due to /dev changing when we watch a subdir of /dev. diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index e6b4c1bfa..e211d6ded 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -54,11 +54,11 @@ systemd uses /etc/tmpfiles.d/ to describe the - creation, cleaning and removal of temporary files and - directories which usually reside in - /var/run or - /tmp). Each configuration file - is named in the style of + creation, cleaning and removal of volatile files and + directories which usually reside in directories such + as /var/run or + /tmp. Each configuration file is + named in the style of /etc/tmpfiles.d/<program>.conf. @@ -97,17 +97,41 @@ d /var/run/user 0755 root root 10d x - Ignore a path + Ignore a path + during cleaning. Use this type + to exclude paths from clean-up + as controlled with the Age + parameter. Note that lines of + this type do not influence the + effect of r or R lines. Lines + of this type accept + shell-style globs in place of + of normal path + names. r - Remove a path + Remove a file + or directory if it + exists. This may not be used + to remove non-empty + directories, use R for + that. Lines of this type + accept shell-style globs in + place of normal path + names. R - Recursively remove a path + Recursively + remove a path and all its + subdirectories (if it is a + directory). Lines of this type + accept shell-style globs in + place of normal path + names. @@ -115,10 +139,11 @@ d /var/run/user 0755 root root 10d Mode - The file access mode to use for this - file or directory. If omitted or when set to - - the default is used: 0755 for directories, - 0644 for files. + The file access mode to use when + creating this file or directory. If omitted or + when set to - the default is used: 0755 for + directories, 0644 for files. This parameter is + ignored for x, r, R lines. @@ -127,8 +152,9 @@ d /var/run/user 0755 root root 10d The user and group to use for this file or directory. This may either be a numeric user/group ID or a user or group name. If - omitted or when set to - the default 0 is - used. + omitted or when set to - the default 0 (root) + is used. . These parameters are ignored for x, + r, R lines. diff --git a/src/tmpfiles.c b/src/tmpfiles.c index d242dac7d..3dabe4692 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "log.h" #include "util.h" @@ -50,10 +52,13 @@ * bootup. */ enum { + /* These ones take file names */ CREATE_FILE = 'f', TRUNCATE_FILE = 'F', CREATE_DIRECTORY = 'd', TRUNCATE_DIRECTORY = 'D', + + /* These ones take globs */ IGNORE_PATH = 'x', REMOVE_PATH = 'r', RECURSIVE_REMOVE_PATH = 'R' @@ -74,7 +79,7 @@ typedef struct Item { bool age_set:1; } Item; -static Hashmap *items = NULL; +static Hashmap *items = NULL, *globs = NULL; static bool arg_create = false; static bool arg_clean = false; @@ -82,6 +87,21 @@ static bool arg_remove = false; #define MAX_DEPTH 256 +static bool needs_glob(int t) { + return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH; +} + +static struct Item* find_glob(Hashmap *h, const char *match) { + Item *j; + Iterator i; + + HASHMAP_FOREACH(j, h, i) + if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) + return j; + + return NULL; +} + static int dir_cleanup( const char *p, DIR *d, @@ -136,6 +156,9 @@ static int dir_cleanup( if (hashmap_get(items, sub_path)) continue; + if (find_glob(globs, sub_path)) + continue; + if (S_ISDIR(s.st_mode)) { if (mountpoint && @@ -411,7 +434,7 @@ finish: return r; } -static int remove_item(Item *i) { +static int remove_item(Item *i, const char *instance) { int r; assert(i); @@ -425,8 +448,8 @@ static int remove_item(Item *i) { break; case REMOVE_PATH: - if (remove(i->path) < 0 && errno != ENOENT) { - log_error("remove(%s): %m", i->path); + if (remove(instance) < 0 && errno != ENOENT) { + log_error("remove(%s): %m", instance); return -errno; } @@ -434,9 +457,9 @@ static int remove_item(Item *i) { case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - if ((r = rm_rf(i->path, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 && + if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 && r != -ENOENT) { - log_error("rm_rf(%s): %s", i->path, strerror(-r)); + log_error("rm_rf(%s): %s", instance, strerror(-r)); return r; } @@ -446,13 +469,57 @@ static int remove_item(Item *i) { return 0; } +static int remove_item_glob(Item *i) { + assert(i); + + switch (i->type) { + + case CREATE_FILE: + case TRUNCATE_FILE: + case CREATE_DIRECTORY: + case IGNORE_PATH: + break; + + case REMOVE_PATH: + case TRUNCATE_DIRECTORY: + case RECURSIVE_REMOVE_PATH: { + int r = 0, k; + glob_t g; + char **fn; + + zero(g); + + errno = 0; + if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) { + + if (k != GLOB_NOMATCH) { + if (errno != 0) + errno = EIO; + + log_error("glob(%s) failed: %m", i->path); + return -errno; + } + } + + STRV_FOREACH(fn, g.gl_pathv) + if ((k = remove_item(i, *fn)) < 0) + r = k; + + globfree(&g); + return r; + } + } + + return 0; +} + static int process_item(Item *i) { int r, q, p; assert(i); r = arg_create ? create_item(i) : 0; - q = arg_remove ? remove_item(i) : 0; + q = arg_remove ? remove_item_glob(i) : 0; p = arg_clean ? clean_item(i) : 0; if (r < 0) @@ -590,7 +657,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, cons i->age_set = true; } - if ((r = hashmap_put(items, i->path, i)) < 0) { + if ((r = hashmap_put(needs_glob(i->type) ? globs : items, i->path, i)) < 0) { if (r == -EEXIST) { log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path); r = 0; @@ -721,7 +788,10 @@ int main(int argc, char *argv[]) { label_init(); - if (!(items = hashmap_new(string_hash_func, string_compare_func))) { + items = hashmap_new(string_hash_func, string_compare_func); + globs = hashmap_new(string_hash_func, string_compare_func); + + if (!items || !globs) { log_error("Out of memory"); r = EXIT_FAILURE; goto finish; @@ -796,6 +866,10 @@ int main(int argc, char *argv[]) { free(de); + HASHMAP_FOREACH(i, globs, iterator) + if (process_item(i) < 0) + r = EXIT_FAILURE; + HASHMAP_FOREACH(i, items, iterator) if (process_item(i) < 0) r = EXIT_FAILURE; @@ -805,6 +879,7 @@ finish: item_free(i); hashmap_free(items); + hashmap_free(globs); label_finish();