From 958a23698c9f1b9b804aa79d410b11b07998b223 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 12 Jun 2016 11:08:41 -0700 Subject: [PATCH] Try other addresses when connecting to multihomed * src/process.c (decode_status, Fmake_network_process): Support (connect . ADDRINFOS) status. (connecting): New function, to support (connect . ADDRINFOS). (connect_network_socket, check_for_dns, wait_for_socket_fds) (wait_while_connecting, wait_reading_process_output, status_notify): Use it. (connect_network_socket) [!WINDOWSNT]: If the connection failed with ECONNREFUSED and there are other addresses to try, do not signal an error; instead, loop around to try the next address. (wait_reading_process_output): Advance to the next address if there are multiple addresses and the first remaining address failed. * src/process.h (struct Lisp_Process.status): Adjust comment to describe (connect . ADDRINFOS). --- src/process.c | 45 ++++++++++++++++++++++++++++++++------------- src/process.h | 4 +++- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/process.c b/src/process.c index e669278..a72bf20 100644 --- a/src/process.c +++ b/src/process.c @@ -542,6 +542,9 @@ decode_status (Lisp_Object l, Lisp_Object *symbol, Lisp_Object *code, { Lisp_Object tem; + if (CONSP (l) && EQ (XCAR (l), Qconnect)) + l = XCAR (l); + if (SYMBOLP (l)) { *symbol = l; @@ -3106,6 +3109,12 @@ finish_after_tls_connection (Lisp_Object proc) } #endif +static bool +connecting (struct Lisp_Process *p) +{ + return CONSP (p->status) && EQ (XCAR (p->status), Qconnect); +} + static void connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos, Lisp_Object use_external_socket_p) @@ -3288,9 +3297,10 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos, eassert (FD_ISSET (s, &fdset)); if (getsockopt (s, SOL_SOCKET, SO_ERROR, &xerrno, &len) < 0) report_file_error ("Failed getsockopt", Qnil); - if (xerrno) + if (xerrno == 0) + break; + if (xerrno != ECONNREFUSED || NILP (addrinfos)) report_file_errno ("Failed connect", Qnil, xerrno); - break; } #endif /* !WINDOWSNT */ @@ -3399,7 +3409,8 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos, /* We may get here if connect did succeed immediately. However, in that case, we still need to signal this like a non-blocking connection. */ - pset_status (p, Qconnect); + if (! (connecting (p) && EQ (XCDR (p->status), addrinfos))) + pset_status (p, Fcons (Qconnect, addrinfos)); if (!FD_ISSET (inch, &connect_wait_mask)) { FD_SET (inch, &connect_wait_mask); @@ -3960,7 +3971,7 @@ usage: (make-network-process &rest ARGS) */) if (!p->is_server && NILP (addrinfos)) { p->dns_request = dns_request; - p->status = Qconnect; + p->status = list1 (Qconnect); return proc; } #endif @@ -4673,7 +4684,7 @@ check_for_dns (Lisp_Object proc) addrinfos = Fnreverse (addrinfos); } /* The DNS lookup failed. */ - else if (EQ (p->status, Qconnect)) + else if (connecting (p)) { deactivate_process (proc); pset_status (p, (list2 @@ -4686,7 +4697,7 @@ check_for_dns (Lisp_Object proc) free_dns_request (proc); /* This process should not already be connected (or killed). */ - if (!EQ (p->status, Qconnect)) + if (! connecting (p)) return Qnil; return addrinfos; @@ -4697,8 +4708,7 @@ check_for_dns (Lisp_Object proc) static void wait_for_socket_fds (Lisp_Object process, char const *name) { - while (XPROCESS (process)->infd < 0 - && EQ (XPROCESS (process)->status, Qconnect)) + while (XPROCESS (process)->infd < 0 && connecting (XPROCESS (process))) { add_to_log ("Waiting for socket from %s...", build_string (name)); wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0); @@ -4708,7 +4718,7 @@ wait_for_socket_fds (Lisp_Object process, char const *name) static void wait_while_connecting (Lisp_Object process) { - while (EQ (XPROCESS (process)->status, Qconnect)) + while (connecting (XPROCESS (process))) { add_to_log ("Waiting for connection..."); wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0); @@ -5010,7 +5020,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, update_status (wait_proc); if (wait_proc && ! EQ (wait_proc->status, Qrun) - && ! EQ (wait_proc->status, Qconnect)) + && ! connecting (wait_proc)) { bool read_some_bytes = false; @@ -5520,9 +5530,18 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, #endif if (xerrno) { - p->tick = ++process_tick; - pset_status (p, list2 (Qfailed, make_number (xerrno))); + Lisp_Object addrinfos + = connecting (p) ? XCDR (p->status) : Qnil; + if (!NILP (addrinfos)) + XSETCDR (p->status, XCDR (addrinfos)); + else + { + p->tick = ++process_tick; + pset_status (p, list2 (Qfailed, make_number (xerrno))); + } deactivate_process (proc); + if (!NILP (addrinfos)) + connect_network_socket (proc, addrinfos, Qnil); } else { @@ -6999,7 +7018,7 @@ status_notify (struct Lisp_Process *deleting_process, /* If process is still active, read any output that remains. */ while (! EQ (p->filter, Qt) - && ! EQ (p->status, Qconnect) + && ! connecting (p) && ! EQ (p->status, Qlisten) /* Network or serial process not stopped: */ && ! EQ (p->command, Qt) diff --git a/src/process.h b/src/process.h index 4430377..6c227bc 100644 --- a/src/process.h +++ b/src/process.h @@ -83,7 +83,9 @@ struct Lisp_Process Lisp_Object mark; /* Symbol indicating status of process. - This may be a symbol: run, open, closed, listen, connect, or failed. + This may be a symbol: run, open, closed, listen, or failed. + Or it may be a pair (connect . ADDRINFOS) where ADDRINFOS is + a list of remaining (PROTOCOL . ADDRINFO) pairs to try. Or it may be (failed ERR) where ERR is an integer, string or symbol. Or it may be a list, whose car is stop, exit or signal and whose cdr is a pair (EXIT_CODE . COREDUMP_FLAG) -- 2.5.5