tmpfiles.c revision b8bb3e8f346468e61dcc7a6aba5e7ac9c623d964
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering, Kay Sievers
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <getopt.h>
#include <stdbool.h>
#include <time.h>
#include <glob.h>
#include <fnmatch.h>
#include "log.h"
#include "util.h"
#include "strv.h"
#include "label.h"
#include "set.h"
/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
* them in the file system. This is intended to be used to create
* 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'
};
typedef struct Item {
char type;
char *path;
bool uid_set:1;
bool gid_set:1;
bool mode_set:1;
bool age_set:1;
} Item;
static bool arg_create = false;
static bool arg_clean = false;
static bool arg_remove = false;
#define MAX_DEPTH 256
static bool needs_glob(int t) {
}
Item *j;
Iterator i;
HASHMAP_FOREACH(j, h, i)
return j;
return NULL;
}
static int dir_cleanup(
const char *p,
DIR *d,
bool mountpoint,
int maxdepth)
{
bool deleted = false;
int r = 0;
struct stat s;
continue;
r = -errno;
}
continue;
}
/* Stay on the same filesystem */
continue;
/* Do not delete read-only files owned by root */
continue;
log_error("Out of memory");
r = -ENOMEM;
goto finish;
}
/* Is there an item configured for this path? */
continue;
continue;
if (mountpoint &&
s.st_uid == 0)
continue;
if (maxdepth <= 0)
else {
int q;
r = -errno;
}
continue;
}
if (q < 0)
r = q;
}
/* Ignore ctime, we change it when deleting */
timespec_load(&s.st_atim));
continue;
r = -errno;
}
}
} else {
/* Skip files for which the sticky bit is
* set. These are semantics we define, and are
* unknown elsewhere. See XDG_RUNTIME_DIR
* specification for details. */
continue;
if (mountpoint) {
s.st_uid == 0)
continue;
continue;
}
timespec_load(&s.st_atim),
timespec_load(&s.st_ctim));
continue;
r = -errno;
}
}
deleted = true;
}
}
if (deleted) {
/* Restore original directory timestamps */
log_error("utimensat(%s): %m", p);
}
return r;
}
static int clean_item(Item *i) {
DIR *d;
bool mountpoint;
int r;
assert(i);
if (i->type != CREATE_DIRECTORY &&
i->type != TRUNCATE_DIRECTORY &&
i->type != IGNORE_PATH)
return 0;
return 0;
n = now(CLOCK_REALTIME);
if (n < i->age)
return 0;
if (!d) {
return 0;
return -errno;
}
r = -errno;
goto finish;
}
r = -ENOTDIR;
goto finish;
}
r = -errno;
goto finish;
}
if (d)
closedir(d);
return r;
}
static int create_item(Item *i) {
int fd = -1, r;
mode_t u;
assert(i);
switch (i->type) {
case IGNORE_PATH:
case REMOVE_PATH:
case RECURSIVE_REMOVE_PATH:
return 0;
case CREATE_FILE:
case TRUNCATE_FILE:
u = umask(0);
umask(u);
if (fd < 0) {
r = -errno;
goto finish;
}
r = -errno;
goto finish;
}
r = -EEXIST;
goto finish;
}
if (i->mode_set)
r = -errno;
goto finish;
}
r = -errno;
goto finish;
}
break;
case TRUNCATE_DIRECTORY:
case CREATE_DIRECTORY:
u = umask(0);
umask(u);
r = -errno;
goto finish;
}
r = -errno;
goto finish;
}
r = -EEXIST;
goto finish;
}
if (i->mode_set)
r = -errno;
goto finish;
}
r = -errno;
goto finish;
}
break;
}
goto finish;
if (fd >= 0)
return r;
}
int r;
assert(i);
switch (i->type) {
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
case IGNORE_PATH:
break;
case REMOVE_PATH:
return -errno;
}
break;
case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
r != -ENOENT) {
return r;
}
break;
}
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_NOMATCH) {
if (errno != 0)
return -errno;
}
}
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_glob(i) : 0;
p = arg_clean ? clean_item(i) : 0;
if (r < 0)
return r;
if (q < 0)
return q;
return p;
}
assert(i);
free(i);
}
Item *i;
int r;
log_error("Out of memory");
return -ENOMEM;
}
"%c "
"%ms "
"%ms "
"%ms "
"%ms "
"%ms",
&i->type,
&i->path,
&mode,
&user,
&group,
&age) < 2) {
r = -EIO;
goto finish;
}
if (i->type != CREATE_FILE &&
i->type != TRUNCATE_FILE &&
i->type != CREATE_DIRECTORY &&
i->type != TRUNCATE_DIRECTORY &&
i->type != IGNORE_PATH &&
i->type != REMOVE_PATH &&
i->type != RECURSIVE_REMOVE_PATH) {
r = -EBADMSG;
goto finish;
}
if (!path_is_absolute(i->path)) {
r = -EBADMSG;
goto finish;
}
path_kill_slashes(i->path);
r = 0;
goto finish;
}
unsigned long lu;
struct passwd *p;
i->uid = 0;
else {
r = -ENOENT;
goto finish;
}
i->uid_set = true;
}
unsigned long lu;
struct group *g;
i->gid = 0;
else {
r = -ENOENT;
goto finish;
}
i->gid_set = true;
}
unsigned m;
r = -ENOENT;
goto finish;
}
i->mode = m;
i->mode_set = true;
} else
r = -EBADMSG;
goto finish;
}
i->age_set = true;
}
if (r == -EEXIST) {
r = 0;
goto finish;
}
goto finish;
}
i = NULL;
r = 0;
if (i)
item_free(i);
return r;
}
static int scandir_filter(const struct dirent *d) {
assert(d);
if (ignore_file(d->d_name))
return 0;
return 0;
}
static int help(void) {
printf("%s [OPTIONS...]\n\n"
"Create and clean up temporary directories.\n\n"
" -h --help Show this help\n"
" --create Create marked files/directories\n"
" --clean Clean up marked directories\n"
" --remove Remove marked files/directories\n",
return 0;
}
enum {
};
};
int c;
switch (c) {
case 'h':
help();
return 0;
case ARG_CREATE:
arg_create = true;
break;
case ARG_CLEAN:
arg_clean = true;
break;
case ARG_REMOVE:
arg_remove = true;
break;
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
help();
return -EINVAL;
}
return 1;
}
int r, n, j;
Item *i;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
else
prefix = "/";
log_open();
label_init();
log_error("Out of memory");
r = EXIT_FAILURE;
goto finish;
}
r = EXIT_SUCCESS;
else {
log_error("Failed to enumerate /etc/tmpfiles.d/ files: %m");
r = EXIT_FAILURE;
}
goto finish;
}
r = EXIT_SUCCESS;
for (j = 0; j < n; j++) {
int k;
char *fn;
FILE *f;
unsigned v;
if (k < 0) {
log_error("Failed to allocate file name.");
r = EXIT_FAILURE;
continue;
}
r = EXIT_FAILURE;
}
continue;
}
v = 0;
for (;;) {
break;
v++;
if (*l == '#' || *l == 0)
continue;
r = EXIT_FAILURE;
}
if (ferror(f)) {
r = EXIT_FAILURE;
}
fclose(f);
}
if (process_item(i) < 0)
r = EXIT_FAILURE;
if (process_item(i) < 0)
r = EXIT_FAILURE;
while ((i = hashmap_steal_first(items)))
item_free(i);
label_finish();
return r;
}