diff options
-rw-r--r-- | man/systemd-run.xml | 26 | ||||
-rw-r--r-- | src/core/dbus-execute.c | 89 | ||||
-rw-r--r-- | src/machine/machinectl.c | 2 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 2 | ||||
-rw-r--r-- | src/run/run.c | 425 | ||||
-rw-r--r-- | src/shared/ptyfwd.c | 33 | ||||
-rw-r--r-- | src/shared/ptyfwd.h | 4 |
7 files changed, 416 insertions, 165 deletions
diff --git a/man/systemd-run.xml b/man/systemd-run.xml index b9cec91d15..5fb4ee28eb 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -225,10 +225,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. </listitem> </varlistentry> - <xi:include href="user-system-options.xml" xpointer="user" /> - <xi:include href="user-system-options.xml" xpointer="system" /> - <xi:include href="user-system-options.xml" xpointer="host" /> - <xi:include href="user-system-options.xml" xpointer="machine" /> + <varlistentry> + <term><option>--pty</option></term> + <term><option>-t</option></term> + + <listitem><para>When invoking a command as service connects + its standard input and output to the invoking tty via a + pseudo TTY device. This allows invoking binaries as services + that expect interactive user input, such as an interactive + command shells.</para></listitem> + </varlistentry> <varlistentry> <term><option>--on-active=</option></term> @@ -278,6 +284,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. <command>set-property</command> command.</para> </listitem> </varlistentry> + <xi:include href="user-system-options.xml" xpointer="user" /> + <xi:include href="user-system-options.xml" xpointer="system" /> + <xi:include href="user-system-options.xml" xpointer="host" /> + <xi:include href="user-system-options.xml" xpointer="machine" /> + <xi:include href="standard-options.xml" xpointer="help" /> <xi:include href="standard-options.xml" xpointer="version" /> </variablelist> @@ -333,6 +344,13 @@ Dec 08 20:44:38 container systemd[1]: Started /bin/touch /tmp/foo. -- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. -- Dec 08 20:44:48 container systemd[1]: Starting /bin/touch /tmp/foo... Dec 08 20:44:48 container systemd[1]: Started /bin/touch /tmp/foo.</programlisting> + + <para>The following command invokes <filename>/bin/bash</filename> + as a service passing its standard input, output and error to + the calling TTY.</para> + + <programlisting># systemd-run -t /bin/bash</programlisting> + </refsect1> <refsect1> diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index bbcd6106ad..a9f7971cde 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -31,11 +31,12 @@ #include "strv.h" #include "fileio.h" #include "execute.h" -#include "dbus-execute.h" #include "capability.h" #include "env-util.h" #include "af-list.h" #include "namespace.h" +#include "path-util.h" +#include "dbus-execute.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -845,6 +846,92 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "TTYPath")) { + const char *tty; + + r = sd_bus_message_read(message, "s", &tty); + if (r < 0) + return r; + + if (!path_is_absolute(tty)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY device not absolute path"); + + if (mode != UNIT_CHECK) { + char *t; + + t = strdup(tty); + if (!t) + return -ENOMEM; + + free(c->tty_path); + c->tty_path = t; + + unit_write_drop_in_private_format(u, mode, name, "TTYPath=%s\n", tty); + } + + return 1; + + } else if (streq(name, "StandardInput")) { + const char *s; + ExecInput p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = exec_input_from_string(s); + if (p < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard input name"); + + if (mode != UNIT_CHECK) { + c->std_input = p; + + unit_write_drop_in_private_format(u, mode, name, "StandardInput=%s\n", exec_input_to_string(p)); + } + + return 1; + + + } else if (streq(name, "StandardOutput")) { + const char *s; + ExecOutput p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = exec_output_from_string(s); + if (p < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard output name"); + + if (mode != UNIT_CHECK) { + c->std_output = p; + + unit_write_drop_in_private_format(u, mode, name, "StandardOutput=%s\n", exec_output_to_string(p)); + } + + return 1; + + } else if (streq(name, "StandardError")) { + const char *s; + ExecOutput p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = exec_output_from_string(s); + if (p < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard error name"); + + if (mode != UNIT_CHECK) { + c->std_error = p; + + unit_write_drop_in_private_format(u, mode, name, "StandardError=%s\n", exec_output_to_string(p)); + } + + return 1; + } else if (streq(name, "Environment")) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index cde0c6af13..4eebcb7716 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1178,7 +1178,7 @@ static int login_machine(int argc, char *argv[], void *userdata) { sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - r = pty_forward_new(event, master, &forward); + r = pty_forward_new(event, master, true, &forward); if (r < 0) return log_error_errno(r, "Failed to create PTY forwarder: %m"); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 72f7d66782..f1f9b7828d 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3519,7 +3519,7 @@ int main(int argc, char *argv[]) { /* simply exit on sigchld */ sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); - r = pty_forward_new(event, master, &forward); + r = pty_forward_new(event, master, true, &forward); if (r < 0) { log_error_errno(r, "Failed to create PTY forwarder: %m"); goto finish; diff --git a/src/run/run.c b/src/run/run.c index 7a80223918..828aa5cf93 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -23,7 +23,9 @@ #include <getopt.h> #include "sd-bus.h" +#include "sd-event.h" #include "bus-util.h" +#include "event-util.h" #include "strv.h" #include "build.h" #include "unit-name.h" @@ -31,6 +33,7 @@ #include "path-util.h" #include "bus-error.h" #include "calendarspec.h" +#include "ptyfwd.h" static bool arg_scope = false; static bool arg_remain_after_exit = false; @@ -48,6 +51,7 @@ 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 usec_t arg_on_active = 0; static usec_t arg_on_boot = 0; static usec_t arg_on_startup = 0; @@ -77,7 +81,8 @@ static void help(void) { " --uid=USER Run as system user\n" " --gid=GROUP Run as system group\n" " --nice=NICE Nice level\n" - " --setenv=NAME=VALUE Set environment\n\n" + " --setenv=NAME=VALUE Set environment\n" + " -t --pty Run service on pseudo tty\n\n" "Timer options:\n\n" " --on-active=SEC Run after seconds\n" " --on-boot=SEC Run after seconds from machine was booted up\n" @@ -109,6 +114,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SERVICE_TYPE, ARG_NICE, ARG_SETENV, + ARG_TTY, ARG_ON_ACTIVE, ARG_ON_BOOT, ARG_ON_STARTUP, @@ -137,6 +143,7 @@ static int parse_argv(int argc, char *argv[]) { { "nice", required_argument, NULL, ARG_NICE }, { "setenv", required_argument, NULL, ARG_SETENV }, { "property", required_argument, NULL, 'p' }, + { "tty", no_argument, NULL, 't' }, { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, { "on-boot", required_argument, NULL, ARG_ON_BOOT }, { "on-startup", required_argument, NULL, ARG_ON_STARTUP }, @@ -153,7 +160,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hrH:M:p:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hrH:M:p:t", options, NULL)) >= 0) switch (c) { @@ -244,6 +251,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case 't': + arg_pty = true; + break; + case ARG_ON_ACTIVE: r = parse_sec(optarg, &arg_on_active); @@ -339,6 +350,11 @@ 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."); + return -EINVAL; + } + if (arg_scope && with_timer()) { log_error("Timer options are not supported in --scope mode."); return -EINVAL; @@ -352,11 +368,15 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int transient_unit_set_properties(sd_bus_message *m, UnitType t) { +static int transient_unit_set_properties(sd_bus_message *m, char **properties) { char **i; int r; - STRV_FOREACH(i, t == UNIT_TIMER ? arg_timer_property : arg_property) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); + if (r < 0) + return r; + + STRV_FOREACH(i, properties) { r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) return r; @@ -373,9 +393,12 @@ static int transient_unit_set_properties(sd_bus_message *m, UnitType t) { return r; } - r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); - if (r < 0) - return r; + return 0; +} + +static int transient_cgroup_set_properties(sd_bus_message *m) { + int r; + assert(m); if (!isempty(arg_slice)) { _cleanup_free_ char *slice; @@ -389,21 +412,36 @@ static int transient_unit_set_properties(sd_bus_message *m, UnitType t) { return r; } - if (arg_send_sighup && t != UNIT_TIMER) { + return 0; +} + +static int transient_kill_set_properties(sd_bus_message *m) { + int r; + assert(m); + + if (arg_send_sighup) { r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup); if (r < 0) return r; } - return 0; + return r; } -static int transient_service_set_properties(sd_bus_message *m, char **argv) { +static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) { int r; assert(m); - r = transient_unit_set_properties(m, UNIT_SERVICE); + r = transient_unit_set_properties(m, arg_property); + if (r < 0) + return r; + + r = transient_kill_set_properties(m); + if (r < 0) + return r; + + r = transient_cgroup_set_properties(m); if (r < 0) return r; @@ -437,6 +475,31 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv) { return r; } + if (pty_path) { + const char *e; + + r = sd_bus_message_append(m, + "(sv)(sv)(sv)(sv)", + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_path); + if (r < 0) + return r; + + e = getenv("TERM"); + if (e) { + char *n; + + n = strappenda("TERM=", e); + r = sd_bus_message_append(m, + "(sv)", + "Environment", "as", 1, n); + if (r < 0) + return r; + } + } + if (!strv_isempty(arg_environment)) { r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) @@ -517,12 +580,32 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv) { return 0; } +static int transient_scope_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, arg_property); + if (r < 0) + return r; + + r = transient_kill_set_properties(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); + if (r < 0) + return r; + + return 0; +} + static int transient_timer_set_properties(sd_bus_message *m) { int r; assert(m); - r = transient_unit_set_properties(m, UNIT_TIMER); + r = transient_unit_set_properties(m, arg_timer_property); if (r < 0) return r; @@ -565,22 +648,6 @@ static int transient_timer_set_properties(sd_bus_message *m) { return 0; } -static int transient_scope_set_properties(sd_bus_message *m) { - int r; - - assert(m); - - r = transient_unit_set_properties(m, UNIT_SCOPE); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); - if (r < 0) - return r; - - return 0; -} - static int start_transient_service( sd_bus *bus, char **argv, @@ -588,11 +655,26 @@ static int start_transient_service( _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_free_ char *service = NULL; + _cleanup_close_ int master = -1; + const char *pty_path = NULL; int r; assert(bus); assert(argv); + if (arg_pty) { + master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); + if (master < 0) + return log_error_errno(errno, "Failed to acquire pseudo tty: %m"); + + pty_path = ptsname(master); + if (!pty_path) + return log_error_errno(errno, "Failed to determine tty name: %m"); + + if (unlockpt(master) < 0) + return log_error_errno(errno, "Failed to unlock tty: %m"); + } + if (arg_unit) { service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); if (!service) @@ -610,17 +692,17 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); - /* name and mode */ + /* Name and mode */ r = sd_bus_message_append(m, "ss", service, "fail"); if (r < 0) return bus_log_create_error(r); - /* properties */ + /* Properties */ r = sd_bus_message_open_container(m, 'a', "(sv)"); if (r < 0) return bus_log_create_error(r); - r = transient_service_set_properties(m, argv); + r = transient_service_set_properties(m, argv, pty_path); if (r < 0) return bus_log_create_error(r); @@ -628,138 +710,51 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); - /* aux */ + /* Auxiliary units */ r = sd_bus_message_append(m, "a(sa(sv))", 0); if (r < 0) return bus_log_create_error(r); - /* send dbus */ r = sd_bus_call(bus, m, 0, error, NULL); if (r < 0) return bus_log_create_error(r); - log_info("Running as unit %s.", service); - - return 0; -} - -static int start_transient_timer( - sd_bus *bus, - char **argv, - sd_bus_error *error) { - - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - _cleanup_free_ char *timer = NULL, *service = NULL; - int r; - - assert(bus); - assert(argv); + if (master >= 0) { + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_event_unref_ sd_event *event = NULL; + sigset_t mask; + char last_char = 0; - if (arg_unit) { - switch(unit_name_to_type(arg_unit)) { - case UNIT_SERVICE: - service = strdup(arg_unit); - timer = unit_name_change_suffix(service, ".timer"); - if (!timer) - return log_oom(); - break; - - case UNIT_TIMER: - timer = strdup(arg_unit); - service = unit_name_change_suffix(timer, ".service"); - if (!service) - return log_oom(); - break; - - default: - service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); - if (!service) - return log_oom(); - - timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer"); - if (!timer) - return log_oom(); - - break; - } - } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) || - (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0)) - return log_oom(); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return bus_log_create_error(r); - - /* name and mode */ - r = sd_bus_message_append(m, "ss", timer, "fail"); - if (r < 0) - return bus_log_create_error(r); - - /* properties */ - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - r = transient_timer_set_properties(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - if (argv[0]) { - r = sd_bus_message_open_container(m, 'a', "(sa(sv))"); + r = sd_event_default(&event); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Failed to get event loop: %m"); - r = sd_bus_message_open_container(m, 'r', "sa(sv)"); - if (r < 0) - return bus_log_create_error(r); + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1); + assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); - r = sd_bus_message_append(m, "s", service); - if (r < 0) - return bus_log_create_error(r); + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); + log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service); - r = transient_service_set_properties(m, argv); + r = pty_forward_new(event, master, false, &forward); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Failed to create PTY forwarder: %m"); - r = sd_bus_message_close_container(m); + r = sd_event_loop(event); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Failed to run event loop: %m"); - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); + pty_forward_last_char(forward, &last_char); - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } else { - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return bus_log_create_error(r); - } + forward = pty_forward_free(forward); - /* send dbus */ - r = sd_bus_call(bus, m, 0, error, NULL); - if (r < 0) - return bus_log_create_error(r); + if (last_char != '\n') + fputc('\n', stdout); - log_info("Running as unit %s.", timer); - if (argv[0]) - log_info("Will run as unit %s.", service); + } else + log_info("Running as unit %s.", service); return 0; } @@ -769,9 +764,9 @@ static int start_transient_scope( char **argv, sd_bus_error *error) { + _cleanup_strv_free_ char **env = NULL, **user_env = NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_free_ char *scope = NULL; - _cleanup_strv_free_ char **env = NULL, **user_env = NULL; int r; assert(bus); @@ -785,21 +780,21 @@ static int start_transient_scope( return log_oom(); r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); - /* name and mode */ + /* Name and Mode */ r = sd_bus_message_append(m, "ss", scope, "fail"); if (r < 0) return bus_log_create_error(r); - /* properties */ + /* Properties */ r = sd_bus_message_open_container(m, 'a', "(sv)"); if (r < 0) return bus_log_create_error(r); @@ -879,8 +874,132 @@ static int start_transient_scope( log_info("Running as unit %s.", scope); execvpe(argv[0], argv, env); - log_error_errno(errno, "Failed to execute: %m"); - return -errno; + + return log_error_errno(errno, "Failed to execute: %m"); +} + +static int start_transient_timer( + sd_bus *bus, + char **argv, + sd_bus_error *error) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + _cleanup_free_ char *timer = NULL, *service = NULL; + int r; + + assert(bus); + assert(argv); + + if (arg_unit) { + switch(unit_name_to_type(arg_unit)) { + + case UNIT_SERVICE: + service = strdup(arg_unit); + if (!service) + return log_oom(); + + timer = unit_name_change_suffix(service, ".timer"); + if (!timer) + return log_oom(); + break; + + case UNIT_TIMER: + timer = strdup(arg_unit); + if (!timer) + return log_oom(); + + service = unit_name_change_suffix(timer, ".service"); + if (!service) + return log_oom(); + break; + + default: + service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); + if (!service) + return log_oom(); + + timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer"); + if (!timer) + return log_oom(); + + break; + } + } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) || + (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0)) + return log_oom(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + /* Name and Mode */ + r = sd_bus_message_append(m, "ss", timer, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_timer_set_properties(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sa(sv))"); + if (r < 0) + return bus_log_create_error(r); + + if (argv[0]) { + r = sd_bus_message_open_container(m, 'r', "sa(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", service); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_service_set_properties(m, argv, NULL); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + /* send dbus */ + r = sd_bus_call(bus, m, 0, error, NULL); + if (r < 0) + return bus_log_create_error(r); + + log_info("Running as unit %s.", timer); + if (argv[0]) + log_info("Will run as unit %s.", service); + + return 0; } int main(int argc, char* argv[]) { diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 085d374ed8..11356e2def 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -53,6 +53,11 @@ struct PTYForward { bool master_writable:1; bool master_hangup:1; + bool repeat:1; + + bool last_char_set:1; + char last_char; + char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; size_t in_buffer_full, out_buffer_full; @@ -169,11 +174,12 @@ static int shovel(PTYForward *f) { * might be cause by vhangup() or * temporary closing of everything on * the other side, we treat it like - * EAGAIN here and try again. */ + * EAGAIN here and try again, unless + * repeat is off. */ - if (errno == EAGAIN || errno == EIO) + if (errno == EAGAIN || (errno == EIO && f->repeat)) f->master_readable = false; - else if (errno == EPIPE || errno == ECONNRESET) { + else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) { f->master_readable = f->master_writable = false; f->master_hangup = true; @@ -203,6 +209,12 @@ static int shovel(PTYForward *f) { } } else { + + if (k > 0) { + f->last_char = f->out_buffer[k-1]; + f->last_char_set = true; + } + assert(f->out_buffer_full >= (size_t) k); memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k); f->out_buffer_full -= k; @@ -285,7 +297,7 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo * return 0; } -int pty_forward_new(sd_event *event, int master, PTYForward **ret) { +int pty_forward_new(sd_event *event, int master, bool repeat, PTYForward **ret) { _cleanup_(pty_forward_freep) PTYForward *f = NULL; struct winsize ws; int r; @@ -294,6 +306,8 @@ int pty_forward_new(sd_event *event, int master, PTYForward **ret) { if (!f) return -ENOMEM; + f->repeat = repeat; + if (event) f->event = sd_event_ref(event); else { @@ -388,3 +402,14 @@ PTYForward *pty_forward_free(PTYForward *f) { return NULL; } + +int pty_forward_last_char(PTYForward *f, char *ch) { + assert(f); + assert(ch); + + if (!f->last_char_set) + return -ENXIO; + + *ch = f->last_char; + return 0; +} diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index 5a612fd597..d7b658e181 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -28,7 +28,9 @@ typedef struct PTYForward PTYForward; -int pty_forward_new(sd_event *event, int master, PTYForward **f); +int pty_forward_new(sd_event *event, int master, bool repeat, PTYForward **f); PTYForward *pty_forward_free(PTYForward *f); +int pty_forward_last_char(PTYForward *f, char *ch); + DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); |