diff options
author | Stefan Kangas <stefankangas@gmail.com> | 2022-09-06 02:05:18 +0200 |
---|---|---|
committer | Stefan Kangas <stefankangas@gmail.com> | 2022-09-06 02:05:18 +0200 |
commit | 6a19f2a024b4cede80e2896318696008d1dd1b21 (patch) | |
tree | 4295e750c4151f90522de8a6c89a7fcf94c79980 /lib-src | |
parent | b648634982bb52be2b21e92d4aeb837621b5ec63 (diff) | |
download | emacs-6a19f2a024b4cede80e2896318696008d1dd1b21.tar.gz |
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)
* doc/emacs/misc.texi (emacsclient Options):
* doc/man/emacsclient.1: Document the above new option.
* etc/NEWS: Announce it.
Diffstat (limited to 'lib-src')
-rw-r--r-- | lib-src/emacsclient.c | 75 |
1 files changed, 72 insertions, 3 deletions
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 73c8e45a865..15acb4589a9 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -1,6 +1,6 @@ /* Client process that communicates with GNU Emacs acting as server. -Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. +Copyright (C) 1986-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -55,6 +55,8 @@ char *w32_getenv (const char *); # include <sys/socket.h> # include <sys/un.h> +# define DEFAULT_TIMEOUT (30) + # define SOCKETS_IN_FILE_SYSTEM # define INVALID_SOCKET (-1) @@ -144,6 +146,9 @@ static char const *socket_name; /* If non-NULL, the filename of the authentication file. */ static char const *server_file; +/* Seconds to wait before timing out (0 means wait forever). */ +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 @@ static struct option const longopts[] = { "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 @@ static struct option const longopts[] = /* Short options, in the same order as the corresponding long options. There is no '-p' short option. */ static char const shortopts[] = - "nqueHVtca:F:" + "nqueHVtca:F:w:" #ifdef SOCKETS_IN_FILE_SYSTEM "s:" #endif @@ -497,6 +503,7 @@ decode_options (int argc, char **argv) if (opt < 0) break; + char* endptr; switch (opt) { case 0: @@ -530,6 +537,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 +689,7 @@ The following OPTIONS are accepted:\n\ 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 +1889,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,19 +2132,42 @@ main (int argc, char **argv) } fflush (stdout); + set_socket_timeout (emacs_socket, timeout > 0 ? timeout : DEFAULT_TIMEOUT); + bool saw_response = false; /* Now, wait for an answer and print any messages. */ while (exit_status == EXIT_SUCCESS) { + bool retry = true; + bool msg_showed = quiet; do { act_on_signals (emacs_socket); rl = recv (emacs_socket, string, BUFSIZ, 0); + retry = check_socket_timeout (rl); + if (retry) + { + if (timeout > 0 && !saw_response) + { + /* 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; + if (msg_showed) + fprintf (stderr, "\nGot response from server"); + saw_response = true; string[rl] = '\0'; /* Loop over all NL-terminated messages. */ |