summaryrefslogtreecommitdiff
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
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.
-rw-r--r--doc/emacs/misc.texi7
-rw-r--r--doc/man/emacsclient.111
-rw-r--r--etc/NEWS7
-rw-r--r--lib-src/emacsclient.c75
4 files changed, 92 insertions, 8 deletions
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index df74577592a..d8ad0bee34f 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -2089,6 +2089,13 @@ all server buffers are finished. You can take as long as you like to
edit the server buffers within Emacs, and they are @emph{not} killed
when you type @kbd{C-x #} in them.
+@item -w
+@itemx --timeout=@var{N}
+Wait for a response from Emacs for @var{N} seconds before giving up.
+If there is no response within that time, @command{emacsclient} will
+display a warning and exit. The default is @samp{0}, which means to
+wait forever.
+
@item --parent-id @var{id}
Open an @command{emacsclient} frame as a client frame in the parent X
window with id @var{id}, via the XEmbed protocol. Currently, this
diff --git a/doc/man/emacsclient.1 b/doc/man/emacsclient.1
index e5d1bbe09ae..83c8a366f8b 100644
--- a/doc/man/emacsclient.1
+++ b/doc/man/emacsclient.1
@@ -1,5 +1,5 @@
.\" See section COPYING for conditions for redistribution.
-.TH EMACSCLIENT 1 "2021-11-05" "GNU Emacs" "GNU"
+.TH EMACSCLIENT 1 "2022-09-05" "GNU Emacs" "GNU"
.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection
.\" other params are allowed: see man(7), man(1)
.SH NAME
@@ -87,9 +87,12 @@ Use TCP configuration file FILENAME for communication.
This can also be specified via the EMACS_SERVER_FILE environment variable.
.TP
.B \-n, \-\-no-wait
-Return
-immediately without waiting for you to "finish" the buffer in Emacs.
-If combined with --eval, this option is ignored.
+Return immediately without waiting for you to "finish" the buffer in
+Emacs. If combined with --eval, this option is ignored.
+.TP
+.B \-w, \-\-timeout=N
+How long to wait, in seconds, for Emacs to respond before giving up.
+The default is 0, which means to wait forever.
.TP
.B \-nw, \-t, \-\-tty
Open a new Emacs frame on the current terminal.
diff --git a/etc/NEWS b/etc/NEWS
index e99c2f21982..b61b88d6fbe 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1003,11 +1003,16 @@ suspicious and could be malicious.
** Emacs server and client changes
+++
-*** New command-line option '-r' for emacsclient.
+*** New command-line option '-r'/'--reuse-frame' for emacsclient.
With this command-line option, Emacs reuses an existing graphical client
frame if one exists; otherwise it creates a new frame.
+++
+*** New command-line option '-w N'/'--timeout=N' for emacsclient.
+With this command-line option, emacsclient will exit if Emacs does not
+respond within N seconds. The default is to wait forever.
+
++++
*** 'server-stop-automatically' can be used to automatically stop the server.
The Emacs server will be automatically stopped when certain conditions
are met. The conditions are given by the argument, which can be
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. */