summaryrefslogtreecommitdiff
path: root/lib-src
diff options
context:
space:
mode:
authorStefan Kangas <stefankangas@gmail.com>2022-09-06 02:05:18 +0200
committerStefan Kangas <stefankangas@gmail.com>2022-09-06 02:05:18 +0200
commit6a19f2a024b4cede80e2896318696008d1dd1b21 (patch)
tree4295e750c4151f90522de8a6c89a7fcf94c79980 /lib-src
parentb648634982bb52be2b21e92d4aeb837621b5ec63 (diff)
downloademacs-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.c75
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. */