diff options
| author | Stefan Kangas | 2022-09-06 02:05:18 +0200 |
|---|---|---|
| committer | Stefan Kangas | 2022-09-06 02:05:18 +0200 |
| commit | 6a19f2a024b4cede80e2896318696008d1dd1b21 (patch) | |
| tree | 4295e750c4151f90522de8a6c89a7fcf94c79980 /lib-src | |
| parent | b648634982bb52be2b21e92d4aeb837621b5ec63 (diff) | |
| download | emacs-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.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 @@ | |||
| 1 | /* Client process that communicates with GNU Emacs acting as server. | 1 | /* Client process that communicates with GNU Emacs acting as server. |
| 2 | 2 | ||
| 3 | Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc. | 3 | Copyright (C) 1986-2022 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | This file is part of GNU Emacs. | 5 | This 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. */ |
| 145 | static char const *server_file; | 147 | static char const *server_file; |
| 146 | 148 | ||
| 149 | /* Seconds to wait before timing out (0 means wait forever). */ | ||
| 150 | static 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. */ |
| 148 | static char const *tramp_prefix; | 153 | static 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. */ |
| 187 | static char const shortopts[] = | 193 | static 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 | ||
| 1892 | static void | ||
| 1893 | set_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 | |||
| 1906 | static bool | ||
| 1907 | check_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 | |||
| 1873 | int | 1919 | int |
| 1874 | main (int argc, char **argv) | 1920 | main (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. */ |