mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-27 13:57:26 +03:00
run: add new --pipe option for including "systemd-run" commands in shell pipelines
In this mode, we'll directly connect stdin/stdout/stderr of the invoked service with whatever systemd-run itself is invoked on. This allows inclusion of "systemd-run" commands in shell pipelines, as unlike "--pty" this means EOF of stdin/stdout/stderr are propagated independently. If --pty and --pipe are combined systemd-run will automatically pick the right choice for the context it is invoked in, i.e. --pty when invoked on a TTY, and --pipe otherwise.
This commit is contained in:
parent
c13ee7cc8b
commit
5dca7739e8
3
TODO
3
TODO
@ -27,9 +27,6 @@ Features:
|
||||
* dissect: when we discover squashfs, don't claim we had a "writable" partition
|
||||
in systemd-dissect
|
||||
|
||||
* systemd-run should have a way how to connect a pair of pipes to
|
||||
stdout/stderr/stdin of the invoked service
|
||||
|
||||
* set LockPersonality= on all our services
|
||||
|
||||
* Add AddUser= setting to unit files, similar to DynamicUser=1 which however
|
||||
|
@ -219,14 +219,31 @@
|
||||
<term><option>--pty</option></term>
|
||||
<term><option>-t</option></term>
|
||||
|
||||
<listitem><para>When invoking the command, the transient service connects its standard input and output to the
|
||||
terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running binaries
|
||||
that expect interactive user input as services, such as interactive command shells.</para>
|
||||
<listitem><para>When invoking the command, the transient service connects its standard input, output and error
|
||||
to the terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running
|
||||
binaries that expect interactive user input/output as services, such as interactive command shells.</para>
|
||||
|
||||
<para>Note that
|
||||
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>shell</command> command is usually a better alternative for requesting a new, interactive login
|
||||
session on the local host or a local container.</para></listitem>
|
||||
session on the local host or a local container.</para>
|
||||
|
||||
<para>See below for details on how this switch combines with <option>--pipe</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--pipe</option></term>
|
||||
<term><option>-P</option></term>
|
||||
|
||||
<listitem><para>If specified, standard input, output, and error of the transient service are inherited from the
|
||||
<command>systemd-run</command> command itself. Use this option in order to use <command>systemd-run</command>
|
||||
within shell pipelines. Note that this mode is not suitable for interactive command shells and similar, as the
|
||||
service process will not be made TTY controller when invoked on a terminal. Use <option>--pty</option> instead
|
||||
in that case.</para>
|
||||
|
||||
<para>When both <option>--pipe</option> and <option>--pty</option> are used in combination the more appropriate
|
||||
option is automatically determined and used. Specifically, when invoked with standard input, output and error
|
||||
connected to a TTY <option>--pty</option> is used, and otherwise <option>--pipe</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -61,7 +61,12 @@ static int arg_nice = 0;
|
||||
static bool arg_nice_set = false;
|
||||
static char **arg_environment = NULL;
|
||||
static char **arg_property = NULL;
|
||||
static bool arg_pty = false;
|
||||
static enum {
|
||||
ARG_STDIO_NONE, /* The default, as it is for normal services, stdin connected to /dev/null, and stdout+stderr to the journal */
|
||||
ARG_STDIO_PTY, /* Interactive behaviour, requested by --pty: we allocate a pty and connect it to the TTY we are invoked from */
|
||||
ARG_STDIO_DIRECT, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */
|
||||
ARG_STDIO_AUTO, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */
|
||||
} arg_stdio = ARG_STDIO_NONE;
|
||||
static usec_t arg_on_active = 0;
|
||||
static usec_t arg_on_boot = 0;
|
||||
static usec_t arg_on_startup = 0;
|
||||
@ -106,7 +111,9 @@ static void help(void) {
|
||||
" --gid=GROUP Run as system group\n"
|
||||
" --nice=NICE Nice level\n"
|
||||
" -E --setenv=NAME=VALUE Set environment\n"
|
||||
" -t --pty Run service on pseudo tty\n"
|
||||
" -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
|
||||
" STDERR\n"
|
||||
" -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
|
||||
" -q --quiet Suppress information messages during runtime\n\n"
|
||||
"Timer options:\n"
|
||||
" --on-active=SECONDS Run after SECONDS delay\n"
|
||||
@ -170,8 +177,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "nice", required_argument, NULL, ARG_NICE },
|
||||
{ "setenv", required_argument, NULL, 'E' },
|
||||
{ "property", required_argument, NULL, 'p' },
|
||||
{ "tty", no_argument, NULL, 't' }, /* deprecated */
|
||||
{ "tty", no_argument, NULL, 't' }, /* deprecated alias */
|
||||
{ "pty", no_argument, NULL, 't' },
|
||||
{ "pipe", no_argument, NULL, 'P' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "on-active", required_argument, NULL, ARG_ON_ACTIVE },
|
||||
{ "on-boot", required_argument, NULL, ARG_ON_BOOT },
|
||||
@ -190,7 +198,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPq", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
@ -279,8 +287,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case 't':
|
||||
arg_pty = true;
|
||||
case 't': /* --pty */
|
||||
if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
|
||||
arg_stdio = ARG_STDIO_AUTO;
|
||||
else
|
||||
arg_stdio = ARG_STDIO_PTY;
|
||||
break;
|
||||
|
||||
case 'P': /* --pipe */
|
||||
if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
|
||||
arg_stdio = ARG_STDIO_AUTO;
|
||||
else
|
||||
arg_stdio = ARG_STDIO_DIRECT;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
@ -373,6 +391,16 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert_not_reached("Unhandled option");
|
||||
}
|
||||
|
||||
|
||||
if (arg_stdio == ARG_STDIO_AUTO) {
|
||||
/* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
|
||||
* to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
|
||||
* pipeline, but we are neatly interactive with tty-level isolation otherwise. */
|
||||
arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
|
||||
ARG_STDIO_PTY :
|
||||
ARG_STDIO_DIRECT;
|
||||
}
|
||||
|
||||
if ((optind >= argc) && (!arg_unit || !with_timer())) {
|
||||
log_error("Command line to execute required.");
|
||||
return -EINVAL;
|
||||
@ -393,18 +421,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_pty && (with_timer() || arg_scope)) {
|
||||
log_error("--pty is not compatible in timer or --scope mode.");
|
||||
if (arg_stdio != ARG_STDIO_NONE && (with_timer() || arg_scope)) {
|
||||
log_error("--pty/--pipe is not compatible in timer or --scope mode.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) {
|
||||
log_error("--pty is only supported when connecting to the local system or containers.");
|
||||
if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE) {
|
||||
log_error("--pty/--pipe is only supported when connecting to the local system or containers.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_pty && arg_no_block) {
|
||||
log_error("--pty is not compatible with --no-block.");
|
||||
if (arg_stdio != ARG_STDIO_NONE && arg_no_block) {
|
||||
log_error("--pty/--pipe is not compatible with --no-block.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -481,6 +509,7 @@ static int transient_kill_set_properties(sd_bus_message *m) {
|
||||
}
|
||||
|
||||
static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
|
||||
bool send_term = false;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -497,7 +526,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_wait || arg_pty) {
|
||||
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
|
||||
r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -534,8 +563,6 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
|
||||
}
|
||||
|
||||
if (pty_path) {
|
||||
const char *e;
|
||||
|
||||
r = sd_bus_message_append(m,
|
||||
"(sv)(sv)(sv)(sv)",
|
||||
"StandardInput", "s", "tty",
|
||||
@ -545,6 +572,23 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
send_term = true;
|
||||
|
||||
} else if (arg_stdio == ARG_STDIO_DIRECT) {
|
||||
r = sd_bus_message_append(m,
|
||||
"(sv)(sv)(sv)",
|
||||
"StandardInputFileDescriptor", "h", STDIN_FILENO,
|
||||
"StandardOutputFileDescriptor", "h", STDOUT_FILENO,
|
||||
"StandardErrorFileDescriptor", "h", STDERR_FILENO);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
send_term = isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
|
||||
}
|
||||
|
||||
if (send_term) {
|
||||
const char *e;
|
||||
|
||||
e = getenv("TERM");
|
||||
if (e) {
|
||||
char *n;
|
||||
@ -875,7 +919,7 @@ static int start_transient_service(
|
||||
assert(argv);
|
||||
assert(retval);
|
||||
|
||||
if (arg_pty) {
|
||||
if (arg_stdio == ARG_STDIO_PTY) {
|
||||
|
||||
if (arg_transport == BUS_TRANSPORT_LOCAL) {
|
||||
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
|
||||
@ -1000,7 +1044,7 @@ static int start_transient_service(
|
||||
if (!arg_quiet)
|
||||
log_info("Running as unit: %s", service);
|
||||
|
||||
if (arg_wait || master >= 0) {
|
||||
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
|
||||
_cleanup_(run_context_free) RunContext c = {};
|
||||
_cleanup_free_ char *path = NULL;
|
||||
const char *mt;
|
||||
@ -1440,7 +1484,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
|
||||
* connection */
|
||||
if (arg_wait || arg_pty)
|
||||
if (arg_wait || arg_stdio != ARG_STDIO_NONE)
|
||||
r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
|
||||
else
|
||||
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
|
||||
|
Loading…
x
Reference in New Issue
Block a user