From 3b74154185e2eea3763a6e16be6fe331c6d20098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20R=C3=BChsen?= Date: Tue, 12 May 2020 18:34:58 +0200 Subject: [PATCH] mi: Make sure fd is non-blocking before select+read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. 2020-05-12 Tim Rühsen * poke/pk-mi.c (pk_mi_fd_set_nonblocking): New function. (pk_mi_fd_restore_blocking): New function. (pk_mi_loop): Set fd to blocking and restore before returning. --- ChangeLog | 6 ++++++ poke/pk-mi.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/poke/pk-mi.c b/poke/pk-mi.c index b16a466b..62faadcb 100644 --- a/poke/pk-mi.c +++ b/poke/pk-mi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "pk-mi-msg.h" #include "pk-mi-json.h" @@ -159,6 +160,33 @@ pk_mi_send_frame_msg (const char *payload) fflush (stdout); } +/* Set fd to non-blocking and return old flags or -1 on error. */ +static int pk_mi_fd_set_nonblocking(int fd) +{ + int flags = fcntl (fd, F_GETFL, 0); + + if (flags >= 0 && (flags & O_NONBLOCK) == 0) + { + if (fcntl (fd, F_SETFL, flags | O_NONBLOCK)) + flags = -1; + } + + if (flags < 0) + perror ("fcntl"); + + return flags; +} + +/* Restore flags to fd if it was set to blocking previously. */ +static void pk_mi_fd_restore_blocking(int fd, int flags) +{ + if (flags >= 0 && (flags & O_NONBLOCK) == 0) + { + if (fcntl (fd, F_SETFL, flags) < 0) + perror ("fcntl"); + } +} + /* This global is used to finalize the MI loop. */ static int pk_mi_exit_p; @@ -166,11 +194,19 @@ static int pk_mi_loop (int fd) { fd_set active_fd_set, read_fd_set; + int old_flags; FD_ZERO (&active_fd_set); FD_SET (fd, &active_fd_set); + /* Make sure that fd is non-blocking. + * From 'man 2 select': + * On Linux, select() may report a socket file descriptor as + * "ready for reading", while nevertheless a subsequent read blocks." */ + old_flags = pk_mi_fd_set_nonblocking (fd); + pk_mi_exit_p = 0; + while (1) { read_fd_set = active_fd_set; @@ -186,14 +222,23 @@ pk_mi_loop (int fd) int ret = pk_mi_read_from_client (fd); if (pk_mi_exit_p) - /* Client requested us to close the connection. */ - return 1; + { + /* Client requested us to close the connection. */ + pk_mi_fd_restore_blocking (fd, old_flags); + return 1; + } else if (ret == -1) - /* Client closed the connection. */ - return 1; + { + /* Client closed the connection. */ + pk_mi_fd_restore_blocking (fd, old_flags); + return 1; + } else if (ret == -2) - /* Protocol error. */ - return 0; + { + /* Protocol error. */ + pk_mi_fd_restore_blocking (fd, old_flags); + return 0; + } } } } -- 2.26.2