diff options
author | Zsolt Dollenstein <zsol.zsol@gmail.com> | 2018-07-03 12:22:29 -0700 |
---|---|---|
committer | The Plumber <50238977+systemd-rhel-bot@users.noreply.github.com> | 2020-05-19 13:36:25 +0200 |
commit | 2808e53f785e9ca7fdab286678e784b661b4c185 (patch) | |
tree | c718e3eec51fac57672cc9687b52bd5dca89fb33 | |
parent | b8af9fd65b697e9bb77a32d1a6a70367814aaed5 (diff) | |
download | systemd-2808e53f785e9ca7fdab286678e784b661b4c185.tar.gz |
Add support for opening files for appending
Addresses part of #8983
(cherry picked from commit 566b7d23eb747e9c5a74e5647693077b52395fc5)
Resolves: #1809175
-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 bdaed68162..e2a5ede968 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1792,8 +1792,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> @@ -1824,11 +1824,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 e7c0b893d1..f9527e56b2 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1772,7 +1772,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); @@ -1796,23 +1799,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 f012023224..3c54ac1110 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: @@ -4168,8 +4175,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, @@ -5111,6 +5122,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 2266355962..86c1cee84c 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -57,6 +57,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 2082166afb..9b2724307d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1016,6 +1016,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 9f238a8430..25536054b3 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -620,8 +620,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 637ffe96bb..0f8dc883b1 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -651,6 +651,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; @@ -698,6 +706,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 fb9f2cdb9b..4d1c51048c 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 |