summaryrefslogtreecommitdiffstats
path: root/call/call.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2015-06-10 23:45:05 +0200
committerRalf Baechle <ralf@linux-mips.org>2015-06-10 23:45:05 +0200
commit1214e075cc0a93fe2c11997202d653fd2422b4e5 (patch)
tree2d5bc4988041b8cfb815af189c3e9dddf6314c30 /call/call.c
parent854c084ec3a966f0655df7e3b25a427ba1791d40 (diff)
call: Fix race condition between select(2) and signal delivery.
If a signal is delivered between the start of the main select loop and select might wait for a long time. Fixing this with select is not possible so switch to pselect. Unlike select pselect akes a struct timespec pointer as its argument so rewrite the related infrastructure to work with struct timespec instead of struct timeval. For now the race did only affect the SIGQUIT delivery. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'call/call.c')
-rw-r--r--call/call.c19
1 files changed, 13 insertions, 6 deletions
diff --git a/call/call.c b/call/call.c
index b0cf956..89d3264 100644
--- a/call/call.c
+++ b/call/call.c
@@ -85,7 +85,7 @@ int paclen;
int fd;
int wait_for_remote_disconnect = FALSE;
-static struct timeval inactivity_timeout;
+static struct timespec inactivity_timeout;
int inactivity_timeout_is_set = FALSE;
int remote_commands_enabled = TRUE;
@@ -1593,6 +1593,7 @@ int cmd_call(char *call[], int mode)
char s[80];
int flags = 0;
int EOF_on_STDIN = FALSE;
+ sigset_t oursigs, oldsigs;
init_crc();
@@ -1609,6 +1610,8 @@ int cmd_call(char *call[], int mode)
signal(SIGQUIT, cmd_intr);
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
+ sigemptyset(&oursigs);
+ sigaddset(&oursigs, SIGQUIT);
fcntl(fd, F_SETFL, O_NONBLOCK);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
@@ -1629,15 +1632,17 @@ int cmd_call(char *call[], int mode)
}
}
+ sigprocmask(SIG_BLOCK, &oursigs, &oldsigs);
+
while (TRUE) {
- struct timeval tv, *timeout;
+ struct timespec tv, *timeout;
int nfds;
if (inactivity_timeout_is_set == TRUE && uploadfile == -1 && downloadfile == -1) {
tv.tv_sec = inactivity_timeout.tv_sec;
- tv.tv_usec = inactivity_timeout.tv_usec;
+ tv.tv_nsec = inactivity_timeout.tv_nsec;
} else {
tv.tv_sec = 0;
- tv.tv_usec = 10;
+ tv.tv_nsec = 10000; /* 10ms */
}
FD_ZERO(&sock_read);
if (EOF_on_STDIN == FALSE)
@@ -1653,7 +1658,8 @@ int cmd_call(char *call[], int mode)
timeout = NULL;
else
timeout = &tv;
- nfds = select(fd + 1, &sock_read, &sock_write, NULL, timeout);
+ nfds = pselect(fd + 1, &sock_read, &sock_write, NULL, timeout,
+ &oldsigs);
if (nfds == -1) {
if (!interrupted) {
if (errno == EINTR)
@@ -2277,6 +2283,7 @@ int cmd_call(char *call[], int mode)
}
fcntl(STDIN_FILENO, F_SETFL, 0);
+ sigprocmask(SIG_SETMASK, &oldsigs, NULL);
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, SIG_DFL);
@@ -2357,7 +2364,7 @@ int main(int argc, char **argv)
double f = atof(optarg);
inactivity_timeout.tv_sec = f;
- inactivity_timeout.tv_usec = (f - (long) f) * 1000000;
+ inactivity_timeout.tv_nsec = (f - (long) f) * 1000000000;
if (f < 0.001 || f > (double) LONG_MAX) {
fprintf(stderr, "call: option '-T' must be > 0.001 (1ms) and < %ld seconds\n", LONG_MAX);