core/exec: add a named-descriptor option ("fd") for streams (#4179)
authorLuca Bruno <luca.bruno@coreos.com>
Tue, 18 Oct 2016 00:05:49 +0000 (00:05 +0000)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 18 Oct 2016 00:05:49 +0000 (20:05 -0400)
This commit adds a `fd` option to `StandardInput=`,
`StandardOutput=` and `StandardError=` properties in order to
connect standard streams to externally named descriptors provided
by some socket units.

This option looks for a file descriptor named as the corresponding
stream. Custom names can be specified, separated by a colon.
If multiple name-matches exist, the first matching fd will be used.

man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/unit.c

index a5a4530..7453aa7 100644 (file)
         <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
         <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>
         <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>
         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>
         <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
index b8720d7..1a7f770 100644 (file)
@@ -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",
index 59c2bd0..1b7b4a9 100644 (file)
@@ -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);
index 1de439c..c7d0f77 100644 (file)
@@ -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);
index a700d85..08c88b6 100644 (file)
@@ -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)
index 8cc7a8e..a69f600 100644 (file)
@@ -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" },
index 8b68874..6d1fe55 100644 (file)
@@ -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);
index b24ca5a..2fa397b 100644 (file)
@@ -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