summaryrefslogtreecommitdiff
path: root/src/posix-io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/posix-io.c')
-rw-r--r--src/posix-io.c127
1 files changed, 124 insertions, 3 deletions
diff --git a/src/posix-io.c b/src/posix-io.c
index e712ef2..5c6cf1d 100644
--- a/src/posix-io.c
+++ b/src/posix-io.c
@@ -570,7 +570,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
if (fd_list[i].fd > fd)
fd = fd_list[i].fd;
fd++;
-#if defined(__sun) || defined(__FreeBSD__)
+#if defined(__sun) || defined(__FreeBSD__) || defined(__GLIBC__)
closefrom (fd);
max_fds = fd;
#else /*!__sun */
@@ -691,8 +691,119 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
nothing to select, > 0 = number of signaled fds. */
-int
-_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+#ifdef HAVE_POLL_H
+static int
+_gpgme_io_select_poll (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+ struct pollfd *poll_fds = NULL;
+ nfds_t poll_nfds;
+ /* Use a 1s timeout. */
+ int timeout = 1000;
+ unsigned int i;
+ int any;
+ int count;
+ void *dbg_help = NULL;
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", NULL,
+ "nfds=%zu, nonblock=%u", nfds, nonblock);
+
+
+ if (nonblock)
+ timeout = 0;
+
+ poll_fds = malloc (sizeof (*poll_fds)*nfds);
+ if (!poll_fds)
+ return -1;
+
+ poll_nfds = 0;
+
+ TRACE_SEQ (dbg_help, "poll on [ ");
+
+ any = 0;
+ for (i = 0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ continue;
+ if (fds[i].for_read || fds[i].for_write)
+ {
+ poll_fds[poll_nfds].fd = fds[i].fd;
+ poll_fds[poll_nfds].events = 0;
+ poll_fds[poll_nfds].revents = 0;
+ if (fds[i].for_read)
+ {
+ poll_fds[poll_nfds].events |= POLLIN;
+ TRACE_ADD1 (dbg_help, "r=%d ", fds[i].fd);
+ }
+ if (fds[i].for_write)
+ {
+ poll_fds[poll_nfds].events |= POLLOUT;
+ TRACE_ADD1 (dbg_help, "w=%d ", fds[i].fd);
+ }
+ poll_nfds++;
+ any = 1;
+ }
+ fds[i].signaled = 0;
+ }
+ TRACE_END (dbg_help, "]");
+ if (!any)
+ {
+ free (poll_fds);
+ return TRACE_SYSRES (0);
+ }
+
+ do
+ count = poll (poll_fds, poll_nfds, timeout);
+ while (count < 0 && (errno == EINTR || errno == EAGAIN));
+ if (count < 0)
+ {
+ int save_errno = errno;
+ free (poll_fds);
+ errno = save_errno;
+ return TRACE_SYSRES (-1);
+ }
+
+ TRACE_SEQ (dbg_help, "poll OK [ ");
+ if (TRACE_ENABLED (dbg_help))
+ {
+ poll_nfds = 0;
+ for (i = 0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ continue;
+ if ((poll_fds[poll_nfds].revents & (POLLIN|POLLHUP)))
+ TRACE_ADD1 (dbg_help, "r=%d ", i);
+ if ((poll_fds[poll_nfds].revents & POLLOUT))
+ TRACE_ADD1 (dbg_help, "w=%d ", i);
+ poll_nfds++;
+ }
+ TRACE_END (dbg_help, "]");
+ }
+
+ poll_nfds = 0;
+ for (i = 0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ continue;
+ if (fds[i].for_read || fds[i].for_write)
+ {
+ short events_to_be_checked = 0;
+
+ if (fds[i].for_read)
+ events_to_be_checked |= (POLLIN|POLLHUP);
+ if (fds[i].for_write)
+ events_to_be_checked |= POLLOUT;
+ if ((poll_fds[poll_nfds].revents & events_to_be_checked))
+ fds[i].signaled = 1;
+
+ poll_nfds++;
+ }
+ }
+
+ free (poll_fds);
+ return TRACE_SYSRES (count);
+}
+#else
+static int
+_gpgme_io_select_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
{
fd_set readfds;
fd_set writefds;
@@ -802,7 +913,17 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
}
return TRACE_SYSRES (count);
}
+#endif
+int
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+#ifdef HAVE_POLL_H
+ return _gpgme_io_select_poll (fds, nfds, nonblock);
+#else
+ return _gpgme_io_select_select (fds, nfds, nonblock);
+#endif
+}
int
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)