Merge pull request #9500 from zsol/append

Add support for opening files for appending
This commit is contained in:
Lennart Poettering 2018-07-20 15:45:08 +02:00 committed by GitHub
commit f606cd16d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 19 deletions

View File

@ -1756,8 +1756,8 @@ SystemCallErrorNumber=EPERM</programlisting>
of <option>inherit</option>, <option>null</option>, <option>tty</option>, <option>journal</option>,
<option>syslog</option>, <option>kmsg</option>, <option>journal+console</option>,
<option>syslog+console</option>, <option>kmsg+console</option>,
<option>file:<replaceable>path</replaceable></option>, <option>socket</option> or
<option>fd:<replaceable>name</replaceable></option>.</para>
<option>file:<replaceable>path</replaceable></option>, <option>append:<replaceable>path</replaceable></option>,
<option>socket</option> or<option>fd:<replaceable>name</replaceable></option>.</para>
<para><option>inherit</option> duplicates the file descriptor of standard input for standard output.</para>
@ -1788,11 +1788,17 @@ SystemCallErrorNumber=EPERM</programlisting>
<para>The <option>file:<replaceable>path</replaceable></option> option may be used to connect a specific file
system object to standard output. The semantics are similar to the same option of
<varname>StandardInput=</varname>, see above. If standard input and output are directed to the same file path,
it is opened only once, for reading as well as writing and duplicated. This is particular useful when the
specified path refers to an <constant>AF_UNIX</constant> socket in the file system, as in that case only a
<varname>StandardInput=</varname>, see above. If <replaceable>path</replaceable> refers to a regular file
on the filesystem, it is opened (created if it doesn't exist yet) for writing at the beginning of the file,
but without truncating it.
If standard input and output are directed to the same file path, it is opened only once, for reading as well
as writing and duplicated. This is particularly useful when the specified path refers to an
<constant>AF_UNIX</constant> socket in the file system, as in that case only a
single stream connection is created for both input and output.</para>
<para><option>append:<replaceable>path</replaceable></option> is similar to <option>file:<replaceable>path
</replaceable></option> above, but it opens the file in append mode.</para>
<para><option>socket</option> connects standard output to a socket acquired via socket activation. The
semantics are similar to the same option of <varname>StandardInput=</varname>, see above.</para>

View File

@ -1731,7 +1731,10 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (STR_IN_SET(name, "StandardInputFile", "StandardOutputFile", "StandardErrorFile")) {
} else if (STR_IN_SET(name,
"StandardInputFile",
"StandardOutputFile", "StandardOutputFileToCreate", "StandardOutputFileToAppend",
"StandardErrorFile", "StandardErrorFileToCreate", "StandardErrorFileToAppend")) {
const char *s;
r = sd_bus_message_read(message, "s", &s);
@ -1755,23 +1758,34 @@ int bus_exec_context_set_transient_property(
c->std_input = EXEC_INPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s);
} else if (streq(name, "StandardOutputFile")) {
} else if (STR_IN_SET(name, "StandardOutputFile", "StandardOutputFileToAppend")) {
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s));
if (r < 0)
return r;
c->std_output = EXEC_OUTPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s);
if (streq(name, "StandardOutputFile")) {
c->std_output = EXEC_OUTPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s);
} else {
assert(streq(name, "StandardOutputFileToAppend"));
c->std_output = EXEC_OUTPUT_FILE_APPEND;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=append:%s", s);
}
} else {
assert(streq(name, "StandardErrorFile"));
assert(STR_IN_SET(name, "StandardErrorFile", "StandardErrorFileToAppend"));
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s));
if (r < 0)
return r;
c->std_error = EXEC_OUTPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=file:%s", s);
if (streq(name, "StandardErrorFile")) {
c->std_error = EXEC_OUTPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s);
} else {
assert(streq(name, "StandardErrorFileToAppend"));
c->std_error = EXEC_OUTPUT_FILE_APPEND;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=append:%s", s);
}
}
}

View File

