]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Add truncate: to StandardOutput= etc.
authorLucas Werkmeister <mail@lucaswerkmeister.de>
Thu, 14 Jan 2021 21:32:55 +0000 (22:32 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 15 Jan 2021 08:54:50 +0000 (09:54 +0100)
This adds the ability to specify truncate:PATH for StandardOutput= and
StandardError=, similar to the existing append:PATH. The code is mostly
copied from the related append: code. Fixes #8983.

man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment.c
src/shared/bus-unit-util.c
src/test/test-execute.c
test/test-execute/exec-standardoutput-truncate.service [new file with mode: 0644]
test/units/testsuite-27.sh

index ed8ab6205c3aa1befcb939b3497040faee62da58..7328f9b96590999a9db26639c4ccf621e5a2296d 100644 (file)
@@ -2383,8 +2383,8 @@ SystemCallErrorNumber=EPERM</programlisting>
         to. Takes one of <option>inherit</option>, <option>null</option>, <option>tty</option>,
         <option>journal</option>, <option>kmsg</option>, <option>journal+console</option>,
         <option>kmsg+console</option>, <option>file:<replaceable>path</replaceable></option>,
-        <option>append:<replaceable>path</replaceable></option>, <option>socket</option> or
-        <option>fd:<replaceable>name</replaceable></option>.</para>
+        <option>append:<replaceable>path</replaceable></option>, <option>truncate:<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>
 
@@ -2424,6 +2424,10 @@ SystemCallErrorNumber=EPERM</programlisting>
         <option>file:<replaceable>path</replaceable></option> above, but it opens the file in append mode.
         </para>
 
+        <para><option>truncate:<replaceable>path</replaceable></option> is similar to
+        <option>file:<replaceable>path</replaceable></option> above, but it truncates the file when opening it.
+        </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>
 
index 04735354a33bf479d64d5fcc9f3c94286f0d4fdd..4ad9181993966eff187cc2c821476c6a6c8de4b7 100644 (file)
@@ -2727,8 +2727,8 @@ int bus_exec_context_set_transient_property(
 
         } else if (STR_IN_SET(name,
                               "StandardInputFile",
-                              "StandardOutputFile", "StandardOutputFileToAppend",
-                              "StandardErrorFile", "StandardErrorFileToAppend")) {
+                              "StandardOutputFile", "StandardOutputFileToAppend", "StandardOutputFileToTruncate",
+                              "StandardErrorFile", "StandardErrorFileToAppend", "StandardErrorFileToTruncate")) {
                 const char *s;
 
                 r = sd_bus_message_read(message, "s", &s);
@@ -2752,7 +2752,7 @@ 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 (STR_IN_SET(name, "StandardOutputFile", "StandardOutputFileToAppend")) {
+                        } else if (STR_IN_SET(name, "StandardOutputFile", "StandardOutputFileToAppend", "StandardOutputFileToTruncate")) {
                                 r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s));
                                 if (r < 0)
                                         return r;
@@ -2760,13 +2760,16 @@ int bus_exec_context_set_transient_property(
                                 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"));
+                                } else if (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, "StandardOutputFileToTruncate"));
+                                        c->std_output = EXEC_OUTPUT_FILE_TRUNCATE;
+                                        unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=truncate:%s", s);
                                 }
                         } else {
-                                assert(STR_IN_SET(name, "StandardErrorFile", "StandardErrorFileToAppend"));
+                                assert(STR_IN_SET(name, "StandardErrorFile", "StandardErrorFileToAppend", "StandardErrorFileToTruncate"));
 
                                 r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s));
                                 if (r < 0)
@@ -2775,10 +2778,13 @@ int bus_exec_context_set_transient_property(
                                 if (streq(name, "StandardErrorFile")) {
                                         c->std_error = EXEC_OUTPUT_FILE;
                                         unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=file:%s", s);
-                                } else {
-                                        assert(streq(name, "StandardErrorFileToAppend"));
+                                } else if (streq(name, "StandardErrorFileToAppend")) {
                                         c->std_error = EXEC_OUTPUT_FILE_APPEND;
                                         unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=append:%s", s);
+                                } else {
+                                        assert(streq(name, "StandardErrorFileToTruncate"));
+                                        c->std_error = EXEC_OUTPUT_FILE_TRUNCATE;
+                                        unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=truncate:%s", s);
                                 }
                         }
                 }
