emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 6a19f2a024: Add new --timeout flag to emacsclient


From: Stefan Kangas
Subject: master 6a19f2a024: Add new --timeout flag to emacsclient
Date: Mon, 5 Sep 2022 20:05:44 -0400 (EDT)

branch: master
commit 6a19f2a024b4cede80e2896318696008d1dd1b21
Author: Stefan Kangas <stefankangas@gmail.com>
Commit: Stefan Kangas <stefankangas@gmail.com>

    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.
---
 doc/emacs/misc.texi   |  7 +++++
 doc/man/emacsclient.1 | 11 +++++---
 etc/NEWS              |  7 ++++-
 lib-src/emacsclient.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 92 insertions(+), 8 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index df74577592..d8ad0bee34 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 e5d1bbe09a..83c8a366f8 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 e99c2f2198..b61b88d6fb 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1003,10 +1003,15 @@ 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
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 73c8e45a86..15acb4589a 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.  */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]