From 7e418860ad2a7cd5d99737a4a7d187cf0f3d686c Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 5 Sep 2022 15:50:20 +0200 Subject: [PATCH] Add new --timeout flag to emacsclient * lib-src/emacsclient.c (DEFAULT_TIMEOUT): New constant. (timeout): New static variable. (longopts, shortopts, decode_options, print_help_and_exit): Add new flag --timeout. (set_socket_timeout, check_socket_timeout): New helper functions. (main): Display a status message or exit after Emacs has not responded for a while, depending on above new --timeout flag. (Bug#50849) --- lib-src/emacsclient.c | 70 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 73c8e45a86..7bc8fc3d4d 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -64,6 +64,8 @@ Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. # define egetenv(VAR) getenv (VAR) +# define DEFAULT_TIMEOUT (30) + #endif /* !WINDOWSNT */ #include @@ -144,6 +146,9 @@ Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. /* If non-NULL, the filename of the authentication file. */ static char const *server_file; +/* If non-NULL, seconds to wait before timing out. */ +static uintmax_t timeout; + /* If non-NULL, the tramp prefix emacs must use to find the files. */ static char const *tramp_prefix; @@ -178,6 +183,7 @@ Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. { "server-file", required_argument, NULL, 'f' }, { "display", required_argument, NULL, 'd' }, { "parent-id", required_argument, NULL, 'p' }, + { "timeout", required_argument, NULL, 'w' }, { "tramp", required_argument, NULL, 'T' }, { 0, 0, 0, 0 } }; @@ -185,7 +191,7 @@ Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. /* Short options, in the same order as the corresponding long options. There is no '-p' short option. */ static char const shortopts[] = - "nqueHVtca:F:" + "nqueHVtcaw:F:" #ifdef SOCKETS_IN_FILE_SYSTEM "s:" #endif @@ -496,7 +502,7 @@ decode_options (int argc, char **argv) int opt = getopt_long_only (argc, argv, shortopts, longopts, NULL); if (opt < 0) break; - + char* endptr; switch (opt) { case 0: @@ -530,6 +536,17 @@ decode_options (int argc, char **argv) nowait = true; break; + case 'w': + timeout = strtoumax (optarg, &endptr, 10); + if (timeout <= 0 || + ((timeout == INTMAX_MAX || timeout == INTMAX_MIN) + && errno == ERANGE)) + { + fprintf (stderr, "Invalid timeout: \"%s\"\n", optarg); + exit (EXIT_FAILURE); + } + break; + case 'e': eval = true; break; @@ -671,6 +688,7 @@ print_help_and_exit (void) Set the parameters of a new frame\n\ -e, --eval Evaluate the FILE arguments as ELisp expressions\n\ -n, --no-wait Don't wait for the server to return\n\ +-w, --timeout Seconds to wait before timing out\n\ -q, --quiet Don't display messages on success\n\ -u, --suppress-output Don't display return values from the server\n\ -d DISPLAY, --display=DISPLAY\n\ @@ -1870,6 +1888,33 @@ start_daemon_and_retry_set_socket (void) return emacs_socket; } +static void +set_socket_timeout (HSOCKET socket, int seconds) +{ +#ifndef WINDOWSNT + struct timeval timeout; + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout); +#else + DWORD timeout = seconds * 1000; + setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof timeout); +#endif +} + +static bool +check_socket_timeout (int rl) +{ +#ifndef WINDOWSNT + return (rl == -1) + && (errno == EAGAIN) + && (errno == EWOULDBLOCK); +#else + return (rl == SOCKET_ERROR) + && (WSAGetLastError() != WSAETIMEDOUT); +#endif +} + int main (int argc, char **argv) { @@ -2086,15 +2131,34 @@ main (int argc, char **argv) } fflush (stdout); + set_socket_timeout (emacs_socket, timeout > 0 ? timeout : DEFAULT_TIMEOUT); /* Now, wait for an answer and print any messages. */ while (exit_status == EXIT_SUCCESS) { + bool retry = true; + bool msg_showed = false; do { act_on_signals (emacs_socket); rl = recv (emacs_socket, string, BUFSIZ, 0); + retry = check_socket_timeout (rl); + if (retry) + { + if (timeout > 0) + { + /* Don't retry if we were given a --timeout flag. */ + fprintf (stderr, "\nServer not responding; timed out after %lu seconds", + timeout); + retry = false; + } + else if (!msg_showed) + { + msg_showed = true; + fprintf (stderr, "\nServer not responding; use Ctrl+C to break"); + } + } } - while (rl < 0 && errno == EINTR); + while ((rl < 0 && errno == EINTR) || retry); if (rl <= 0) break; -- 2.30.2