mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 17:51:22 +03:00
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.
This commit is contained in:
parent
11f5d82507
commit
08f3be7a38
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user