aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorStefan Kangas2022-09-06 02:05:18 +0200
committerStefan Kangas2022-09-06 02:05:18 +0200
commit6a19f2a024b4cede80e2896318696008d1dd1b21 (patch)
tree4295e750c4151f90522de8a6c89a7fcf94c79980 /lib-src
parentb648634982bb52be2b21e92d4aeb837621b5ec63 (diff)
downloademacs-6a19f2a024b4cede80e2896318696008d1dd1b21.tar.gz
emacs-6a19f2a024b4cede80e2896318696008d1dd1b21.zip
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 @@
1/* Client process that communicates with GNU Emacs acting as server. 1/* Client process that communicates with GNU Emacs acting as server.
2 2
3Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. 3Copyright (C) 1986-2022 Free Software Foundation, Inc.
4 4
5This file is part of GNU Emacs. 5This file is part of GNU Emacs.
6 6
@@ -55,6 +55,8 @@ char *w32_getenv (const char *);
55# include <sys/socket.h> 55# include <sys/socket.h>
56# include <sys/un.h> 56# include <sys/un.h>
57 57
58# define DEFAULT_TIMEOUT (30)
59
58# define SOCKETS_IN_FILE_SYSTEM 60# define SOCKETS_IN_FILE_SYSTEM
59 61
60# define INVALID_SOCKET (-1) 62# define INVALID_SOCKET (-1)
@@ -144,6 +146,9 @@ static char const *socket_name;
144/* If non-NULL, the filename of the authentication file. */ 146/* If non-NULL, the filename of the authentication file. */
145static char const *server_file; 147static char const *server_file;
146 148
149/* Seconds to wait before timing out (0 means wait forever). */
150static uintmax_t timeout;
151
147/* If non-NULL, the tramp prefix emacs must use to find the files. */ 152/* If non-NULL, the tramp prefix emacs must use to find the files. */
148static char const *tramp_prefix; 153static char const *tramp_prefix;
149 154
@@ -178,6 +183,7 @@ static struct option const longopts[] =
178 { "server-file", required_argument, NULL, 'f' }, 183 { "server-file", required_argument, NULL, 'f' },
179 { "display", required_argument, NULL, 'd' }, 184 { "display", required_argument, NULL, 'd' },
180 { "parent-id", required_argument, NULL, 'p' }, 185 { "parent-id", required_argument, NULL, 'p' },
186 { "timeout", required_argument, NULL, 'w' },
181 { "tramp", required_argument, NULL, 'T' }, 187 { "tramp", required_argument, NULL, 'T' },
182 { 0, 0, 0, 0 } 188 { 0, 0, 0, 0 }
183}; 189};
@@ -185,7 +191,7 @@ static struct option const longopts[] =
185/* Short options, in the same order as the corresponding long options. 191/* Short options, in the same order as the corresponding long options.
186 There is no '-p' short option. */ 192 There is no '-p' short option. */
187static char const shortopts[] = 193static char const shortopts[] =
188 "nqueHVtca:F:" 194 "nqueHVtca:F:w:"
189#ifdef SOCKETS_IN_FILE_SYSTEM 195#ifdef SOCKETS_IN_FILE_SYSTEM
190 "s:" 196 "s:"
191#endif 197#endif
@@ -497,6 +503,7 @@ decode_options (int argc, char **argv)
497 if (opt < 0) 503 if (opt < 0)
498 break; 504 break;
499 505
506 char* endptr;
500 switch (opt) 507 switch (opt)
501 { 508 {
502 case 0: 509 case 0:
@@ -530,6 +537,17 @@ decode_options (int argc, char **argv)
530 nowait = true; 537 nowait = true;
531 break; 538 break;
532 539
540 case 'w':
541 timeout = strtoumax (optarg, &endptr, 10);
542 if (timeout <= 0 ||
543 ((timeout == INTMAX_MAX || timeout == INTMAX_MIN)
544 && errno == ERANGE))
545 {
546 fprintf (stderr, "Invalid timeout: \"%s\"\n", optarg);
547 exit (EXIT_FAILURE);
548 }
549 break;
550
533 case 'e': 551 case 'e':
534 eval = true; 552 eval = true;
535 break; 553 break;
@@ -671,6 +689,7 @@ The following OPTIONS are accepted:\n\
671 Set the parameters of a new frame\n\ 689 Set the parameters of a new frame\n\
672-e, --eval Evaluate the FILE arguments as ELisp expressions\n\ 690-e, --eval Evaluate the FILE arguments as ELisp expressions\n\
673-n, --no-wait Don't wait for the server to return\n\ 691-n, --no-wait Don't wait for the server to return\n\
692-w, --timeout Seconds to wait before timing out\n\
674-q, --quiet Don't display messages on success\n\ 693-q, --quiet Don't display messages on success\n\
675-u, --suppress-output Don't display return values from the server\n\ 694-u, --suppress-output Don't display return values from the server\n\
676-d DISPLAY, --display=DISPLAY\n\ 695-d DISPLAY, --display=DISPLAY\n\
@@ -1870,6 +1889,33 @@ start_daemon_and_retry_set_socket (void)
1870 return emacs_socket; 1889 return emacs_socket;
1871} 1890}
1872 1891
1892static void
1893set_socket_timeout (HSOCKET socket, int seconds)
1894{
1895#ifndef WINDOWSNT
1896 struct timeval timeout;
1897 timeout.tv_sec = seconds;
1898 timeout.tv_usec = 0;
1899 setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout);
1900#else
1901 DWORD timeout = seconds * 1000;
1902 setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof timeout);
1903#endif
1904}
1905
1906static bool
1907check_socket_timeout (int rl)
1908{
1909#ifndef WINDOWSNT
1910 return (rl == -1)
1911 && (errno == EAGAIN)
1912 && (errno == EWOULDBLOCK);
1913#else
1914 return (rl == SOCKET_ERROR)
1915 && (WSAGetLastError() == WSAETIMEDOUT);
1916#endif
1917}
1918
1873int 1919int
1874main (int argc, char **argv) 1920main (int argc, char **argv)
1875{ 1921{
@@ -2086,19 +2132,42 @@ main (int argc, char **argv)
2086 } 2132 }
2087 fflush (stdout); 2133 fflush (stdout);
2088 2134
2135 set_socket_timeout (emacs_socket, timeout > 0 ? timeout : DEFAULT_TIMEOUT);
2136 bool saw_response = false;
2089 /* Now, wait for an answer and print any messages. */ 2137 /* Now, wait for an answer and print any messages. */
2090 while (exit_status == EXIT_SUCCESS) 2138 while (exit_status == EXIT_SUCCESS)
2091 { 2139 {
2140 bool retry = true;
2141 bool msg_showed = quiet;
2092 do 2142 do
2093 { 2143 {
2094 act_on_signals (emacs_socket); 2144 act_on_signals (emacs_socket);
2095 rl = recv (emacs_socket, string, BUFSIZ, 0); 2145 rl = recv (emacs_socket, string, BUFSIZ, 0);
2146 retry = check_socket_timeout (rl);
2147 if (retry)
2148 {
2149 if (timeout > 0 && !saw_response)
2150 {
2151 /* Don't retry if we were given a --timeout flag. */
2152 fprintf (stderr, "\nServer not responding; timed out after %lu seconds",
2153 timeout);
2154 retry = false;
2155 }
2156 else if (!msg_showed)
2157 {
2158 msg_showed = true;
2159 fprintf (stderr, "\nServer not responding; use Ctrl+C to break");
2160 }
2161 }
2096 } 2162 }
2097 while (rl < 0 && errno == EINTR); 2163 while ((rl < 0 && errno == EINTR) || retry);
2098 2164
2099 if (rl <= 0) 2165 if (rl <= 0)
2100 break; 2166 break;
2101 2167
2168 if (msg_showed)
2169 fprintf (stderr, "\nGot response from server");
2170 saw_response = true;
2102 string[rl] = '\0'; 2171 string[rl] = '\0';
2103 2172
2104 /* Loop over all NL-terminated messages. */ 2173 /* Loop over all NL-terminated messages. */