@ -89,6 +89,7 @@
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
#include "umask-util.h"
#include "unit.h"
#include "user-util.h"
#include "util.h"
@ -675,9 +676,10 @@ static int setup_output(
(void) fd_nonblock(named_iofds[fileno], false);
return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
case EXEC_OUTPUT_FILE: {
case EXEC_OUTPUT_FILE:
case EXEC_OUTPUT_FILE_APPEND: {
bool rw;
int fd;
int fd, flags;
assert(context->stdio_file[fileno]);
@ -687,11 +689,16 @@ static int setup_output(
if (rw)
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
fd = acquire_path(context->stdio_file[fileno], O_WRONLY, 0666 & ~context->umask);
flags = O_WRONLY;
if (o == EXEC_OUTPUT_FILE_APPEND)
flags |= O_APPEND;
fd = acquire_path(context->stdio_file[fileno], flags, 0666 & ~context->umask);
if (fd < 0)
return fd;
return move_fd(fd, fileno, false);
return move_fd(fd, fileno, 0);
}
default:
@ -4036,8 +4043,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
if (c->std_output == EXEC_OUTPUT_FILE)
fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
if (c->std_error == EXEC_OUTPUT_FILE)
fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
if (c->tty_path)
fprintf(f,
@ -4968,6 +4979,7 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_SOCKET] = "socket",
[EXEC_OUTPUT_NAMED_FD] = "fd",
[EXEC_OUTPUT_FILE] = "file",
[EXEC_OUTPUT_FILE_APPEND] = "append",
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);

View File

@ -56,6 +56,7 @@ typedef enum ExecOutput {
EXEC_OUTPUT_SOCKET,
EXEC_OUTPUT_NAMED_FD,
EXEC_OUTPUT_FILE,
EXEC_OUTPUT_FILE_APPEND,
_EXEC_OUTPUT_MAX,
_EXEC_OUTPUT_INVALID = -1
} ExecOutput;

View File

@ -1015,6 +1015,17 @@ int config_parse_exec_output(
eo = EXEC_OUTPUT_FILE;
} else if ((n = startswith(rvalue, "append:"))) {
r = unit_full_printf(u, n, &resolved);
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
if (r < 0)
return -ENOEXEC;
eo = EXEC_OUTPUT_FILE_APPEND;
} else {
eo = exec_output_from_string(rvalue);
if (eo < 0) {

View File

@ -604,8 +604,8 @@ static int config_parse_output_restricted(
return 0;
}
if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file: are not supported as defaults, ignoring: %s", rvalue);
if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
return 0;
}

View File

@ -625,6 +625,14 @@ static void test_exec_standardinput(Manager *m) {
test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
}
static void test_exec_standardoutput(Manager *m) {
test(m, "exec-standardoutput-file.service", 0, CLD_EXITED);
}
static void test_exec_standardoutput_append(Manager *m) {
test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
}
static int run_tests(UnitFileScope scope, const test_function_t *tests) {
const test_function_t *test = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
@ -672,6 +680,8 @@ int main(int argc, char *argv[]) {
test_exec_restrictnamespaces,
test_exec_runtimedirectory,
test_exec_standardinput,
test_exec_standardoutput,
test_exec_standardoutput_append,
test_exec_supplementarygroups,
test_exec_systemcallerrornumber,
test_exec_systemcallfilter,

View File

@ -115,6 +115,8 @@ test_data_files = '''
test-execute/exec-specifier@.service
test-execute/exec-standardinput-data.service
test-execute/exec-standardinput-file.service
test-execute/exec-standardoutput-file.service
test-execute/exec-standardoutput-append.service
test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
test-execute/exec-supplementarygroups-multiple-groups-withgid.service
test-execute/exec-supplementarygroups-multiple-groups-withuid.service

View File

@ -0,0 +1,13 @@
[Unit]
Description=Test for StandardOutput=append:
[Service]
ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
ExecStartPre=sh -c 'printf "hello\nhello\n" > /tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hello
StandardOutput=append:/tmp/test-exec-standardoutput-output
StandardError=null
ExecStart=cat
ExecStart=cmp /tmp/test-exec-standardoutput-output /tmp/test-exec-standardoutput-expected
Type=oneshot

View File

@ -0,0 +1,13 @@
[Unit]
Description=Test for StandardOutput=file:
[Service]
ExecStartPre=sh -c 'printf "nooo\nhello\n" > /tmp/test-exec-standardoutput-output'
ExecStartPre=sh -c 'printf "hello\nello\n" > /tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hello
StandardOutput=file:/tmp/test-exec-standardoutput-output
StandardError=null
ExecStart=cat
ExecStart=cmp /tmp/test-exec-standardoutput-expected /tmp/test-exec-standardoutput-output
Type=oneshot