1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

Add support for opening files for appending

Addresses part of #8983
This commit is contained in:
Zsolt Dollenstein 2018-07-03 12:22:29 -07:00 committed by Zsolt Dollenstein
parent 48a0ab391c
commit 566b7d23eb
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>, 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</option>, <option>kmsg</option>, <option>journal+console</option>,
<option>syslog+console</option>, <option>kmsg+console</option>, <option>syslog+console</option>, <option>kmsg+console</option>,
<option>file:<replaceable>path</replaceable></option>, <option>socket</option> or <option>file:<replaceable>path</replaceable></option>, <option>append:<replaceable>path</replaceable></option>,
<option>fd:<replaceable>name</replaceable></option>.</para> <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> <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 <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 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, <varname>StandardInput=</varname>, see above. If <replaceable>path</replaceable> refers to a regular file
it is opened only once, for reading as well as writing and duplicated. This is particular useful when the on the filesystem, it is opened (created if it doesn't exist yet) for writing at the beginning of the file,
specified path refers to an <constant>AF_UNIX</constant> socket in the file system, as in that case only a 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> 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 <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> 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; 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; const char *s;
r = sd_bus_message_read(message, "s", &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; c->std_input = EXEC_INPUT_FILE;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s); 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)); r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s));
if (r < 0) if (r < 0)
return r; return r;
c->std_output = EXEC_OUTPUT_FILE; if (streq(name, "StandardOutputFile")) {
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s); 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 { } 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)); r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s));
if (r < 0) if (r < 0)
return r; return r;
c->std_error = EXEC_OUTPUT_FILE; if (streq(name, "StandardErrorFile")) {
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=file:%s", s); 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 "strv.h"
#include "syslog-util.h" #include "syslog-util.h"
#include "terminal-util.h" #include "terminal-util.h"
#include "umask-util.h"
#include "unit.h" #include "unit.h"
#include "user-util.h" #include "user-util.h"
#include "util.h" #include "util.h"
@ -675,9 +676,10 @@ static int setup_output(
(void) fd_nonblock(named_iofds[fileno], false); (void) fd_nonblock(named_iofds[fileno], false);
return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno; return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
case EXEC_OUTPUT_FILE: { case EXEC_OUTPUT_FILE:
case EXEC_OUTPUT_FILE_APPEND: {
bool rw; bool rw;
int fd; int fd, flags;
assert(context->stdio_file[fileno]); assert(context->stdio_file[fileno]);
@ -687,11 +689,16 @@ static int setup_output(
if (rw) if (rw)
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; 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) if (fd < 0)
return fd; return fd;
return move_fd(fd, fileno, false); return move_fd(fd, fileno, 0);
} }
default: 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]); fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
if (c->std_output == EXEC_OUTPUT_FILE) if (c->std_output == EXEC_OUTPUT_FILE)
fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]); 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) if (c->std_error == EXEC_OUTPUT_FILE)
fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]); 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) if (c->tty_path)
fprintf(f, fprintf(f,
@ -4968,6 +4979,7 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_SOCKET] = "socket", [EXEC_OUTPUT_SOCKET] = "socket",
[EXEC_OUTPUT_NAMED_FD] = "fd", [EXEC_OUTPUT_NAMED_FD] = "fd",
[EXEC_OUTPUT_FILE] = "file", [EXEC_OUTPUT_FILE] = "file",
[EXEC_OUTPUT_FILE_APPEND] = "append",
}; };
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);

View File

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

View File

@ -1015,6 +1015,17 @@ int config_parse_exec_output(
eo = EXEC_OUTPUT_FILE; 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 { } else {
eo = exec_output_from_string(rvalue); eo = exec_output_from_string(rvalue);
if (eo < 0) { if (eo < 0) {

View File

@ -604,8 +604,8 @@ static int config_parse_output_restricted(
return 0; return 0;
} }
if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE)) { 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: are not supported as defaults, ignoring: %s", rvalue); 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; return 0;
} }

View File

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

View File

@ -115,6 +115,8 @@ test_data_files = '''
test-execute/exec-specifier@.service test-execute/exec-specifier@.service
test-execute/exec-standardinput-data.service test-execute/exec-standardinput-data.service
test-execute/exec-standardinput-file.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-default-group-user.service
test-execute/exec-supplementarygroups-multiple-groups-withgid.service test-execute/exec-supplementarygroups-multiple-groups-withgid.service
test-execute/exec-supplementarygroups-multiple-groups-withuid.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