diff options
Diffstat (limited to 'src/shared/ptyfwd.c')
-rw-r--r-- | src/shared/ptyfwd.c | 167 |
1 files changed, 115 insertions, 52 deletions
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 789f217efc..24055e772b 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -19,19 +17,35 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> #include <sys/epoll.h> #include <sys/ioctl.h> -#include <limits.h> +#include <sys/time.h> #include <termios.h> +#include <unistd.h> + +#include "sd-event.h" -#include "util.h" +#include "alloc-util.h" +#include "fd-util.h" +#include "log.h" +#include "macro.h" #include "ptyfwd.h" +#include "time-util.h" struct PTYForward { sd_event *event; int master; + PTYForwardFlags flags; + sd_event_source *stdin_event_source; sd_event_source *stdout_event_source; sd_event_source *master_event_source; @@ -41,8 +55,6 @@ struct PTYForward { struct termios saved_stdin_attr; struct termios saved_stdout_attr; - bool read_only:1; - bool saved_stdin:1; bool saved_stdout:1; @@ -54,8 +66,9 @@ struct PTYForward { bool master_writable:1; bool master_hangup:1; - /* Continue reading after hangup? */ - bool ignore_vhangup:1; + bool read_from_master:1; + + bool done:1; bool last_char_set:1; char last_char; @@ -65,10 +78,54 @@ struct PTYForward { usec_t escape_timestamp; unsigned escape_counter; + + PTYForwardHandler handler; + void *userdata; }; #define ESCAPE_USEC (1*USEC_PER_SEC) +static void pty_forward_disconnect(PTYForward *f) { + + if (f) { + f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); + f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); + + f->master_event_source = sd_event_source_unref(f->master_event_source); + f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source); + f->event = sd_event_unref(f->event); + + if (f->saved_stdout) + tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr); + if (f->saved_stdin) + tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr); + + f->saved_stdout = f->saved_stdin = false; + } + + /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */ + fd_nonblock(STDIN_FILENO, false); + fd_nonblock(STDOUT_FILENO, false); +} + +static int pty_forward_done(PTYForward *f, int rcode) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + assert(f); + + if (f->done) + return 0; + + e = sd_event_ref(f->event); + + f->done = true; + pty_forward_disconnect(f); + + if (f->handler) + return f->handler(f, rcode, f->userdata); + else + return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode); +} + static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { const char *p; @@ -100,6 +157,18 @@ static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { return false; } +static bool ignore_vhangup(PTYForward *f) { + assert(f); + + if (f->flags & PTY_FORWARD_IGNORE_VHANGUP) + return true; + + if ((f->flags & PTY_FORWARD_IGNORE_INITIAL_VHANGUP) && !f->read_from_master) + return true; + + return false; +} + static int shovel(PTYForward *f) { ssize_t k; @@ -124,7 +193,7 @@ static int shovel(PTYForward *f) { f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); } else { log_error_errno(errno, "read(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); + return pty_forward_done(f, -errno); } } else if (k == 0) { /* EOF on stdin */ @@ -133,12 +202,10 @@ static int shovel(PTYForward *f) { f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); } else { - /* Check if ^] has been - * pressed three times within - * one second. If we get this - * we quite immediately. */ + /* Check if ^] has been pressed three times within one second. If we get this we quite + * immediately. */ if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) - return sd_event_exit(f->event, EXIT_FAILURE); + return pty_forward_done(f, -ECANCELED); f->in_buffer_full += (size_t) k; } @@ -158,7 +225,7 @@ static int shovel(PTYForward *f) { f->master_event_source = sd_event_source_unref(f->master_event_source); } else { log_error_errno(errno, "write(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); + return pty_forward_done(f, -errno); } } else { assert(f->in_buffer_full >= (size_t) k); @@ -179,7 +246,7 @@ static int shovel(PTYForward *f) { * EAGAIN here and try again, unless * ignore_vhangup is off. */ - if (errno == EAGAIN || (errno == EIO && f->ignore_vhangup)) + if (errno == EAGAIN || (errno == EIO && ignore_vhangup(f))) f->master_readable = false; else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) { f->master_readable = f->master_writable = false; @@ -188,10 +255,12 @@ static int shovel(PTYForward *f) { f->master_event_source = sd_event_source_unref(f->master_event_source); } else { log_error_errno(errno, "read(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); + return pty_forward_done(f, -errno); } - } else + } else { + f->read_from_master = true; f->out_buffer_full += (size_t) k; + } } if (f->stdout_writable && f->out_buffer_full > 0) { @@ -207,7 +276,7 @@ static int shovel(PTYForward *f) { f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); } else { log_error_errno(errno, "write(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); + return pty_forward_done(f, -errno); } } else { @@ -230,7 +299,7 @@ static int shovel(PTYForward *f) { if ((f->out_buffer_full <= 0 || f->stdout_hangup) && (f->in_buffer_full <= 0 || f->master_hangup)) - return sd_event_exit(f->event, EXIT_SUCCESS); + return pty_forward_done(f, 0); } return 0; @@ -302,8 +371,7 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo * int pty_forward_new( sd_event *event, int master, - bool ignore_vhangup, - bool read_only, + PTYForwardFlags flags, PTYForward **ret) { _cleanup_(pty_forward_freep) PTYForward *f = NULL; @@ -314,8 +382,7 @@ int pty_forward_new( if (!f) return -ENOMEM; - f->read_only = read_only; - f->ignore_vhangup = ignore_vhangup; + f->flags = flags; if (event) f->event = sd_event_ref(event); @@ -325,7 +392,7 @@ int pty_forward_new( return r; } - if (!read_only) { + if (!(flags & PTY_FORWARD_READ_ONLY)) { r = fd_nonblock(STDIN_FILENO, true); if (r < 0) return r; @@ -344,7 +411,7 @@ int pty_forward_new( if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) (void) ioctl(master, TIOCSWINSZ, &ws); - if (!read_only) { + if (!(flags & PTY_FORWARD_READ_ONLY)) { if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) { struct termios raw_stdin_attr; @@ -395,26 +462,8 @@ int pty_forward_new( } PTYForward *pty_forward_free(PTYForward *f) { - - if (f) { - sd_event_source_unref(f->stdin_event_source); - sd_event_source_unref(f->stdout_event_source); - sd_event_source_unref(f->master_event_source); - sd_event_unref(f->event); - - if (f->saved_stdout) - tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr); - if (f->saved_stdin) - tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr); - - free(f); - } - - /* STDIN/STDOUT should not be nonblocking normally, so let's - * unconditionally reset it */ - fd_nonblock(STDIN_FILENO, false); - fd_nonblock(STDOUT_FILENO, false); - + pty_forward_disconnect(f); + free(f); return NULL; } @@ -429,16 +478,17 @@ int pty_forward_get_last_char(PTYForward *f, char *ch) { return 0; } -int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) { +int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) { int r; assert(f); - if (f->ignore_vhangup == ignore_vhangup) + if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) return 0; - f->ignore_vhangup = ignore_vhangup; - if (!f->ignore_vhangup) { + SET_FLAG(f->flags, PTY_FORWARD_IGNORE_VHANGUP, b); + + if (!ignore_vhangup(f)) { /* We shall now react to vhangup()s? Let's check * immediately if we might be in one */ @@ -452,8 +502,21 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) { return 0; } -int pty_forward_get_ignore_vhangup(PTYForward *f) { +bool pty_forward_get_ignore_vhangup(PTYForward *f) { + assert(f); + + return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP); +} + +bool pty_forward_is_done(PTYForward *f) { + assert(f); + + return f->done; +} + +void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) { assert(f); - return f->ignore_vhangup; + f->handler = cb; + f->userdata = userdata; } |