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>
<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>
} 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);
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;
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)
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);
}
}
}
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;
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;
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)
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,
[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);
EXEC_OUTPUT_NAMED_FD,
EXEC_OUTPUT_FILE,
EXEC_OUTPUT_FILE_APPEND,
+ EXEC_OUTPUT_FILE_TRUNCATE,
_EXEC_OUTPUT_MAX,
_EXEC_OUTPUT_INVALID = -1
} ExecOutput;
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) {
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;
}
}
} 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)
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);
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),
--- /dev/null
+[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
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