tmpfiles: support writing short strings to files, in order to support /sys manipulations at boot time, a la sysctl
This commit is contained in:
parent
88f0664562
commit
31ed59c511
3
TODO
3
TODO
|
@ -21,6 +21,8 @@ Bugfixes:
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
|
* rework namespace support, don't use pivot_root, and mount things after creating the namespace, not before
|
||||||
|
|
||||||
* systemctl journal command
|
* systemctl journal command
|
||||||
|
|
||||||
* journalctl: --cursor support, priority filtering
|
* journalctl: --cursor support, priority filtering
|
||||||
|
@ -82,7 +84,6 @@ Features:
|
||||||
* service restart retry configuration
|
* service restart retry configuration
|
||||||
|
|
||||||
* tmpfiles: apply "x" on "D" too (see patch from William Douglas)
|
* tmpfiles: apply "x" on "D" too (see patch from William Douglas)
|
||||||
* tmpfiles: support generation of char/block devices, symlinks and one-line files (think sysfs)
|
|
||||||
|
|
||||||
* don't set $HOME in services unless requested
|
* don't set $HOME in services unless requested
|
||||||
|
|
||||||
|
|
|
@ -98,12 +98,17 @@ L /tmp/foobar - - - - /dev/null</programlisting>
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>f</varname></term>
|
<term><varname>f</varname></term>
|
||||||
<listitem><para>Create a file if it doesn't exist yet</para></listitem>
|
<listitem><para>Create a file if it doesn't exist yet (optionally writing a short string into it, if the argument parameter is passed)</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>F</varname></term>
|
<term><varname>F</varname></term>
|
||||||
<listitem><para>Create or truncate a file</para></listitem>
|
<listitem><para>Create or truncate a file (optionally writing a short string into it, if the argument parameter is passed)</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>w</varname></term>
|
||||||
|
<listitem><para>Write the argument parameter to a file, if it exists.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -260,7 +265,10 @@ L /tmp/foobar - - - - /dev/null</programlisting>
|
||||||
path of the symlink. For c, b determines the
|
path of the symlink. For c, b determines the
|
||||||
major/minor of the device node, with major and
|
major/minor of the device node, with major and
|
||||||
minor formatted as integers, separated by :,
|
minor formatted as integers, separated by :,
|
||||||
e.g. "1:3". Ignored for all other lines.</para>
|
e.g. "1:3". For f, F, w may be used to specify
|
||||||
|
a short string that is written to the file,
|
||||||
|
suffixed by a newline. Ignored for all other
|
||||||
|
lines.</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
|
@ -54,6 +54,7 @@ typedef enum ItemType {
|
||||||
/* These ones take file names */
|
/* These ones take file names */
|
||||||
CREATE_FILE = 'f',
|
CREATE_FILE = 'f',
|
||||||
TRUNCATE_FILE = 'F',
|
TRUNCATE_FILE = 'F',
|
||||||
|
WRITE_FILE = 'w',
|
||||||
CREATE_DIRECTORY = 'd',
|
CREATE_DIRECTORY = 'd',
|
||||||
TRUNCATE_DIRECTORY = 'D',
|
TRUNCATE_DIRECTORY = 'D',
|
||||||
CREATE_FIFO = 'p',
|
CREATE_FIFO = 'p',
|
||||||
|
@ -574,19 +575,48 @@ static int create_item(Item *i) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case CREATE_FILE:
|
case CREATE_FILE:
|
||||||
case TRUNCATE_FILE: {
|
case TRUNCATE_FILE:
|
||||||
int fd;
|
case WRITE_FILE: {
|
||||||
|
int fd, flags;
|
||||||
|
|
||||||
|
flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
|
||||||
|
i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
|
||||||
|
|
||||||
u = umask(0);
|
u = umask(0);
|
||||||
fd = open(i->path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW|
|
fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
|
||||||
(i->type == TRUNCATE_FILE ? O_TRUNC : 0), i->mode);
|
|
||||||
umask(u);
|
umask(u);
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
|
if (i->type == WRITE_FILE && errno == ENOENT)
|
||||||
|
break;
|
||||||
|
|
||||||
log_error("Failed to create file %s: %m", i->path);
|
log_error("Failed to create file %s: %m", i->path);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i->argument) {
|
||||||
|
ssize_t n;
|
||||||
|
size_t l;
|
||||||
|
struct iovec iovec[2];
|
||||||
|
static const char new_line = '\n';
|
||||||
|
|
||||||
|
l = strlen(i->argument);
|
||||||
|
|
||||||
|
zero(iovec);
|
||||||
|
iovec[0].iov_base = i->argument;
|
||||||
|
iovec[0].iov_len = l;
|
||||||
|
|
||||||
|
iovec[1].iov_base = (void*) &new_line;
|
||||||
|
iovec[1].iov_len = 1;
|
||||||
|
|
||||||
|
n = writev(fd, iovec, 2);
|
||||||
|
if (n < 0 || (size_t) n != l+1) {
|
||||||
|
log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short");
|
||||||
|
close_nointr_nofail(fd);
|
||||||
|
return n < 0 ? n : -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
close_nointr_nofail(fd);
|
close_nointr_nofail(fd);
|
||||||
|
|
||||||
if (stat(i->path, &st) < 0) {
|
if (stat(i->path, &st) < 0) {
|
||||||
|
@ -752,6 +782,7 @@ static int remove_item_instance(Item *i, const char *instance) {
|
||||||
case IGNORE_PATH:
|
case IGNORE_PATH:
|
||||||
case RELABEL_PATH:
|
case RELABEL_PATH:
|
||||||
case RECURSIVE_RELABEL_PATH:
|
case RECURSIVE_RELABEL_PATH:
|
||||||
|
case WRITE_FILE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REMOVE_PATH:
|
case REMOVE_PATH:
|
||||||
|
@ -793,6 +824,7 @@ static int remove_item(Item *i) {
|
||||||
case IGNORE_PATH:
|
case IGNORE_PATH:
|
||||||
case RELABEL_PATH:
|
case RELABEL_PATH:
|
||||||
case RECURSIVE_RELABEL_PATH:
|
case RECURSIVE_RELABEL_PATH:
|
||||||
|
case WRITE_FILE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REMOVE_PATH:
|
case REMOVE_PATH:
|
||||||
|
@ -857,7 +889,10 @@ static bool item_equal(Item *a, Item *b) {
|
||||||
(a->age_set && a->age != b->age))
|
(a->age_set && a->age != b->age))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (a->type == CREATE_SYMLINK &&
|
if ((a->type == CREATE_FILE ||
|
||||||
|
a->type == TRUNCATE_FILE ||
|
||||||
|
a->type == WRITE_FILE ||
|
||||||
|
a->type == CREATE_SYMLINK) &&
|
||||||
!streq(a->argument, b->argument))
|
!streq(a->argument, b->argument))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -874,7 +909,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||||
char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
|
char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
|
||||||
char type;
|
char type;
|
||||||
Hashmap *h;
|
Hashmap *h;
|
||||||
int r;
|
int r, n = -1;
|
||||||
|
|
||||||
assert(fname);
|
assert(fname);
|
||||||
assert(line >= 1);
|
assert(line >= 1);
|
||||||
|
@ -893,19 +928,30 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||||
"%ms "
|
"%ms "
|
||||||
"%ms "
|
"%ms "
|
||||||
"%ms "
|
"%ms "
|
||||||
"%ms",
|
"%n",
|
||||||
&type,
|
&type,
|
||||||
&i->path,
|
&i->path,
|
||||||
&mode,
|
&mode,
|
||||||
&user,
|
&user,
|
||||||
&group,
|
&group,
|
||||||
&age,
|
&age,
|
||||||
&i->argument) < 2) {
|
&n) < 2) {
|
||||||
log_error("[%s:%u] Syntax error.", fname, line);
|
log_error("[%s:%u] Syntax error.", fname, line);
|
||||||
r = -EIO;
|
r = -EIO;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (n >= 0) {
|
||||||
|
n += strspn(buffer+n, WHITESPACE);
|
||||||
|
if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
|
||||||
|
i->argument = unquote(buffer+n, "\"");
|
||||||
|
if (!i->argument) {
|
||||||
|
log_error("Out of memory");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
|
|
||||||
case CREATE_FILE:
|
case CREATE_FILE:
|
||||||
|
@ -928,6 +974,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WRITE_FILE:
|
||||||
|
if (!i->argument) {
|
||||||
|
log_error("[%s:%u] Write file requires argument.", fname, line);
|
||||||
|
r = -EBADMSG;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case CREATE_CHAR_DEVICE:
|
case CREATE_CHAR_DEVICE:
|
||||||
case CREATE_BLOCK_DEVICE: {
|
case CREATE_BLOCK_DEVICE: {
|
||||||
unsigned major, minor;
|
unsigned major, minor;
|
||||||
|
|
|
@ -4118,7 +4118,8 @@ char *unquote(const char *s, const char* quotes) {
|
||||||
size_t l;
|
size_t l;
|
||||||
assert(s);
|
assert(s);
|
||||||
|
|
||||||
if ((l = strlen(s)) < 2)
|
l = strlen(s);
|
||||||
|
if (l < 2)
|
||||||
return strdup(s);
|
return strdup(s);
|
||||||
|
|
||||||
if (strchr(quotes, s[0]) && s[l-1] == s[0])
|
if (strchr(quotes, s[0]) && s[l-1] == s[0])
|
||||||
|
|
Loading…
Reference in New Issue