From 0d49c72d27dd400062c5632a14c4ff225de22832 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 27 Jun 2016 10:16:51 +0100 Subject: [PATCH] p2v: Fix timeout error when connecting to unresponsive ssh server (RHBZ#1350363). If an ssh server is completely unresponsive (ie. it doesn't even send back ICMP errors), then eventually ssh times out after ConnectTimeout seconds [ConnectTimeout is an ssh option]. Unfortunately the miniexpect timeout (default 60 seconds) was less than ConnectTimeout so we never saw the ssh error. Instead we would see the internal error: remote server closed the connection unexpectedly, waiting for: password prompt This commit sets the ssh ConnectTimeout to an explicit value, and sets the miniexpect timeout to be 20 seconds larger than ConnectTimeout, so that in most cases we will see the ssh error message before miniexpect times out. Thanks: Ming Xie for finding and diagnosing the problem (cherry picked from commit 0b836c40f2674e8632212363b9ad4f7781e5622e) --- p2v/ssh.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/p2v/ssh.c b/p2v/ssh.c index 93c4c55..f4adde4 100644 --- a/p2v/ssh.c +++ b/p2v/ssh.c @@ -57,6 +57,8 @@ #include "miniexpect.h" #include "p2v.h" +#define SSH_TIMEOUT 60 /* seconds */ + char *v2v_version = NULL; char **input_drivers = NULL; char **output_drivers = NULL; @@ -309,6 +311,7 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) { size_t i, j, nr_args, count; char port_str[64]; + char connect_timeout_str[128]; CLEANUP_FREE /* [sic] */ const char **args = NULL; mexp_h *h; const int ovecsize = 12; @@ -328,9 +331,9 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) nr_args = guestfs_int_count_strings (extra_args); if (using_password_auth) - nr_args += 11; - else nr_args += 13; + else + nr_args += 15; args = malloc (sizeof (char *) * nr_args); if (args == NULL) { perror ("malloc"); @@ -346,6 +349,10 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) args[j++] = config->username ? config->username : "root"; args[j++] = "-o"; /* Host key will always be novel. */ args[j++] = "StrictHostKeyChecking=no"; + args[j++] = "-o"; /* ConnectTimeout */ + snprintf (connect_timeout_str, sizeof connect_timeout_str, + "ConnectTimeout=%d", SSH_TIMEOUT); + args[j++] = connect_timeout_str; if (using_password_auth) { /* Only use password authentication. */ args[j++] = "-o"; @@ -366,12 +373,30 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) args[j++] = NULL; assert (j == nr_args); +#if DEBUG_STDERR && 0 + fputs ("ssh command: ", stderr); + for (i = 0; i < nr_args - 1; ++i) { + if (i > 0) fputc (' ', stderr); + fputs (args[i], stderr); + } + fputc ('\n', stderr); +#endif + + /* Create the miniexpect handle. */ h = mexp_spawnv ("ssh", (char **) args); if (h == NULL) { set_ssh_internal_error ("ssh: mexp_spawnv: %m"); return NULL; } + /* We want the ssh ConnectTimeout to be less than the miniexpect + * timeout, so that if the server is completely unresponsive we + * still see the error from ssh, not a timeout from miniexpect. The + * obvious solution to this is to set ConnectTimeout (above) and to + * set the miniexpect timeout to be a little bit larger. + */ + mexp_set_timeout (h, SSH_TIMEOUT + 20); + if (using_password_auth && config->password && strlen (config->password) > 0) { CLEANUP_FREE char *ssh_message = NULL; -- 1.8.3.1