index 8f901fa7154d460efe03e9367444787e46f89c2e..ee5f082783b484b826f17fe0dd77299dde4b7394 100644 (file)
@@ -562,7 +562,7 @@ static bool can_inherit_stderr_from_stdout(
         if (e == EXEC_OUTPUT_NAMED_FD)
                 return streq_ptr(context->stdio_fdname[STDOUT_FILENO], context->stdio_fdname[STDERR_FILENO]);
 
-        if (IN_SET(e, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND))
+        if (IN_SET(e, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND, EXEC_OUTPUT_FILE_TRUNCATE))
                 return streq_ptr(context->stdio_file[STDOUT_FILENO], context->stdio_file[STDERR_FILENO]);
 
         return true;
@@ -698,7 +698,8 @@ static int setup_output(
                 return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
 
         case EXEC_OUTPUT_FILE:
-        case EXEC_OUTPUT_FILE_APPEND: {
+        case EXEC_OUTPUT_FILE_APPEND:
+        case EXEC_OUTPUT_FILE_TRUNCATE: {
                 bool rw;
                 int fd, flags;
 
@@ -713,6 +714,8 @@ static int setup_output(
                 flags = O_WRONLY;
                 if (o == EXEC_OUTPUT_FILE_APPEND)
                         flags |= O_APPEND;
+                else if (o == EXEC_OUTPUT_FILE_TRUNCATE)
+                        flags |= O_TRUNC;
 
                 fd = acquire_path(context->stdio_file[fileno], flags, 0666 & ~context->umask);
                 if (fd < 0)
@@ -5357,10 +5360,14 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 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_output == EXEC_OUTPUT_FILE_TRUNCATE)
+                fprintf(f, "%sStandardOutputFileToTruncate: %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->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
+                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
 
         if (c->tty_path)
                 fprintf(f,
@@ -6449,6 +6456,7 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
         [EXEC_OUTPUT_NAMED_FD] = "fd",
         [EXEC_OUTPUT_FILE] = "file",
         [EXEC_OUTPUT_FILE_APPEND] = "append",
+        [EXEC_OUTPUT_FILE_TRUNCATE] = "truncate",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
index 33d7e1693d64c89d6ac900547cfb0a809a773d31..da8d6ae27290f8b994e0c9b4fa8b2a57de57b345 100644 (file)
@@ -61,6 +61,7 @@ typedef enum ExecOutput {
         EXEC_OUTPUT_NAMED_FD,
         EXEC_OUTPUT_FILE,
         EXEC_OUTPUT_FILE_APPEND,
+        EXEC_OUTPUT_FILE_TRUNCATE,
         _EXEC_OUTPUT_MAX,
         _EXEC_OUTPUT_INVALID = -1
 } ExecOutput;
index 4964249bf213e2cdf00bfa288d6ef17359609dcb..4401e598b5ce2dd3c80fd66db21ce2011014fb6d 100644 (file)
@@ -1202,6 +1202,20 @@ int config_parse_exec_output(
                         return 0;
 
                 eo = EXEC_OUTPUT_FILE_APPEND;
+
+        } else if ((n = startswith(rvalue, "truncate:"))) {
+
+                r = unit_full_printf(u, n, &resolved);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
+                        return 0;
+                }
+
+                r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
+                if (r < 0)
+                        return 0;
+
+                eo = EXEC_OUTPUT_FILE_TRUNCATE;
         } else {
                 eo = exec_output_from_string(rvalue);
                 if (eo < 0) {
@@ -5761,8 +5775,8 @@ int config_parse_output_restricted(
                         return 0;
                 }
 
-                if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
-                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append: 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, EXEC_OUTPUT_FILE_TRUNCATE)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append:, truncate: are not supported as defaults, ignoring: %s", rvalue);
                         return 0;
                 }
         }
index bc17b6e1fbeabdc84501a5ddf051b7715e57da94..07f936dc6cbf778feb1719cae097f71974ed0782 100644 (file)
@@ -1144,6 +1144,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 } else if ((n = startswith(eq, "append:"))) {
                         appended = strjoina(field, "FileToAppend");
                         r = sd_bus_message_append(m, "(sv)", appended, "s", n);
+                } else if ((n = startswith(eq, "truncate:"))) {
+                        appended = strjoina(field, "FileToTruncate");
+                        r = sd_bus_message_append(m, "(sv)", appended, "s", n);
                 } else
                         r = sd_bus_message_append(m, "(sv)", field, "s", eq);
                 if (r < 0)
index 83816f474c5a070d5ae3ed00aa06121129fbd01d..01e2443777c0697ca99c6e638a2f311f9f94bcb9 100644 (file)
@@ -811,6 +811,10 @@ static void test_exec_standardoutput_append(Manager *m) {
         test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
 }
 
+static void test_exec_standardoutput_truncate(Manager *m) {
+        test(m, "exec-standardoutput-truncate.service", 0, CLD_EXITED);
+}
+
 static void test_exec_condition(Manager *m) {
         test_service(m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE);
         test_service(m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
@@ -876,6 +880,7 @@ int main(int argc, char *argv[]) {
                 entry(test_exec_standardinput),
                 entry(test_exec_standardoutput),
                 entry(test_exec_standardoutput_append),
+                entry(test_exec_standardoutput_truncate),
                 entry(test_exec_supplementarygroups),
                 entry(test_exec_systemcallerrornumber),
                 entry(test_exec_systemcallfilter),
diff --git a/test/test-execute/exec-standardoutput-truncate.service b/test/test-execute/exec-standardoutput-truncate.service
new file mode 100644 (file)
index 0000000..7a12a06
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=Test for StandardOutput=truncate:
+
+[Service]
+ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hi\n" > /tmp/test-exec-standardoutput-expected'
+StandardInput=data
+StandardInputText=hi
+StandardOutput=truncate:/tmp/test-exec-standardoutput-output
+StandardError=null
+ExecStart=cat
+ExecStartPost=cmp /tmp/test-exec-standardoutput-output /tmp/test-exec-standardoutput-expected
+Type=oneshot
index 9d92e6e574284592cfc9e805b1c0f6ec8a7ab942..0e9ffe118927aa2c7ed6554123e907e9febc29d3 100755 (executable)
@@ -43,6 +43,18 @@ a
 c
 EOF
 
+systemd-run --wait --unit=test27-four \
+            -p StandardOutput=truncate:/tmp/stdout \
+            -p StandardError=truncate:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo a ; echo b >&2'
+cmp /tmp/stdout <<EOF
+a
+EOF
+cmp /tmp/stderr <<EOF
+b
+EOF
+
 systemd-analyze log-level info
 
 echo OK >/testok