tmpfiles.c revision bb29785e0df6a7cf07db0259a60bc1f3b4814cb4
3488e51e244adfc756837287fbfbcc03eca8bf7avboxsync/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync This file is part of systemd.
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync Copyright 2010 Lennart Poettering, Kay Sievers
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync systemd is free software; you can redistribute it and/or modify it
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync under the terms of the GNU General Public License as published by
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync the Free Software Foundation; either version 2 of the License, or
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync (at your option) any later version.
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync systemd is distributed in the hope that it will be useful, but
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync WITHOUT ANY WARRANTY; without even the implied warranty of
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync General Public License for more details.
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync You should have received a copy of the GNU General Public License
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync along with systemd; If not, see <http://www.gnu.org/licenses/>.
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
7e77c8f54449be6f28ccbf32f7e651554d9f4060vboxsync * them in the file system. This is intended to be used to create
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync * properly owned directories beneath /tmp, /var/tmp, /run and
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync * /var/lock which are volatile and hence need to be recreated on
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync * bootup. */
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync /* These ones take file names */
bcb837ec8269b7ed260be79053941b4c39c2b59evboxsync /* These ones take globs */
83ee0b9fd6aa227bf4276f9d41af7ef59c03e8eevboxsynctypedef struct Item {
08640b1dc58f26140fca6525ced3dbdef4ce45f7vboxsyncstatic bool arg_create = false;
770da3dbb247278c98d1b21d2e11a0a7769131a4vboxsyncstatic bool arg_clean = false;
085bc29163eb87e345acaae02789e4c233d51f3bvboxsyncstatic bool arg_remove = false;
346af0930020342df40a1ca8d13eb185ad48067evboxsyncstatic bool needs_glob(int t) {
346af0930020342df40a1ca8d13eb185ad48067evboxsync return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH;
346af0930020342df40a1ca8d13eb185ad48067evboxsyncstatic struct Item* find_glob(Hashmap *h, const char *match) {
346af0930020342df40a1ca8d13eb185ad48067evboxsync if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
346af0930020342df40a1ca8d13eb185ad48067evboxsyncstatic void load_unix_sockets(void) {
346af0930020342df40a1ca8d13eb185ad48067evboxsync /* We maintain a cache of the sockets we found in
346af0930020342df40a1ca8d13eb185ad48067evboxsync * /proc/net/unix to speed things up a little. */
346af0930020342df40a1ca8d13eb185ad48067evboxsync if (!(unix_sockets = set_new(string_hash_func, string_compare_func)))
346af0930020342df40a1ca8d13eb185ad48067evboxsync char *p, *s;
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync if (*p != '/')
346af0930020342df40a1ca8d13eb185ad48067evboxsync if (!(s = strdup(p)))
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync /* We don't know, so assume yes */
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync return true;
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync const char *p,
cab65d7f7f5418e157d6e02eb812d1cb43bce2f5vboxsync bool deleted = false;
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync log_error("stat(%s/%s) failed: %m", p, dent->d_name);
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync /* Stay on the same filesystem */
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync /* Do not delete read-only files owned by root */
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
de3518a42830a03b8ae77186324815046435ab64vboxsync /* Is there an item configured for this path? */
de3518a42830a03b8ae77186324815046435ab64vboxsync sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW);
de3518a42830a03b8ae77186324815046435ab64vboxsync log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
de3518a42830a03b8ae77186324815046435ab64vboxsync q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
de3518a42830a03b8ae77186324815046435ab64vboxsync /* Ignore ctime, we change it when deleting */
579bade2e306536e128d4c10a9d90e9a30cb995cvboxsync if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync /* Skip files for which the sticky bit is
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync * set. These are semantics we define, and are
d9d070cfd2c99624fe6974842b7ad4a30d5b71e7vboxsync * unknown elsewhere. See XDG_RUNTIME_DIR
04e845ee9ef813501cd2570a4188cb852d170408vboxsync * specification for details. */
9161d9a8318db73b2848c1feaef3880980474e64vboxsync /* Ignore sockets that are listed in /proc/net/unix */
af62929dce3cc5e14c75438cd2b893f82ae6dbc8vboxsync if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
af62929dce3cc5e14c75438cd2b893f82ae6dbc8vboxsync /* Ignore device nodes */
6ee54d76da1437d7dcaf42a0a359de75762fa8bevboxsync /* Restore original directory timestamps */
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync if (n < i->age)
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync log_error("Failed to open directory %s: %m", i->path);
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync switch (i->type) {
0019a5195e700f7380e64717fcb10e1ce0fcfd91vboxsync fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW|
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync (i->type == TRUNCATE_FILE ? O_TRUNC : 0), i->mode);
0019a5195e700f7380e64717fcb10e1ce0fcfd91vboxsync log_error("Failed to create file %s: %m", i->path);
c31578d36637d7ab03bf8d8e9db2ab056ca7e692vboxsync log_error("Failed to create directory %s: %m", i->path);
346af0930020342df40a1ca8d13eb185ad48067evboxsyncstatic int remove_item(Item *i, const char *instance) {
346af0930020342df40a1ca8d13eb185ad48067evboxsync switch (i->type) {
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 &&
346af0930020342df40a1ca8d13eb185ad48067evboxsync log_error("rm_rf(%s): %s", instance, strerror(-r));
346af0930020342df40a1ca8d13eb185ad48067evboxsync switch (i->type) {
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync int r = 0, k;
346af0930020342df40a1ca8d13eb185ad48067evboxsync if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
0229ec87789aab83ed0595b9ad5151351778e2cfvboxsync int r, q, p;
0de1998ac52682bb5322df476e45f237265ea9b7vboxsyncstatic int parse_line(const char *fname, unsigned line, const char *buffer) {
3933885bc0c2c93436d858a14564c6179ec72872vboxsync char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
1ffcba1fe37addee8c8dad3d0f38e84370dcaad3vboxsync log_error("[%s:%u] Unknown file type '%c'.", fname, line, i->type);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync unsigned long lu;
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync unsigned long lu;
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync unsigned m;
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync i->mode = i->type == CREATE_DIRECTORY ? 0755 : 0644;
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync if ((r = hashmap_put(needs_glob(i->type) ? globs : items, i->path, i)) < 0) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync if (r == -EEXIST) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_error("Failed to insert item %s: %s", i->path, strerror(-r));
0de1998ac52682bb5322df476e45f237265ea9b7vboxsyncstatic int help(void) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync " -h --help Show this help\n"
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync " --create Create marked files/directories\n"
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync " --clean Clean up marked directories\n"
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync " --remove Remove marked files/directories\n"
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync switch (c) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync log_error("You need to specify at least one of --clean, --create or --remove.");
0de1998ac52682bb5322df476e45f237265ea9b7vboxsyncstatic int read_config_file(const char *fn, bool ignore_enoent) {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync unsigned v = 0;
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync if (*l == '#' || *l == 0)
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync if (r == 0)
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync if (r == 0)
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync items = hashmap_new(string_hash_func, string_compare_func);
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync globs = hashmap_new(string_hash_func, string_compare_func);
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync if ((n = scandir("/etc/tmpfiles.d/", &de, scandir_filter, alphasort)) < 0) {
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync log_error("Failed to enumerate /etc/tmpfiles.d/ files: %m");
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync for (j = 0; j < n; j++) {
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync k = asprintf(&fn, "/etc/tmpfiles.d/%s", de[j]->d_name);
83fd17a3a00dc7bf6a36e23bbd2393dfc953da06vboxsync if (k < 0) {