]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
machinectl: fix race when opening new shells with "machinectl shell"
authorLennart Poettering <lennart@poettering.net>
Wed, 7 Oct 2015 18:10:48 +0000 (20:10 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 7 Oct 2015 18:10:48 +0000 (20:10 +0200)
Previously, we'd allocate the TTY, spawn a service on it, but
immediately start processing the TTY and forwarding it to whatever the
commnd was started on. This is however problematic, as the TTY might get
actually opened only much later by the service. We'll hence first get
EIOs on the master as the other side is still closed, and hence
considered it hung up and terminated the session.

With this change we add a flag to the pty forwarding logic:
PTY_FORWARD_IGNORE_INITIAL_VHANGUP. If set, we'll ignore all hangups
(i.e. EIOs) on the master PTY until the first byte is successfully read.
From that point on we consider a hangup/EIO a regular connection termination. This
way, we handle the race: when we get EIO initially we'll ignore it,
until the connection is properly set up, at which time we start
honouring it.

src/machine/machinectl.c
src/nspawn/nspawn.c
src/run/run.c
src/shared/ptyfwd.c
src/shared/ptyfwd.h

index 17a186eb0e29d578bc9884a35712d9666d337db7..e75b1833283bacd1c6d2feb7a1b075890e7666c2 100644 (file)
@@ -1173,7 +1173,7 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r
         return 0;
 }
 
-static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) {
+static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
         char last_char = 0;
         bool machine_died;
         int ret = 0, r;
@@ -1192,7 +1192,7 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo
         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
         sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
 
-        r = pty_forward_new(event, master, ignore_vhangup, false, forward);
+        r = pty_forward_new(event, master, flags, forward);
         if (r < 0)
                 return log_error_errno(r, "Failed to create PTY forwarder: %m");
 
@@ -1203,7 +1203,7 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo
         pty_forward_get_last_char(*forward, &last_char);
 
         machine_died =
-                ignore_vhangup &&
+                (flags & PTY_FORWARD_IGNORE_VHANGUP) &&
                 pty_forward_get_ignore_vhangup(*forward) == 0;
 
         *forward = pty_forward_free(*forward);
@@ -1286,7 +1286,7 @@ static int login_machine(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return process_forward(event, &forward, master, true, machine);
+        return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
 }
 
 static int shell_machine(int argc, char *argv[], void *userdata) {
@@ -1390,7 +1390,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return process_forward(event, &forward, master, false, machine);
+        return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, machine);
 }
 
 static int remove_image(int argc, char *argv[], void *userdata) {
index f4a2e3d9babfed4b2c9771995106a56ca207c594..ab93f98df41f87816a7c10897be07ee935a68b0f 100644 (file)
@@ -3512,7 +3512,7 @@ int main(int argc, char *argv[]) {
 
                 rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
 
-                r = pty_forward_new(event, master, true, !interactive, &forward);
+                r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), &forward);
                 if (r < 0) {
                         log_error_errno(r, "Failed to create PTY forwarder: %m");
                         goto finish;
index 62687591f416d527307ee3c23e5198ec1795d516..bb18dd4383e19592420d4751f4cb0df8aa58a673 100644 (file)
@@ -828,7 +828,7 @@ static int start_transient_service(
                 if (!arg_quiet)
                         log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
 
-                r = pty_forward_new(event, master, false, false, &forward);
+                r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward);
                 if (r < 0)
                         return log_error_errno(r, "Failed to create PTY forwarder: %m");
 
index 789f217efc88b4eb8fb8afd4dc7f59512247f5c0..7749f205408b43691366c534dcc00cb953c36562 100644 (file)
@@ -32,6 +32,8 @@ struct PTYForward {
 
         int master;
 
+        PTYForwardFlags flags;
+
         sd_event_source *stdin_event_source;
         sd_event_source *stdout_event_source;
         sd_event_source *master_event_source;
@@ -41,8 +43,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 +54,7 @@ struct PTYForward {
         bool master_writable:1;
         bool master_hangup:1;
 
-        /* Continue reading after hangup? */
-        bool ignore_vhangup:1;
+        bool read_from_master:1;
 
         bool last_char_set:1;
         char last_char;
@@ -100,6 +99,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;
 
@@ -179,7 +190,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;
@@ -190,8 +201,10 @@ static int shovel(PTYForward *f) {
                                         log_error_errno(errno, "read(): %m");
                                         return sd_event_exit(f->event, EXIT_FAILURE);
                                 }
-                        }  else
+                        }  else {
+                                f->read_from_master = true;
                                 f->out_buffer_full += (size_t) k;
+                        }
                 }
 
                 if (f->stdout_writable && f->out_buffer_full > 0) {
@@ -302,8 +315,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 +326,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 +336,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 +355,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;
 
@@ -429,16 +440,20 @@ 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) {
+        if (b)
+                f->flags |= PTY_FORWARD_IGNORE_VHANGUP;
+        else
+                f->flags &= ~PTY_FORWARD_IGNORE_VHANGUP;
+
+        if (!ignore_vhangup(f)) {
 
                 /* We shall now react to vhangup()s? Let's check
                  * immediately if we might be in one */
@@ -455,5 +470,5 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) {
 int pty_forward_get_ignore_vhangup(PTYForward *f) {
         assert(f);
 
-        return f->ignore_vhangup;
+        return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP);
 }
index 6f84e4036a7e67881f8a88d11650a736aa7099bc..9b3214221bd2990a206ffc00d5b81a845da72278 100644 (file)
 
 typedef struct PTYForward PTYForward;
 
-int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, bool read_only, PTYForward **f);
+typedef enum PTYForwardFlags {
+        PTY_FORWARD_READ_ONLY = 1,
+
+        /* Continue reading after hangup? */
+        PTY_FORWARD_IGNORE_VHANGUP = 2,
+
+        /* Continue reading after hangup but only if we never read anything else? */
+        PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
+} PTYForwardFlags;
+
+int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
 PTYForward *pty_forward_free(PTYForward *f);
 
 int pty_forward_get_last_char(PTYForward *f, char *ch);