aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorPaul Eggert2018-11-26 08:25:36 -0800
committerPaul Eggert2018-11-26 11:39:44 -0800
commit0331f2f4c5d7d9221522e231ebd5e4f20868c2b7 (patch)
tree823516bbb6e7aa324390af1b3d1b280b5c758d6e /lib-src
parentf3328f995ee316cffa1a86117e6da2ba299d2c90 (diff)
downloademacs-0331f2f4c5d7d9221522e231ebd5e4f20868c2b7.tar.gz
emacs-0331f2f4c5d7d9221522e231ebd5e4f20868c2b7.zip
emacsclient: fix some races on POSIX systems
Fix some longstanding race conditions due to emacsclient’s use of ‘signal’ instead of ‘sigaction’ and its use of nested signal handlers. These races could cause premature exit or incorrect commands sent to Emacs. * lib-src/emacsclient.c (signal) [!WINDOWSNT]: Do not undef. (emacs_socket): Remove this static variable. It is now a parameter. (send_to_emacs): Do not exit merely because ‘send’ was interrupted. Instead, act on the signal if possible, and then retry the ‘send’. (pass_signal_to_emacs): Remove; now done by act_on_signals. (reinstall_handler_if_needed, handle_sigttou, handle_sigwinch) (install_handler): New functions. (got_sigcont, got_sigtstp, got_sigttou, got_sigwinch): New globals, used for more-portable signal handling. (handle_sigcont, handle_sigtstp): Just set the static var; other actions are now done later by act_on_signals. (install_handler): New function that arranges for signals to never be reset to default, on modern POSIX platforms. This fixes some races. (act_on_signals): New function. When acting on SIGCONT, don’t bother calling getpgrp if tcgetpgrp fails. (start_daemon_and_retry_set_socket): Return the socket rather than setting a global variable. All uses changed. (flush_stdout): New function that acts on signals received while flushing. (main): Use it. emacs_socket is now a local var. Act on signals received during recv.
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/emacsclient.c281
1 files changed, 196 insertions, 85 deletions
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 3c6215a0144..d544fa63356 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -41,6 +41,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
41char *w32_getenv (const char *); 41char *w32_getenv (const char *);
42# define egetenv(VAR) w32_getenv (VAR) 42# define egetenv(VAR) w32_getenv (VAR)
43 43
44# undef signal
45
44#else /* !WINDOWSNT */ 46#else /* !WINDOWSNT */
45 47
46# ifdef HAVE_NTGUI 48# ifdef HAVE_NTGUI
@@ -68,8 +70,6 @@ char *w32_getenv (const char *);
68 70
69#endif /* !WINDOWSNT */ 71#endif /* !WINDOWSNT */
70 72
71#undef signal
72
73#include <ctype.h> 73#include <ctype.h>
74#include <errno.h> 74#include <errno.h>
75#include <getopt.h> 75#include <getopt.h>
@@ -144,7 +144,7 @@ static char const *server_file;
144/* If non-NULL, the tramp prefix emacs must use to find the files. */ 144/* If non-NULL, the tramp prefix emacs must use to find the files. */
145static char const *tramp_prefix; 145static char const *tramp_prefix;
146 146
147/* PID of the Emacs server process. */ 147/* If nonzero, PID of the Emacs server process. */
148static pid_t emacs_pid; 148static pid_t emacs_pid;
149 149
150/* If non-NULL, a string that should form a frame parameter alist to 150/* If non-NULL, a string that should form a frame parameter alist to
@@ -734,10 +734,13 @@ fail (void)
734 734
735#if defined HAVE_SOCKETS && defined HAVE_INET_SOCKETS 735#if defined HAVE_SOCKETS && defined HAVE_INET_SOCKETS
736 736
737enum { AUTH_KEY_LENGTH = 64 }; 737# ifndef NO_SOCKETS_IN_FILE_SYSTEM
738static void act_on_signals (HSOCKET);
739# else
740static void act_on_signals (HSOCKET s) {}
741# endif
738 742
739/* Socket used to communicate with the Emacs server process. */ 743enum { AUTH_KEY_LENGTH = 64 };
740static HSOCKET emacs_socket = 0;
741 744
742static void 745static void
743sock_err_message (const char *function_name) 746sock_err_message (const char *function_name)
@@ -790,16 +793,22 @@ send_to_emacs (HSOCKET s, const char *data)
790 if (sblen == SEND_BUFFER_SIZE 793 if (sblen == SEND_BUFFER_SIZE
791 || (0 < sblen && send_buffer[sblen - 1] == '\n')) 794 || (0 < sblen && send_buffer[sblen - 1] == '\n'))
792 { 795 {
793 int sent = send (s, send_buffer, sblen, 0); 796 int sent;
794 if (sent < 0) 797 while ((sent = send (s, send_buffer, sblen, 0)) < 0)
795 { 798 {
796 message (true, "%s: failed to send %d bytes to socket: %s\n", 799 if (errno != EINTR)
797 progname, sblen, strerror (errno)); 800 {
798 fail (); 801 message (true, "%s: failed to send %d bytes to socket: %s\n",
802 progname, sblen, strerror (errno));
803 fail ();
804 }
805 /* Act on signals not requiring communication to Emacs,
806 but defer action on the others to avoid confusing the
807 communication currently in progress. */
808 act_on_signals (INVALID_SOCKET);
799 } 809 }
800 if (sent != sblen)
801 memmove (send_buffer, &send_buffer[sent], sblen - sent);
802 sblen -= sent; 810 sblen -= sent;
811 memmove (send_buffer, &send_buffer[sent], sblen);
803 } 812 }
804 813
805 dlen -= part; 814 dlen -= part;
@@ -1091,86 +1100,181 @@ socket_status (const char *name)
1091} 1100}
1092 1101
1093 1102
1094/* A signal handler that passes the signal to the Emacs process. 1103/* Signal handlers merely set a flag, to avoid race conditions on
1095 Useful for SIGWINCH. */ 1104 POSIXish systems. Non-POSIX platforms lacking sigaction make do
1096 1105 with traditional calls to 'signal'; races are rare so this usually
1106 works. Although this approach may treat multiple deliveries of SIG
1107 as a single delivery and may act on signals in a different order
1108 than received, that is OK for emacsclient. Also, this approach may
1109 omit output if a printf call is interrupted by a signal, but printf
1110 output is not that important (emacsclient does not check for printf
1111 errors, after all) so this is also OK for emacsclient. */
1112
1113/* Reinstall for SIG the signal handler HANDLER if needed. It is
1114 needed on a non-POSIX or traditional platform where an interrupt
1115 resets the signal handler to SIG_DFL. */
1097static void 1116static void
1098pass_signal_to_emacs (int signalnum) 1117reinstall_handler_if_needed (int sig, void (*handler) (int))
1099{ 1118{
1100 int old_errno = errno; 1119# ifndef SA_RESETHAND
1120 /* This is a platform without POSIX's sigaction. */
1121 signal (sig, handler);
1122# endif
1123}
1101 1124
1102 if (emacs_pid) 1125/* Flags for each signal, and handlers that set the flags. */
1103 kill (emacs_pid, signalnum);
1104 1126
1105 signal (signalnum, pass_signal_to_emacs); 1127static sig_atomic_t volatile
1106 errno = old_errno; 1128 got_sigcont, got_sigtstp, got_sigttou, got_sigwinch;
1129
1130static void
1131handle_sigcont (int sig)
1132{
1133 got_sigcont = 1;
1134 reinstall_handler_if_needed (sig, handle_sigcont);
1135}
1136static void
1137handle_sigtstp (int sig)
1138{
1139 got_sigtstp = 1;
1140 reinstall_handler_if_needed (sig, handle_sigtstp);
1141}
1142static void
1143handle_sigttou (int sig)
1144{
1145 got_sigttou = 1;
1146 reinstall_handler_if_needed (sig, handle_sigttou);
1147}
1148static void
1149handle_sigwinch (int sig)
1150{
1151 got_sigwinch = 1;
1152 reinstall_handler_if_needed (sig, handle_sigwinch);
1107} 1153}
1108 1154
1109/* Signal handler for SIGCONT; notify the Emacs process that it can 1155/* Install for signal SIG the handler HANDLER. However, if FLAG is
1110 now resume our tty frame. */ 1156 non-null and if the signal is currently being ignored, do not
1157 install the handler and keep *FLAG zero. */
1111 1158
1112static void 1159static void
1113handle_sigcont (int signalnum) 1160install_handler (int sig, void (*handler) (int), sig_atomic_t volatile *flag)
1114{ 1161{
1115 int old_errno = errno; 1162# ifdef SA_RESETHAND
1116 pid_t pgrp = getpgrp (); 1163 if (flag)
1117 pid_t tcpgrp = tcgetpgrp (STDOUT_FILENO);
1118
1119 if (tcpgrp == pgrp)
1120 { 1164 {
1121 /* We are in the foreground. */ 1165 struct sigaction oact;
1122 send_to_emacs (emacs_socket, "-resume \n"); 1166 if (sigaction (sig, NULL, &oact) == 0 && oact.sa_handler == SIG_IGN)
1167 return;
1123 } 1168 }
1124 else if (0 <= tcpgrp && tty) 1169 struct sigaction act = { .sa_handler = handler };
1170 sigemptyset (&act.sa_mask);
1171 sigaction (sig, &act, NULL);
1172# else
1173 void (*ohandler) (int) = signal (sig, handler);
1174 if (flag)
1125 { 1175 {
1126 /* We are in the background; cancel the continue. */ 1176 if (ohandler == SIG_IGN)
1127 kill (-pgrp, SIGTTIN); 1177 {
1178 signal (sig, SIG_IGN);
1179 /* While HANDLER was mistakenly installed a signal may have
1180 arrived and set *FLAG, so clear *FLAG now. */
1181 *flag = 0;
1182 }
1128 } 1183 }
1129 1184# endif
1130 signal (signalnum, handle_sigcont);
1131 errno = old_errno;
1132} 1185}
1133 1186
1134/* Signal handler for SIGTSTP; notify the Emacs process that we are 1187/* Initial installation of signal handlers. */
1135 going to sleep. Normally the suspend is initiated by Emacs via
1136 server-handle-suspend-tty, but if the server gets out of sync with
1137 reality, we may get a SIGTSTP on C-z. Handling this signal and
1138 notifying Emacs about it should get things under control again. */
1139 1188
1140static void 1189static void
1141handle_sigtstp (int signalnum) 1190init_signals (void)
1142{ 1191{
1143 int old_errno = errno; 1192 install_handler (SIGCONT, handle_sigcont, &got_sigcont);
1144 sigset_t set; 1193 install_handler (SIGTSTP, handle_sigtstp, &got_sigtstp);
1145 1194 install_handler (SIGTTOU, handle_sigttou, &got_sigttou);
1146 if (emacs_socket) 1195 install_handler (SIGWINCH, handle_sigwinch, &got_sigwinch);
1147 send_to_emacs (emacs_socket, "-suspend \n"); 1196 /* Don't mess with SIGINT and SIGQUIT, as Emacs has no way to
1148 1197 determine which terminal the signal came from. C-g is a normal
1149 /* Unblock this signal and call the default handler by temporarily 1198 input event on secondary terminals. */
1150 changing the handler and resignaling. */
1151 sigprocmask (SIG_BLOCK, NULL, &set);
1152 sigdelset (&set, signalnum);
1153 signal (signalnum, SIG_DFL);
1154 raise (signalnum);
1155 sigprocmask (SIG_SETMASK, &set, NULL); /* Let's the above signal through. */
1156 signal (signalnum, handle_sigtstp);
1157
1158 errno = old_errno;
1159} 1199}
1160 1200
1201/* Act on delivered tty-related signal SIG that normally has handler
1202 HANDLER. EMACS_SOCKET connects to Emacs. */
1161 1203
1162/* Set up signal handlers before opening a frame on the current tty. */ 1204static void
1205act_on_tty_signal (int sig, void (*handler) (int), HSOCKET emacs_socket)
1206{
1207 /* Notify Emacs that we are going to sleep. Normally the suspend is
1208 initiated by Emacs via server-handle-suspend-tty, but if the
1209 server gets out of sync with reality, we may get a SIGTSTP on
1210 C-z. Handling this signal and notifying Emacs about it should
1211 get things under control again. */
1212 send_to_emacs (emacs_socket, "-suspend \n");
1213
1214 /* Execute the default action by temporarily changing handling to
1215 the default and resignaling. */
1216 install_handler (sig, SIG_DFL, NULL);
1217 raise (sig);
1218 install_handler (sig, handler, NULL);
1219}
1220
1221/* Act on delivered signals if possible. If EMACS_SOCKET is valid,
1222 use it to communicate to Emacs. */
1163 1223
1164static void 1224static void
1165init_signals (void) 1225act_on_signals (HSOCKET emacs_socket)
1166{ 1226{
1167 /* Don't pass SIGINT and SIGQUIT to Emacs, because it has no way of 1227 while (true)
1168 deciding which terminal the signal came from. C-g is now a 1228 {
1169 normal input event on secondary terminals. */ 1229 bool took_action = false;
1170 signal (SIGWINCH, pass_signal_to_emacs); 1230
1171 signal (SIGCONT, handle_sigcont); 1231 if (emacs_socket != INVALID_SOCKET)
1172 signal (SIGTSTP, handle_sigtstp); 1232 {
1173 signal (SIGTTOU, handle_sigtstp); 1233 if (got_sigcont)
1234 {
1235 got_sigcont = 0;
1236 took_action = true;
1237 pid_t tcpgrp = tcgetpgrp (STDOUT_FILENO);
1238 if (0 <= tcpgrp)
1239 {
1240 pid_t pgrp = getpgrp ();
1241 if (tcpgrp == pgrp)
1242 {
1243 /* We are in the foreground. */
1244 send_to_emacs (emacs_socket, "-resume \n");
1245 }
1246 else if (tty)
1247 {
1248 /* We are in the background; cancel the continue. */
1249 kill (-pgrp, SIGTTIN);
1250 }
1251 }
1252 }
1253
1254 if (got_sigtstp)
1255 {
1256 got_sigtstp = 0;
1257 took_action = true;
1258 act_on_tty_signal (SIGTSTP, handle_sigtstp, emacs_socket);
1259 }
1260 if (got_sigttou)
1261 {
1262 got_sigttou = 0;
1263 took_action = true;
1264 act_on_tty_signal (SIGTTOU, handle_sigttou, emacs_socket);
1265 }
1266 }
1267
1268 if (emacs_pid && got_sigwinch)
1269 {
1270 got_sigwinch = 0;
1271 took_action = true;
1272 kill (emacs_pid, SIGWINCH);
1273 }
1274
1275 if (!took_action)
1276 break;
1277 }
1174} 1278}
1175 1279
1176/* Create a local socket and connect it to Emacs. */ 1280/* Create a local socket and connect it to Emacs. */
@@ -1464,7 +1568,7 @@ w32_give_focus (void)
1464 1568
1465/* Start the emacs daemon and try to connect to it. */ 1569/* Start the emacs daemon and try to connect to it. */
1466 1570
1467static void 1571static HSOCKET
1468start_daemon_and_retry_set_socket (void) 1572start_daemon_and_retry_set_socket (void)
1469{ 1573{
1470# ifndef WINDOWSNT 1574# ifndef WINDOWSNT
@@ -1581,13 +1685,23 @@ start_daemon_and_retry_set_socket (void)
1581 "Emacs daemon should have started, trying to connect again\n"); 1685 "Emacs daemon should have started, trying to connect again\n");
1582# endif /* WINDOWSNT */ 1686# endif /* WINDOWSNT */
1583 1687
1584 emacs_socket = set_socket (true); 1688 HSOCKET emacs_socket = set_socket (true);
1585 if (emacs_socket == INVALID_SOCKET) 1689 if (emacs_socket == INVALID_SOCKET)
1586 { 1690 {
1587 message (true, 1691 message (true,
1588 "Error: Cannot connect even after starting the Emacs daemon\n"); 1692 "Error: Cannot connect even after starting the Emacs daemon\n");
1589 exit (EXIT_FAILURE); 1693 exit (EXIT_FAILURE);
1590 } 1694 }
1695 return emacs_socket;
1696}
1697
1698/* Flush standard output and its underlying file descriptor. */
1699static void
1700flush_stdout (HSOCKET emacs_socket)
1701{
1702 fflush (stdout);
1703 while (fdatasync (STDOUT_FILENO) != 0 && errno == EINTR)
1704 act_on_signals (emacs_socket);
1591} 1705}
1592#endif /* HAVE_SOCKETS && HAVE_INET_SOCKETS */ 1706#endif /* HAVE_SOCKETS && HAVE_INET_SOCKETS */
1593 1707
@@ -1641,13 +1755,14 @@ main (int argc, char **argv)
1641 in case of failure to connect. */ 1755 in case of failure to connect. */
1642 bool start_daemon_if_needed = alternate_editor && !alternate_editor[0]; 1756 bool start_daemon_if_needed = alternate_editor && !alternate_editor[0];
1643 1757
1644 emacs_socket = set_socket (alternate_editor || start_daemon_if_needed); 1758 HSOCKET emacs_socket = set_socket (alternate_editor
1759 || start_daemon_if_needed);
1645 if (emacs_socket == INVALID_SOCKET) 1760 if (emacs_socket == INVALID_SOCKET)
1646 { 1761 {
1647 if (! start_daemon_if_needed) 1762 if (! start_daemon_if_needed)
1648 fail (); 1763 fail ();
1649 1764
1650 start_daemon_and_retry_set_socket (); 1765 emacs_socket = start_daemon_and_retry_set_socket ();
1651 } 1766 }
1652 1767
1653 char *cwd = get_current_dir_name (); 1768 char *cwd = get_current_dir_name ();
@@ -1719,6 +1834,8 @@ main (int argc, char **argv)
1719 if (find_tty (&tty_type, &tty_name, !tty)) 1834 if (find_tty (&tty_type, &tty_name, !tty))
1720 { 1835 {
1721# ifndef NO_SOCKETS_IN_FILE_SYSTEM 1836# ifndef NO_SOCKETS_IN_FILE_SYSTEM
1837 /* Install signal handlers before opening a frame on the
1838 current tty. */
1722 init_signals (); 1839 init_signals ();
1723# endif 1840# endif
1724 send_to_emacs (emacs_socket, "-tty "); 1841 send_to_emacs (emacs_socket, "-tty ");
@@ -1809,20 +1926,16 @@ main (int argc, char **argv)
1809 printf ("Waiting for Emacs..."); 1926 printf ("Waiting for Emacs...");
1810 skiplf = false; 1927 skiplf = false;
1811 } 1928 }
1812 fflush (stdout); 1929 flush_stdout (emacs_socket);
1813 while (fdatasync (STDOUT_FILENO) != 0 && errno == EINTR)
1814 continue;
1815 1930
1816 /* Now, wait for an answer and print any messages. */ 1931 /* Now, wait for an answer and print any messages. */
1817 while (exit_status == EXIT_SUCCESS) 1932 while (exit_status == EXIT_SUCCESS)
1818 { 1933 {
1819 do 1934 do
1820 { 1935 {
1821 errno = 0; 1936 act_on_signals (emacs_socket);
1822 rl = recv (emacs_socket, string, BUFSIZ, 0); 1937 rl = recv (emacs_socket, string, BUFSIZ, 0);
1823 } 1938 }
1824 /* If we receive a signal (e.g. SIGWINCH, which we pass
1825 through to Emacs), on some OSes we get EINTR and must retry. */
1826 while (rl < 0 && errno == EINTR); 1939 while (rl < 0 && errno == EINTR);
1827 1940
1828 if (rl <= 0) 1941 if (rl <= 0)
@@ -1916,9 +2029,7 @@ main (int argc, char **argv)
1916 2029
1917 if (!skiplf) 2030 if (!skiplf)
1918 printf ("\n"); 2031 printf ("\n");
1919 fflush (stdout); 2032 flush_stdout (emacs_socket);
1920 while (fdatasync (STDOUT_FILENO) != 0 && errno == EINTR)
1921 continue;
1922 2033
1923 if (rl < 0) 2034 if (rl < 0)
1924 exit_status = EXIT_FAILURE; 2035 exit_status = EXIT_FAILURE;