diff options
author | Zsolt Dollenstein <zsol.zsol@gmail.com> | 2018-07-03 21:22:29 +0200 |
---|---|---|
committer | Zsolt Dollenstein <zsol@fb.com> | 2018-07-20 12:54:22 +0200 |
commit | 566b7d23eb747e9c5a74e5647693077b52395fc5 (patch) | |
tree | d18c8eb14208a543717ed590c4548adf71874344 | |
parent | Merge pull request #9659 from yuwata/fix-network-memleak2 (diff) | |
download | systemd-566b7d23eb747e9c5a74e5647693077b52395fc5.tar.xz systemd-566b7d23eb747e9c5a74e5647693077b52395fc5.zip |
Add support for opening files for appending
Addresses part of #8983
-rw-r--r-- | man/systemd.exec.xml | 16 | ||||
-rw-r--r-- | src/core/dbus-execute.c | 30 | ||||
-rw-r--r-- | src/core/execute.c | 20 | ||||
-rw-r--r-- | src/core/execute.h | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 11 | ||||
-rw-r--r-- | src/core/main.c | 4 | ||||
-rw-r--r-- | src/test/test-execute.c | 10 | ||||
-rw-r--r-- | test/meson.build | 2 | ||||
-rw-r--r-- | test/test-execute/exec-standardoutput-append.service | 13 | ||||
-rw-r--r-- | test/test-execute/exec-standardoutput-file.service | 13 |
10 files changed, 101 insertions, 19 deletions
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index ccf79b21d6..c898d226a7 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -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> diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index c44970c10c..1cdeaa8cfb 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -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); + } } } diff --git a/src/core/execute.c b/src/core/execute.c index 8ac69d1a0f..a08e3105bf 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -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); diff --git a/src/core/execute.h b/src/core/execute.h index 77ffe82323..de44085492 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -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; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e18499384a..89b7bd8d09 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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) { diff --git a/src/core/main.c b/src/core/main.c index 44dd8348be..6bffd6f362 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -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; } diff --git a/src/test/test-execute.c b/src/test/test-execute.c index fa8efdddd2..acf28d82c4 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -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, diff --git a/test/meson.build b/test/meson.build index 826e684e59..4aab7437e4 100644 --- a/test/meson.build +++ b/test/meson.build @@ -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 diff --git a/test/test-execute/exec-standardoutput-append.service b/test/test-execute/exec-standardoutput-append.service new file mode 100644 index 0000000000..8983bb056b --- /dev/null +++ b/test/test-execute/exec-standardoutput-append.service @@ -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 diff --git a/test/test-execute/exec-standardoutput-file.service b/test/test-execute/exec-standardoutput-file.service new file mode 100644 index 0000000000..71e2604b94 --- /dev/null +++ b/test/test-execute/exec-standardoutput-file.service @@ -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 |