diff options
author | Lennart Poettering <lennart@poettering.net> | 2017-10-27 11:33:05 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2017-11-17 11:13:44 +0100 |
commit | 08f3be7a38d245b5fed9902f10d3129f339477fd (patch) | |
tree | b9b4c8aff09fe77791edae41ef08e2d2dad3933f /src | |
parent | 11f5d82507494b39fcce27e102e5a8b186451acf (diff) | |
download | systemd-08f3be7a38d245b5fed9902f10d3129f339477fd.tar.gz systemd-08f3be7a38d245b5fed9902f10d3129f339477fd.tar.bz2 systemd-08f3be7a38d245b5fed9902f10d3129f339477fd.zip |
core: add two new unit file settings: StandardInputData= + StandardInputText=
Both permit configuring data to pass through STDIN to an invoked
process. StandardInputText= accepts a line of text (possibly with
embedded C-style escapes as well as unit specifiers), which is appended
to the buffer to pass as stdin, followed by a single newline.
StandardInputData= is similar, but accepts arbitrary base64 encoded
data, and will not resolve specifiers or C-style escapes, nor append
newlines.
This may be used to pass input/configuration data to services, directly
in-line from unit files, either in a cooked or in a more raw format.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/dbus-execute.c | 64 | ||||
-rw-r--r-- | src/core/execute.c | 36 | ||||
-rw-r--r-- | src/core/execute.h | 6 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 126 | ||||
-rw-r--r-- | src/core/load-fragment.h | 2 | ||||
-rw-r--r-- | src/core/service.c | 10 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 56 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 19 |
9 files changed, 311 insertions, 10 deletions
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index eeb4ac9a01..3931bf7e70 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -34,6 +34,7 @@ #include "execute.h" #include "fd-util.h" #include "fileio.h" +#include "hexdecoct.h" #include "io-util.h" #include "ioprio.h" #include "journal-util.h" @@ -730,6 +731,25 @@ static int property_get_output_fdname( return sd_bus_message_append(reply, "s", name); } +static int property_get_input_data( + 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; + + assert(bus); + assert(c); + assert(property); + assert(reply); + + return sd_bus_message_append_array(reply, 'y', c->stdin_data, c->stdin_data_size); +} + static int property_get_bind_paths( sd_bus *bus, const char *path, @@ -858,6 +878,7 @@ const sd_bus_vtable bus_exec_vtable[] = { 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("StandardInputData", "ay", property_get_input_data, 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), @@ -1840,6 +1861,49 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "StandardInputData")) { + const void *p; + size_t sz; + + r = sd_bus_message_read_array(message, 'y', &p, &sz); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *encoded = NULL; + + if (sz == 0) { + c->stdin_data = mfree(c->stdin_data); + c->stdin_data_size = 0; + + unit_write_drop_in_private(u, mode, name, "StandardInputData="); + } else { + void *q; + ssize_t n; + + if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */ + c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) + return -E2BIG; + + n = base64mem(p, sz, &encoded); + if (n < 0) + return (int) n; + + q = realloc(c->stdin_data, c->stdin_data_size + sz); + if (!q) + return -ENOMEM; + + memcpy((uint8_t*) q + c->stdin_data_size, p, sz); + + c->stdin_data = q; + c->stdin_data_size += sz; + + unit_write_drop_in_private_format(u, mode, name, "StandardInputData=%s", encoded); + } + } + + return 1; + } else if (STR_IN_SET(name, "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", diff --git a/src/core/execute.c b/src/core/execute.c index 547ab40657..f7243f3c0f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -390,7 +390,16 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) { return move_fd(fd, nfd, false); } -static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { +static int fixup_input( + const ExecContext *context, + int socket_fd, + bool apply_tty_stdin) { + + ExecInput std_input; + + assert(context); + + std_input = context->std_input; if (is_terminal_input(std_input) && !apply_tty_stdin) return EXEC_INPUT_NULL; @@ -398,6 +407,9 @@ static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0) return EXEC_INPUT_NULL; + if (std_input == EXEC_INPUT_DATA && context->stdin_data_size == 0) + return EXEC_INPUT_NULL; + return std_input; } @@ -433,7 +445,7 @@ static int setup_input( return STDIN_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); + i = fixup_input(context, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); switch (i) { @@ -463,6 +475,16 @@ static int setup_input( (void) fd_nonblock(named_iofds[STDIN_FILENO], false); return dup2(named_iofds[STDIN_FILENO], STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; + case EXEC_INPUT_DATA: { + int fd; + + fd = acquire_data_fd(context->stdin_data, context->stdin_data_size, 0); + if (fd < 0) + return fd; + + return move_fd(fd, STDIN_FILENO, false); + } + default: assert_not_reached("Unknown input type"); } @@ -507,7 +529,7 @@ static int setup_output( return STDERR_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); + i = fixup_input(context, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); o = fixup_output(context->std_output, socket_fd); if (fileno == STDERR_FILENO) { @@ -536,8 +558,8 @@ static int setup_output( if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input)) return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno); - /* If the input is connected to anything that's not a /dev/null, inherit that... */ - if (i != EXEC_INPUT_NULL) + /* If the input is connected to anything that's not a /dev/null or a data fd, inherit that... */ + if (!IN_SET(i, EXEC_INPUT_NULL, EXEC_INPUT_DATA)) return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */ @@ -3517,6 +3539,9 @@ void exec_context_done(ExecContext *c) { c->log_level_max = -1; exec_context_free_log_extra_fields(c); + + c->stdin_data = mfree(c->stdin_data); + c->stdin_data_size = 0; } int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) { @@ -4608,6 +4633,7 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = { [EXEC_INPUT_TTY_FAIL] = "tty-fail", [EXEC_INPUT_SOCKET] = "socket", [EXEC_INPUT_NAMED_FD] = "fd", + [EXEC_INPUT_DATA] = "data", }; DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); diff --git a/src/core/execute.h b/src/core/execute.h index ab9d0dbe2d..d1cf7dca93 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -37,6 +37,8 @@ typedef struct ExecParameters ExecParameters; #include "namespace.h" #include "nsflags.h" +#define EXEC_STDIN_DATA_MAX (64U*1024U*1024U) + typedef enum ExecUtmpMode { EXEC_UTMP_INIT, EXEC_UTMP_LOGIN, @@ -52,6 +54,7 @@ typedef enum ExecInput { EXEC_INPUT_TTY_FAIL, EXEC_INPUT_SOCKET, EXEC_INPUT_NAMED_FD, + EXEC_INPUT_DATA, _EXEC_INPUT_MAX, _EXEC_INPUT_INVALID = -1 } ExecInput; @@ -163,6 +166,9 @@ struct ExecContext { ExecOutput std_error; char *stdio_fdname[3]; + void *stdin_data; + size_t stdin_data_size; + nsec_t timer_slack_nsec; bool stdio_as_fds; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index ffc3e20359..73b13977ed 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -41,6 +41,8 @@ $1.RemoveIPC, config_parse_bool, 0, $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.StandardInputText, config_parse_exec_input_text, 0, offsetof($1, exec_context) +$1.StandardInputData, config_parse_exec_input_data, 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 a3950b8d72..cff3779f4b 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -45,6 +45,7 @@ #include "escape.h" #include "fd-util.h" #include "fs-util.h" +#include "hexdecoct.h" #include "io-util.h" #include "ioprio.h" #include "journal-util.h" @@ -882,6 +883,131 @@ int config_parse_exec_input( return 0; } +int config_parse_exec_input_text( + 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) { + + _cleanup_free_ char *unescaped = NULL, *resolved = NULL; + ExecContext *c = data; + Unit *u = userdata; + size_t sz; + void *p; + int r; + + assert(data); + assert(filename); + assert(line); + assert(rvalue); + + if (isempty(rvalue)) { + /* Reset if the empty string is assigned */ + c->stdin_data = mfree(c->stdin_data); + c->stdin_data_size = 0; + return 0; + } + + r = cunescape(rvalue, 0, &unescaped); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text, ignoring: %s", rvalue); + return 0; + } + + r = unit_full_printf(u, unescaped, &resolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", unescaped); + return 0; + } + + sz = strlen(resolved); + if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */ + c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) { + log_syntax(unit, LOG_ERR, filename, line, r, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX); + return 0; + } + + p = realloc(c->stdin_data, c->stdin_data_size + sz + 1); + if (!p) + return log_oom(); + + *((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n'; + + c->stdin_data = p; + c->stdin_data_size += sz + 1; + + return 0; +} + +int config_parse_exec_input_data( + 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) { + + _cleanup_free_ char *cleaned = NULL; + _cleanup_free_ void *p = NULL; + ExecContext *c = data; + size_t sz; + void *q; + int r; + + assert(data); + assert(filename); + assert(line); + assert(rvalue); + + if (isempty(rvalue)) { + /* Reset if the empty string is assigned */ + c->stdin_data = mfree(c->stdin_data); + c->stdin_data_size = 0; + return 0; + } + + /* Be tolerant to whitespace. Remove it all before decoding */ + cleaned = strdup(rvalue); + if (!cleaned) + return log_oom(); + delete_chars(cleaned, WHITESPACE); + + r = unbase64mem(cleaned, (size_t) -1, &p, &sz); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", cleaned); + return 0; + } + + assert(sz > 0); + + if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */ + c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) { + log_syntax(unit, LOG_ERR, filename, line, r, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX); + return 0; + } + + q = realloc(c->stdin_data, c->stdin_data_size + sz); + if (!q) + return log_oom(); + + memcpy((uint8_t*) q + c->stdin_data_size, p, sz); + + c->stdin_data = q; + c->stdin_data_size += sz; + + return 0; +} + int config_parse_exec_output(const char *unit, const char *filename, unsigned line, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 58d335807b..bba1259ceb 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -48,6 +48,8 @@ int config_parse_socket_bindtodevice(const char *unit, const char *filename, uns 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_exec_input_text(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_data(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); int config_parse_exec_cpu_sched_policy(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/service.c b/src/core/service.c index 4b912fc3b9..823c4c4166 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -591,10 +591,8 @@ static int service_add_default_dependencies(Service *s) { static void service_fix_output(Service *s) { assert(s); - /* If nothing has been explicitly configured, patch default - * output in. If input is socket/tty we avoid this however, - * since in that case we want output to default to the same - * place as we read input from. */ + /* If nothing has been explicitly configured, patch default output in. If input is socket/tty we avoid this + * however, since in that case we want output to default to the same place as we read input from. */ if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && s->exec_context.std_output == EXEC_OUTPUT_INHERIT && @@ -604,6 +602,10 @@ static void service_fix_output(Service *s) { if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT && s->exec_context.std_input == EXEC_INPUT_NULL) s->exec_context.std_output = UNIT(s)->manager->default_std_output; + + if (s->exec_context.std_input == EXEC_INPUT_NULL && + s->exec_context.stdin_data_size > 0) + s->exec_context.std_input = EXEC_INPUT_DATA; } static int service_setup_bus_name(Service *s) { diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 52750872bc..8b189322b1 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -28,6 +28,7 @@ #include "errno-list.h" #include "escape.h" #include "hashmap.h" +#include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" #include "list.h" @@ -273,6 +274,34 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "sv", "TasksMax", "t", t); goto finish; + + } else if (streq(field, "StandardInputText")) { + _cleanup_free_ char *unescaped = NULL; + + r = cunescape(eq, 0, &unescaped); + if (r < 0) + return log_error_errno(r, "Failed to unescape text '%s': %m", eq); + + if (!strextend(&unescaped, "\n", NULL)) + return log_oom(); + + /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic + * interface anyway */ + + r = sd_bus_message_append(m, "s", "StandardInputData"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + goto finish; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); @@ -366,7 +395,32 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "KeyringMode", "CollectMode")) r = sd_bus_message_append(m, "v", "s", eq); - else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { + else if (streq(field, "StandardInputData")) { + _cleanup_free_ char *cleaned = NULL; + _cleanup_free_ void *decoded = NULL; + size_t sz; + + cleaned = strdup(eq); + if (!cleaned) + return log_oom(); + + delete_chars(cleaned, WHITESPACE); + + r = unbase64mem(cleaned, (size_t) -1, &decoded, &sz); + if (r < 0) + return log_error_errno(r, "Failed to decode base64 data '%s': %m", cleaned); + + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', decoded, sz); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { bool ignore; const char *s; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9d9b45f08a..8b701103dc 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -55,6 +55,7 @@ #include "fs-util.h" #include "glob-util.h" #include "hostname-util.h" +#include "hexdecoct.h" #include "initreq.h" #include "install.h" #include "io-util.h" @@ -4977,6 +4978,24 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); return 0; + + } else if (contents[1] == SD_BUS_TYPE_BYTE && streq(name, "StandardInputData")) { + _cleanup_free_ char *h = NULL; + const void *p; + size_t sz; + ssize_t n; + + r = sd_bus_message_read_array(m, 'y', &p, &sz); + if (r < 0) + return bus_log_parse_error(r); + + n = base64mem(p, sz, &h); + if (n < 0) + return log_oom(); + + print_prop(name, "%s", h); + + return 0; } break; |