summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemd.exec.xml46
-rw-r--r--src/core/dbus-execute.c86
-rw-r--r--src/core/execute.c86
-rw-r--r--src/core/execute.h5
-rw-r--r--src/core/load-fragment-gperf.gperf.m46
-rw-r--r--src/core/load-fragment.c104
-rw-r--r--src/core/load-fragment.h2
-rw-r--r--src/core/unit.c20
8 files changed, 322 insertions, 33 deletions
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index a5a453031f..7453aa7bee 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -409,8 +409,9 @@
<option>null</option>,
<option>tty</option>,
<option>tty-force</option>,
- <option>tty-fail</option> or
- <option>socket</option>.</para>
+ <option>tty-fail</option>,
+ <option>socket</option> or
+ <option>fd</option>.</para>
<para>If <option>null</option> is selected, standard input
will be connected to <filename>/dev/null</filename>, i.e. all
@@ -448,6 +449,20 @@
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
daemon.</para>
+ <para>The <option>fd</option> option connects
+ the input stream to a single file descriptor provided by a socket unit.
+ A custom named file descriptor can be specified as part of this option,
+ after a <literal>:</literal> (e.g. <literal>fd:<replaceable>foobar</replaceable></literal>).
+ If no name is specified, <literal>stdin</literal> is assumed
+ (i.e. <literal>fd</literal> is equivalent to <literal>fd:stdin</literal>).
+ At least one socket unit defining such name must be explicitly provided via the
+ <varname>Sockets=</varname> option, and file descriptor name may differ
+ from the name of its containing socket unit.
+ If multiple matches are found, the first one will be used.
+ See <varname>FileDescriptorName=</varname> in
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for more details about named descriptors and ordering.</para>
+
<para>This setting defaults to
<option>null</option>.</para></listitem>
</varlistentry>
@@ -464,8 +479,9 @@
<option>kmsg</option>,
<option>journal+console</option>,
<option>syslog+console</option>,
- <option>kmsg+console</option> or
- <option>socket</option>.</para>
+ <option>kmsg+console</option>,
+ <option>socket</option> or
+ <option>fd</option>.</para>
<para><option>inherit</option> duplicates the file descriptor
of standard input for standard output.</para>
@@ -514,6 +530,20 @@
similar to the same option of
<varname>StandardInput=</varname>.</para>
+ <para>The <option>fd</option> option connects
+ the output stream to a single file descriptor provided by a socket unit.
+ A custom named file descriptor can be specified as part of this option,
+ after a <literal>:</literal> (e.g. <literal>fd:<replaceable>foobar</replaceable></literal>).
+ If no name is specified, <literal>stdout</literal> is assumed
+ (i.e. <literal>fd</literal> is equivalent to <literal>fd:stdout</literal>).
+ At least one socket unit defining such name must be explicitly provided via the
+ <varname>Sockets=</varname> option, and file descriptor name may differ
+ from the name of its containing socket unit.
+ If multiple matches are found, the first one will be used.
+ See <varname>FileDescriptorName=</varname> in
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for more details about named descriptors and ordering.</para>
+
<para>If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the
kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname> on
<filename>systemd-journald.socket</filename> (also see the automatic dependencies section above).</para>
@@ -531,9 +561,13 @@
<listitem><para>Controls where file descriptor 2 (STDERR) of
the executed processes is connected to. The available options
are identical to those of <varname>StandardOutput=</varname>,
- with one exception: if set to <option>inherit</option> the
+ with some exceptions: if set to <option>inherit</option> the
file descriptor used for standard output is duplicated for
- standard error. This setting defaults to the value set with
+ standard error, while <option>fd</option> operates on the error
+ stream and will look by default for a descriptor named
+ <literal>stderr</literal>.</para>
+
+ <para>This setting defaults to the value set with
<option>DefaultStandardError=</option> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
which defaults to <option>inherit</option>. Note that setting
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index b8720d7d3d..1a7f770db1 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -627,6 +627,53 @@ static int property_get_syslog_facility(
return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority));
}
+static int property_get_input_fdname(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ const char *name;
+
+ assert(bus);
+ assert(c);
+ assert(property);
+ assert(reply);
+
+ name = exec_context_fdname(c, STDIN_FILENO);
+
+ return sd_bus_message_append(reply, "s", name);
+}
+
+static int property_get_output_fdname(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ const char *name = NULL;
+
+ assert(bus);
+ assert(c);
+ assert(property);
+ assert(reply);
+
+ if (c->std_output == EXEC_OUTPUT_NAMED_FD && streq(property, "StandardOutputFileDescriptorName"))
+ name = exec_context_fdname(c, STDOUT_FILENO);
+ else if (c->std_error == EXEC_OUTPUT_NAMED_FD && streq(property, "StandardErrorFileDescriptorName"))
+ name = exec_context_fdname(c, STDERR_FILENO);
+
+ return sd_bus_message_append(reply, "s", name);
+}
+
const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -677,8 +724,11 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StandardInput", "s", property_get_exec_input, offsetof(ExecContext, std_input), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StandardInputFileDescriptorName", "s", property_get_input_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StandardOutput", "s", bus_property_get_exec_output, offsetof(ExecContext, std_output), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StandardOutputFileDescriptorName", "s", property_get_output_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StandardError", "s", bus_property_get_exec_output, offsetof(ExecContext, std_error), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StandardErrorFileDescriptorName", "s", property_get_output_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYPath", "s", NULL, offsetof(ExecContext, tty_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1030,7 +1080,6 @@ int bus_exec_context_set_transient_property(
return 1;
-
} else if (streq(name, "StandardOutput")) {
const char *s;
ExecOutput p;
@@ -1072,6 +1121,41 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (STR_IN_SET(name,
+ "StandardInputFileDescriptorName", "StandardOutputFileDescriptorName", "StandardErrorFileDescriptorName")) {
+ const char *s;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ if (!fdname_is_valid(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
+
+ if (mode != UNIT_CHECK) {
+ if (streq(name, "StandardInputFileDescriptorName")) {
+ c->std_input = EXEC_INPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], s);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private_format(u, mode, name, "StandardInput=fd:%s", s);
+ } else if (streq(name, "StandardOutputFileDescriptorName")) {
+ c->std_output = EXEC_OUTPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], s);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private_format(u, mode, name, "StandardOutput=fd:%s", s);
+ } else if (streq(name, "StandardErrorFileDescriptorName")) {
+ c->std_error = EXEC_OUTPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], s);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private_format(u, mode, name, "StandardError=fd:%s", s);
+ }
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name,
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
diff --git a/src/core/execute.c b/src/core/execute.c
index 59c2bd0dd2..1b7b4a928d 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -411,7 +411,8 @@ static int fixup_output(ExecOutput std_output, int socket_fd) {
static int setup_input(
const ExecContext *context,
const ExecParameters *params,
- int socket_fd) {
+ int socket_fd,
+ int named_iofds[3]) {
ExecInput i;
@@ -461,6 +462,10 @@ static int setup_input(
case EXEC_INPUT_SOCKET:
return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+ case EXEC_INPUT_NAMED_FD:
+ (void) fd_nonblock(named_iofds[STDIN_FILENO], false);
+ return dup2(named_iofds[STDIN_FILENO], STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+
default:
assert_not_reached("Unknown input type");
}
@@ -472,6 +477,7 @@ static int setup_output(
const ExecParameters *params,
int fileno,
int socket_fd,
+ int named_iofds[3],
const char *ident,
uid_t uid,
gid_t gid,
@@ -523,7 +529,7 @@ static int setup_output(
return fileno;
/* Duplicate from stdout if possible */
- if (e == o || e == EXEC_OUTPUT_INHERIT)
+ if ((e == o && e != EXEC_OUTPUT_NAMED_FD) || e == EXEC_OUTPUT_INHERIT)
return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
o = e;
@@ -585,6 +591,10 @@ static int setup_output(
assert(socket_fd >= 0);
return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
+ case EXEC_OUTPUT_NAMED_FD:
+ (void) fd_nonblock(named_iofds[fileno], false);
+ return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
+
default:
assert_not_reached("Unknown error type");
}
@@ -2157,6 +2167,7 @@ static int exec_child(
DynamicCreds *dcreds,
char **argv,
int socket_fd,
+ int named_iofds[3],
int *fds, unsigned n_fds,
char **files_env,
int user_lookup_fd,
@@ -2298,19 +2309,19 @@ static int exec_child(
if (socket_fd >= 0)
(void) fd_nonblock(socket_fd, false);
- r = setup_input(context, params, socket_fd);
+ r = setup_input(context, params, socket_fd, named_iofds);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
- r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+ r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
- r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+ r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -2829,6 +2840,7 @@ int exec_spawn(Unit *unit,
int *fds = NULL; unsigned n_fds = 0;
_cleanup_free_ char *line = NULL;
int socket_fd, r;
+ int named_iofds[3] = { -1, -1, -1 };
char **argv;
pid_t pid;
@@ -2855,6 +2867,10 @@ int exec_spawn(Unit *unit,
n_fds = params->n_fds;
}
+ r = exec_context_named_iofds(unit, context, params, named_iofds);
+ if (r < 0)
+ return log_unit_error_errno(unit, r, "Failed to load a named file descriptor: %m");
+
r = exec_context_load_environment(unit, context, &files_env);
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
@@ -2884,6 +2900,7 @@ int exec_spawn(Unit *unit,
dcreds,
argv,
socket_fd,
+ named_iofds,
fds, n_fds,
files_env,
unit->manager->user_lookup_fds[1],
@@ -2946,6 +2963,9 @@ void exec_context_done(ExecContext *c) {
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
+ for (l = 0; l < 3; l++)
+ c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
+
c->working_directory = mfree(c->working_directory);
c->root_directory = mfree(c->root_directory);
c->tty_path = mfree(c->tty_path);
@@ -3044,6 +3064,56 @@ static void invalid_env(const char *p, void *userdata) {
log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
}
+const char* exec_context_fdname(const ExecContext *c, int fd_index) {
+ assert(c);
+
+ switch (fd_index) {
+ case STDIN_FILENO:
+ if (c->std_input != EXEC_INPUT_NAMED_FD)
+ return NULL;
+ return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
+ case STDOUT_FILENO:
+ if (c->std_output != EXEC_OUTPUT_NAMED_FD)
+ return NULL;
+ return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
+ case STDERR_FILENO:
+ if (c->std_error != EXEC_OUTPUT_NAMED_FD)
+ return NULL;
+ return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
+ default:
+ return NULL;
+ }
+}
+
+int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParameters *p, int named_iofds[3]) {
+ unsigned i, targets;
+ const char *stdio_fdname[3];
+
+ assert(c);
+ assert(p);
+
+ targets = (c->std_input == EXEC_INPUT_NAMED_FD) +
+ (c->std_output == EXEC_OUTPUT_NAMED_FD) +
+ (c->std_error == EXEC_OUTPUT_NAMED_FD);
+
+ for (i = 0; i < 3; i++)
+ stdio_fdname[i] = exec_context_fdname(c, i);
+
+ for (i = 0; i < p->n_fds && targets > 0; i++)
+ if (named_iofds[STDIN_FILENO] < 0 && c->std_input == EXEC_INPUT_NAMED_FD && stdio_fdname[STDIN_FILENO] && streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) {
+ named_iofds[STDIN_FILENO] = p->fds[i];
+ targets--;
+ } else if (named_iofds[STDOUT_FILENO] < 0 && c->std_output == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDOUT_FILENO] && streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
+ named_iofds[STDOUT_FILENO] = p->fds[i];
+ targets--;
+ } else if (named_iofds[STDERR_FILENO] < 0 && c->std_error == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDERR_FILENO] && streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
+ named_iofds[STDERR_FILENO] = p->fds[i];
+ targets--;
+ }
+
+ return (targets == 0 ? 0 : -ENOENT);
+}
+
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
char **i, **r = NULL;
@@ -3896,7 +3966,8 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_TTY] = "tty",
[EXEC_INPUT_TTY_FORCE] = "tty-force",
[EXEC_INPUT_TTY_FAIL] = "tty-fail",
- [EXEC_INPUT_SOCKET] = "socket"
+ [EXEC_INPUT_SOCKET] = "socket",
+ [EXEC_INPUT_NAMED_FD] = "fd",
};
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
@@ -3911,7 +3982,8 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
[EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
[EXEC_OUTPUT_JOURNAL] = "journal",
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
- [EXEC_OUTPUT_SOCKET] = "socket"
+ [EXEC_OUTPUT_SOCKET] = "socket",
+ [EXEC_OUTPUT_NAMED_FD] = "fd",
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
diff --git a/src/core/execute.h b/src/core/execute.h
index 1de439c3ad..c7d0f7761e 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -50,6 +50,7 @@ typedef enum ExecInput {
EXEC_INPUT_TTY_FORCE,
EXEC_INPUT_TTY_FAIL,
EXEC_INPUT_SOCKET,
+ EXEC_INPUT_NAMED_FD,
_EXEC_INPUT_MAX,
_EXEC_INPUT_INVALID = -1
} ExecInput;
@@ -65,6 +66,7 @@ typedef enum ExecOutput {
EXEC_OUTPUT_JOURNAL,
EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
EXEC_OUTPUT_SOCKET,
+ EXEC_OUTPUT_NAMED_FD,
_EXEC_OUTPUT_MAX,
_EXEC_OUTPUT_INVALID = -1
} ExecOutput;
@@ -120,6 +122,7 @@ struct ExecContext {
ExecInput std_input;
ExecOutput std_output;
ExecOutput std_error;
+ char *stdio_fdname[3];
nsec_t timer_slack_nsec;
@@ -284,6 +287,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_root);
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l);
+int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParameters *p, int named_iofds[3]);
+const char* exec_context_fdname(const ExecContext *c, int fd_index);
bool exec_context_may_touch_console(ExecContext *c);
bool exec_context_maintains_privileges(ExecContext *c);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index a700d853cc..08c88b6b53 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -35,9 +35,9 @@ $1.Environment, config_parse_environ, 0,
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
$1.DynamicUser, config_parse_bool, 0, offsetof($1, exec_context.dynamic_user)
-$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input)
-$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output)
-$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error)
+$1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context)
+$1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context)
+$1.StandardError, config_parse_exec_output, 0, offsetof($1, exec_context)
$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path)
$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset)
$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 8cc7a8e765..a69f60097d 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -776,8 +776,104 @@ int config_parse_socket_bindtodevice(
return 0;
}
-DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input literal specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output literal specifier");
+
+int config_parse_exec_input(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ ExecContext *c = data;
+ const char *name;
+ int r;
+
+ assert(data);
+ assert(filename);
+ assert(line);
+ assert(rvalue);
+
+ name = startswith(rvalue, "fd:");
+ if (name) {
+ /* Strip prefix and validate fd name */
+ if (!fdname_is_valid(name)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
+ return 0;
+ }
+ c->std_input = EXEC_INPUT_NAMED_FD;
+ r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], name);
+ if (r < 0)
+ log_oom();
+ return r;
+ } else {
+ ExecInput ei = exec_input_from_string(rvalue);
+ if (ei == _EXEC_INPUT_INVALID)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
+ else
+ c->std_input = ei;
+ return 0;
+ }
+}
+
+int config_parse_exec_output(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ ExecContext *c = data;
+ ExecOutput eo;
+ const char *name;
+ int r;
+
+ assert(data);
+ assert(filename);
+ assert(line);
+ assert(lvalue);
+ assert(rvalue);
+
+ name = startswith(rvalue, "fd:");
+ if (name) {
+ /* Strip prefix and validate fd name */
+ if (!fdname_is_valid(name)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
+ return 0;
+ }
+ eo = EXEC_OUTPUT_NAMED_FD;
+ } else {
+ eo = exec_output_from_string(rvalue);
+ if (eo == _EXEC_OUTPUT_INVALID) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
+ return 0;
+ }
+ }
+
+ if (streq(lvalue, "StandardOutput")) {
+ c->std_output = eo;
+ r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], name);
+ if (r < 0)
+ log_oom();
+ return r;
+ } else if (streq(lvalue, "StandardError")) {
+ c->std_error = eo;
+ r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], name);
+ if (r < 0)
+ log_oom();
+ return r;
+ } else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output property, ignoring: %s", lvalue);
+ return 0;
+ }
+}
int config_parse_exec_io_class(const char *unit,
const char *filename,
@@ -4183,8 +4279,8 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_exec_cpu_affinity, "CPUAFFINITY" },
{ config_parse_mode, "MODE" },
{ config_parse_unit_env_file, "FILE" },
- { config_parse_output, "OUTPUT" },
- { config_parse_input, "INPUT" },
+ { config_parse_exec_output, "OUTPUT" },
+ { config_parse_exec_input, "INPUT" },
{ config_parse_log_facility, "FACILITY" },
{ config_parse_log_level, "LEVEL" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 8b688740cf..6d1fe55bcd 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -45,7 +45,9 @@ int config_parse_service_timeout(const char *unit, const char *filename, unsigne
int config_parse_service_type(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_service_restart(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_socket_bindtodevice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_output(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_output(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_input(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_input(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_io_class(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_io_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/core/unit.c b/src/core/unit.c
index b24ca5aed8..2fa397bd41 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -858,18 +858,14 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- if (c->std_output != EXEC_OUTPUT_KMSG &&
- c->std_output != EXEC_OUTPUT_SYSLOG &&
- c->std_output != EXEC_OUTPUT_JOURNAL &&
- c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
- c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE &&
- c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_KMSG &&
- c->std_error != EXEC_OUTPUT_SYSLOG &&
- c->std_error != EXEC_OUTPUT_JOURNAL &&
- c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE)
+ if (!IN_SET(c->std_output,
+ EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+ !IN_SET(c->std_error,
+ EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE))
return 0;
/* If syslog or kernel logging is requested, make sure our own