summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2015-09-25 10:45:22 +0200
committerWerner Koch <wk@gnupg.org>2015-09-25 11:32:07 +0200
commit071c2170479869f4c6694ae85d2b113e84482a01 (patch)
tree00f0ef082a9c2f26b6ee0e26b37c0828d2886f26 /src
parent61d832c53b7db5367a5542347e3454c882d0bf28 (diff)
downloadlibgpg-error-071c2170479869f4c6694ae85d2b113e84482a01.tar.gz
estream: Add gpgrt_set_nonblock and gpgrt_poll.
* configure.ac (AC_CHECK_HEADERS): Add sys/select.h and sys/time.h. * src/estream.c: Include both header if available. (COOKIE_IOCTL_NONBLOCK): New. (struct estream_cookie_fd): Add field nonblock. (func_fd_create): Set nonblock from MODEFLAGS. (es_func_fd_ioctl): New. (parse_mode): Add modeflag "nonblock". (es_fill): Map EWOULDBLOCK to EAGAIN. Do not set error indicator for EAGAIN. (es_flush, es_seek, es_write_nbf): Map EWOULDBLOCK to EAGAIN. (do_fdopen): Call COOKIE_IOCTL_NONBLOCK. (_gpgrt_set_nonblock): New. (_gpgrt_get_nonblock): New. (_gpgrt_poll): New. * src/gpg-error.h.in (struct _gpgrt_poll_s): New. (gpgrt_poll_t, es_poll_t): New. (es_set_nonblock, es_get_nonblock, es_poll): New. * src/gpg-error.vers, src/gpg-error.def.in: Add gpgrt_set_nonblock, gpgrt_get_nonblock, and gpgrt_poll. * src/visibility.c (gpgrt_set_nonblock, gpgrt_get_nonblock): New. (gpgrt_poll): New. * tests/t-common.h (DIM): New. * tests/t-poll.c: New. * tests/Makefile.am (TESTS): Add t-poll. (t_poll_LDADD): New. -- The poll interface uses select(2) internally because that is more portable than poll(2). Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'src')
-rw-r--r--src/estream.c349
-rw-r--r--src/gpg-error.def.in4
-rw-r--r--src/gpg-error.h.in33
-rw-r--r--src/gpg-error.vers3
-rw-r--r--src/gpgrt-int.h4
-rw-r--r--src/visibility.c18
-rw-r--r--src/visibility.h6
7 files changed, 413 insertions, 4 deletions
diff --git a/src/estream.c b/src/estream.c
index 7d12e36..58f069c 100644
--- a/src/estream.c
+++ b/src/estream.c
@@ -1,6 +1,6 @@
/* estream.c - Extended Stream I/O Library
* Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011,
- * 2014 g10 Code GmbH
+ * 2014, 2015 g10 Code GmbH
*
* This file is part of Libestream.
*
@@ -67,6 +67,12 @@
# endif
#endif
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
@@ -108,6 +114,11 @@
# define S_IWOTH S_IWUSR
# define S_IXGRP S_IXUSR
# define S_IXOTH S_IXUSR
+# define O_NONBLOCK 0 /* FIXME: Not yet supported. */
+#endif
+
+#ifndef EAGAIN
+# define EAGAIN EWOULDBLOCK
#endif
@@ -157,6 +168,7 @@ typedef int (*cookie_ioctl_function_t) (void *cookie, int cmd,
void *ptr, size_t *len);
/* IOCTL commands for the private cookie function. */
#define COOKIE_IOCTL_SNATCH_BUFFER 1
+#define COOKIE_IOCTL_NONBLOCK 2
/* The internal stream object. */
@@ -869,6 +881,7 @@ typedef struct estream_cookie_fd
{
int fd; /* The file descriptor we are using for actual output. */
int no_close; /* If set we won't close the file descriptor. */
+ int nonblock; /* Non-blocking mode is enabled. */
} *estream_cookie_fd_t;
/* Create function for objects indentified by a libc file descriptor. */
@@ -887,11 +900,10 @@ func_fd_create (void **cookie, int fd, unsigned int modeflags, int no_close)
/* Make sure it is in binary mode if requested. */
if ( (modeflags & O_BINARY) )
setmode (fd, O_BINARY);
-#else
- (void)modeflags;
#endif
fd_cookie->fd = fd;
fd_cookie->no_close = no_close;
+ fd_cookie->nonblock = !!(modeflags & O_NONBLOCK);
*cookie = fd_cookie;
err = 0;
}
@@ -990,6 +1002,47 @@ es_func_fd_seek (void *cookie, gpgrt_off_t *offset, int whence)
return err;
}
+/* An IOCTL function for fd objects. */
+static int
+es_func_fd_ioctl (void *cookie, int cmd, void *ptr, size_t *len)
+{
+ estream_cookie_fd_t fd_cookie = cookie;
+ int ret;
+
+ if (cmd == COOKIE_IOCTL_NONBLOCK && !len)
+ {
+ fd_cookie->nonblock = !!ptr;
+ if (IS_INVALID_FD (fd_cookie->fd))
+ {
+ _set_errno (EINVAL);
+ ret = -1;
+ }
+ else
+ {
+#ifdef _WIN32
+ _set_errno (EOPNOTSUPP); /* FIXME: Implement for Windows. */
+ ret = -1;
+#else
+ _set_errno (0);
+ ret = fcntl (fd_cookie->fd, F_GETFL, 0);
+ if (ret == -1 && errno)
+ ;
+ else if (fd_cookie->nonblock)
+ ret = fcntl (fd_cookie->fd, F_SETFL, (ret | O_NONBLOCK));
+ else
+ ret = fcntl (fd_cookie->fd, F_SETFL, (ret & ~O_NONBLOCK));
+#endif
+ }
+ }
+ else
+ {
+ _set_errno (EINVAL);
+ ret = -1;
+ }
+
+ return ret;
+}
+
/* Destroy function for fd objects. */
static int
es_func_fd_destroy (void *cookie)
@@ -1496,6 +1549,10 @@ func_file_create (void **cookie, int *filedes,
disables any internal locking. This keyword is also found on
IBM systems.
+ nonblock
+
+ The object is opened in non-blocking mode. This is the same as
+ calling gpgrt_set_nonblock on the file.
Note: R_CMODE is optional because is only required by functions
which are able to creat a file. */
@@ -1591,6 +1648,16 @@ parse_mode (const char *modestr,
}
*samethread = 1;
}
+ else if (!strncmp (modestr, "nonblock", 8))
+ {
+ modestr += 8;
+ if (*modestr && !strchr (" \t,", *modestr))
+ {
+ _set_errno (EINVAL);
+ return -1;
+ }
+ oflags |= O_NONBLOCK;
+ }
}
if (!got_cmode)
cmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
@@ -1631,6 +1698,10 @@ es_fill (estream_t stream)
{
bytes_read = 0;
err = -1;
+#if EWOULDBLOCK != EAGAIN
+ if (errno == EWOULDBLOCK)
+ _set_errno (EAGAIN);
+#endif
}
else
{
@@ -1640,7 +1711,10 @@ es_fill (estream_t stream)
}
if (err)
- stream->intern->indicators.err = 1;
+ {
+ if (errno != EAGAIN)
+ stream->intern->indicators.err = 1;
+ }
else if (!bytes_read)
stream->intern->indicators.eof = 1;
@@ -1690,6 +1764,10 @@ es_flush (estream_t stream)
{
bytes_written = 0;
err = -1;
+#if EWOULDBLOCK != EAGAIN
+ if (errno == EWOULDBLOCK)
+ _set_errno (EAGAIN);
+#endif
}
else
bytes_written = ret;
@@ -1941,6 +2019,10 @@ es_read_nbf (estream_t _GPGRT__RESTRICT stream,
if (ret == -1)
{
err = -1;
+#if EWOULDBLOCK != EAGAIN
+ if (errno == EWOULDBLOCK)
+ _set_errno (EAGAIN);
+#endif
break;
}
else if (ret)
@@ -2209,6 +2291,10 @@ es_seek (estream_t _GPGRT__RESTRICT stream, gpgrt_off_t offset, int whence,
if (ret == -1)
{
err = -1;
+#if EWOULDBLOCK != EAGAIN
+ if (errno == EWOULDBLOCK)
+ _set_errno (EAGAIN);
+#endif
goto out;
}
@@ -2259,6 +2345,10 @@ es_write_nbf (estream_t _GPGRT__RESTRICT stream,
if (ret == -1)
{
err = -1;
+#if EWOULDBLOCK != EAGAIN
+ if (errno == EWOULDBLOCK)
+ _set_errno (EAGAIN);
+#endif
break;
}
else
@@ -2941,6 +3031,13 @@ do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list)
err = es_create (&stream, cookie, &syshd, estream_functions_fd,
modeflags, samethread, with_locked_list);
+ if (!err && stream)
+ {
+ stream->intern->func_ioctl = es_func_fd_ioctl;
+ if ((modeflags & O_NONBLOCK))
+ err = es_func_fd_ioctl (cookie, COOKIE_IOCTL_NONBLOCK, "", NULL);
+ }
+
out:
if (err && create_called)
(*estream_functions_fd.func_close) (cookie);
@@ -4293,6 +4390,249 @@ _gpgrt_set_binary (estream_t stream)
}
+/* Set non-blocking mode for STREAM. Use true for ONOFF to enable and
+ false to disable non-blocking mode. Returns 0 on success or -1 on
+ error and sets ERRNO. Note that not all backends support
+ non-blocking mode.
+
+ In non-blocking mode a system call will not block but return an
+ error and set errno to EAGAIN. The estream API always uses EAGAIN
+ and not EWOULDBLOCK. If a buffered function like es_fgetc() or
+ es_fgets() returns an error and both, feof() and ferror() return
+ false the caller may assume that the error condition was EAGAIN.
+
+ Switching back from non-blocking to blocking may raise problems
+ with buffering, thus care should be taken. Although read+write
+ sockets are supported in theory, switching from write to read may
+ result into problems because estream may first flush the write
+ buffers and there is no way to handle that non-blocking (EAGAIN)
+ case. Explicit flushing should thus be done before before
+ switching to read. */
+int
+_gpgrt_set_nonblock (estream_t stream, int onoff)
+{
+ cookie_ioctl_function_t func_ioctl;
+ int ret;
+
+ lock_stream (stream);
+ func_ioctl = stream->intern->func_ioctl;
+ if (!func_ioctl)
+ {
+ _set_errno (EOPNOTSUPP);
+ ret = -1;
+ }
+ else
+ {
+ unsigned int save_flags = stream->intern->modeflags;
+
+ if (onoff)
+ stream->intern->modeflags |= O_NONBLOCK;
+ else
+ stream->intern->modeflags &= ~O_NONBLOCK;
+
+ ret = func_ioctl (stream->intern->cookie, COOKIE_IOCTL_NONBLOCK,
+ onoff?"":NULL, NULL);
+ if (ret)
+ stream->intern->modeflags = save_flags;
+ }
+ unlock_stream (stream);
+ return ret;
+}
+
+
+/* Return true if STREAM is in non-blocking mode. */
+int
+_gpgrt_get_nonblock (estream_t stream)
+{
+ int ret;
+
+ lock_stream (stream);
+ ret = !!(stream->intern->modeflags & O_NONBLOCK);
+ unlock_stream (stream);
+ return ret;
+}
+
+
+/* A version of poll(2) working on estream handles. Note that not all
+ estream types work with this function. In contrast to the standard
+ poll function the gpgrt_poll_t object uses a set of names bit flags
+ instead of the EVENTS and REVENTS members. An item with the IGNORE
+ flag set is entirely ignored. The TIMEOUT values is given in
+ milliseconds, a value of -1 waits indefinitely, and a value of 0
+ returns immediately.
+
+ A positive return value gives the number of fds with new
+ information. A return value of 0 indicates a timeout and -1
+ indicates an error in which case ERRNO is set. */
+int
+_gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout)
+{
+ gpgrt_poll_t *item;
+ int count = 0;
+ fd_set readfds, writefds, exceptfds;
+ int any_readfd, any_writefd, any_exceptfd;
+ int idx;
+ int max_fd;
+ int fd, ret, any;
+
+ if (!fds)
+ {
+ _set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Clear all response fields (even for ignored items). */
+ for (item = fds, idx = 0; idx < nfds; item++, idx++)
+ {
+ item->got_read = 0;
+ item->got_write = 0;
+ item->got_oob = 0;
+ item->got_rdhup = 0;
+ item->got_err = 0;
+ item->got_hup = 0;
+ item->got_nval = 0;
+ }
+
+ /* Check for pending reads. */
+ for (item = fds, idx = 0; idx < nfds; item++, idx++)
+ {
+ if (item->ignore)
+ continue;
+ if (!item->want_read)
+ continue;
+ if (_gpgrt__pending (item->stream))
+ {
+ item->got_read = 1;
+ count++;
+ }
+ }
+
+ /* Check for space in the write buffers. */
+ for (item = fds, idx = 0; idx < nfds; item++, idx++)
+ {
+ if (item->ignore)
+ continue;
+ if (!item->want_write)
+ continue;
+ /* FIXME */
+ }
+
+ if (count)
+ return count; /* Early return without waiting. */
+
+ /* Now do the real select. */
+ any_readfd = any_writefd = any_exceptfd = 0;
+ max_fd = 0;
+ for (item = fds, idx = 0; idx < nfds; item++, idx++)
+ {
+ if (item->ignore)
+ continue;
+ fd = _gpgrt_fileno (item->stream);
+ if (fd == -1)
+ continue; /* Stream does not support polling. */
+
+ if (item->want_read)
+ {
+ if (!any_readfd)
+ {
+ FD_ZERO (&readfds);
+ any_readfd = 1;
+ }
+ FD_SET (fd, &readfds);
+ if (fd > max_fd)
+ max_fd = fd;
+ }
+ if (item->want_write)
+ {
+ if (!any_writefd)
+ {
+ FD_ZERO (&writefds);
+ any_writefd = 1;
+ }
+ FD_SET (fd, &writefds);
+ if (fd > max_fd)
+ max_fd = fd;
+ }
+ if (item->want_oob)
+ {
+ if (!any_exceptfd)
+ {
+ FD_ZERO (&exceptfds);
+ any_exceptfd = 1;
+ }
+ FD_SET (fd, &exceptfds);
+ if (fd > max_fd)
+ max_fd = fd;
+ }
+ }
+
+#ifdef _WIN32
+ (void)timeout;
+ ret = -1;
+ _set_errno (EOPNOTSUPP);
+#else
+ if (pre_syscall_func)
+ pre_syscall_func ();
+ do
+ {
+ struct timeval timeout_val;
+
+ timeout_val.tv_sec = timeout / 1000;
+ timeout_val.tv_usec = (timeout % 1000) * 1000;
+ ret = select (max_fd+1,
+ any_readfd? &readfds : NULL,
+ any_writefd? &writefds : NULL,
+ any_exceptfd? &exceptfds : NULL,
+ timeout == -1 ? NULL : &timeout_val);
+ }
+ while (ret == -1 && errno == EINTR);
+ if (post_syscall_func)
+ post_syscall_func ();
+#endif
+
+ if (ret == -1)
+ return -1;
+ if (!ret)
+ return 0; /* Timeout. Note that in this case we can't return
+ got_err for an invalid stream. */
+
+ for (item = fds, idx = 0; idx < nfds; item++, idx++)
+ {
+ if (item->ignore)
+ continue;
+ fd = _gpgrt_fileno (item->stream);
+ if (fd == -1)
+ {
+ item->got_err = 1; /* Stream does not support polling. */
+ count++;
+ continue;
+ }
+
+ any = 0;
+ if (item->want_read && FD_ISSET (fd, &readfds))
+ {
+ item->got_read = 1;
+ any = 1;
+ }
+ if (item->want_write && FD_ISSET (fd, &writefds))
+ {
+ item->got_write = 1;
+ any = 1;
+ }
+ if (item->want_oob && FD_ISSET (fd, &exceptfds))
+ {
+ item->got_oob = 1;
+ any = 1;
+ }
+
+ if (any)
+ count++;
+ }
+
+ return count;
+}
+
+
void
_gpgrt_opaque_set (estream_t stream, void *opaque)
{
@@ -4373,6 +4713,7 @@ _gpgrt_fname_get (estream_t stream)
}
+
/* Print a BUFFER to STREAM while replacing all control characters and
the characters in DELIMITERS by standard C escape sequences.
Returns 0 on success or -1 on error. If BYTES_WRITTEN is not NULL
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index cba973d..16de809 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -140,4 +140,8 @@ EXPORTS
_gpgrt_pending @104
_gpgrt_pending_unlocked @105
+ gpgrt_set_nonblock @106
+ gpgrt_get_nonblock @107
+ gpgrt_poll @108
+
;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index ad5d470..61b57fc 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -525,6 +525,32 @@ typedef struct _gpgrt_syshd es_syshd_t;
#define ES_SYSHD_HANDLE GPGRT_SYSHD_HANDLE
#endif
+/* The object used with gpgrt_poll. */
+struct _gpgrt_poll_s
+{
+ gpgrt_stream_t stream;
+ unsigned int want_read:1;
+ unsigned int want_write:1;
+ unsigned int want_oob:1;
+ unsigned int want_rdhup:1;
+ unsigned int _reserv1:4;
+ unsigned int got_read:1;
+ unsigned int got_write:1;
+ unsigned int got_oob:1;
+ unsigned int got_rdhup:1;
+ unsigned int _reserv2:4;
+ unsigned int got_err:1;
+ unsigned int got_hup:1;
+ unsigned int got_nval:1;
+ unsigned int _reserv3:4;
+ unsigned int ignore:1;
+ unsigned int user:8; /* For application use. */
+};
+typedef struct _gpgrt_poll_s gpgrt_poll_t;
+#ifdef GPGRT_ENABLE_ES_MACROS
+typedef struct _gpgrt_poll_s es_poll_t;
+#endif
+
gpgrt_stream_t gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t gpgrt_mopen (void *_GPGRT__RESTRICT data,
@@ -682,6 +708,10 @@ void gpgrt_setbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf);
void gpgrt_set_binary (gpgrt_stream_t stream);
+int gpgrt_set_nonblock (gpgrt_stream_t stream, int onoff);
+int gpgrt_get_nonblock (gpgrt_stream_t stream);
+
+int gpgrt_poll (gpgrt_poll_t *fdlist, unsigned int nfds, int timeout);
gpgrt_stream_t gpgrt_tmpfile (void);
@@ -777,6 +807,9 @@ int gpgrt_vsnprintf (char *buf,size_t bufsize,
# define es_setvbuf gpgrt_setvbuf
# define es_setbuf gpgrt_setbuf
# define es_set_binary gpgrt_set_binary
+# define es_set_nonblock gpgrt_set_nonblock
+# define es_get_nonblock gpgrt_get_nonblock
+# define es_poll gpgrt_poll
# define es_tmpfile gpgrt_tmpfile
# define es_opaque_set gpgrt_opaque_set
# define es_opaque_get gpgrt_opaque_get
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index 758e549..067bb29 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -104,6 +104,9 @@ GPG_ERROR_1.0 {
gpgrt_setvbuf;
gpgrt_setbuf;
gpgrt_set_binary;
+ gpgrt_set_nonblock;
+ gpgrt_get_nonblock;
+ gpgrt_poll;
gpgrt_tmpfile;
gpgrt_opaque_set;
gpgrt_opaque_get;
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index 34e5d72..0f7b29b 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -189,6 +189,10 @@ int _gpgrt_setvbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int mode, size_t size);
void _gpgrt_set_binary (gpgrt_stream_t stream);
+int _gpgrt_set_nonblock (gpgrt_stream_t stream, int onoff);
+int _gpgrt_get_nonblock (gpgrt_stream_t stream);
+
+int _gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout);
gpgrt_stream_t _gpgrt_tmpfile (void);
diff --git a/src/visibility.c b/src/visibility.c
index 9213ce9..4750685 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -592,6 +592,24 @@ gpgrt_set_binary (estream_t stream)
_gpgrt_set_binary (stream);
}
+int
+gpgrt_set_nonblock (estream_t stream, int onoff)
+{
+ return _gpgrt_set_nonblock (stream, onoff);
+}
+
+int
+gpgrt_get_nonblock (estream_t stream)
+{
+ return _gpgrt_get_nonblock (stream);
+}
+
+int
+gpgrt_poll (gpgrt_poll_t *fds, unsigned int nfds, int timeout)
+{
+ return _gpgrt_poll (fds, nfds, timeout);
+}
+
estream_t
gpgrt_tmpfile (void)
{
diff --git a/src/visibility.h b/src/visibility.h
index 6f7de84..ec3a124 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -127,6 +127,9 @@ MARK_VISIBLE (gpgrt_vfprintf_unlocked)
MARK_VISIBLE (gpgrt_setvbuf)
MARK_VISIBLE (gpgrt_setbuf)
MARK_VISIBLE (gpgrt_set_binary)
+MARK_VISIBLE (gpgrt_set_nonblock)
+MARK_VISIBLE (gpgrt_get_nonblock)
+MARK_VISIBLE (gpgrt_poll)
MARK_VISIBLE (gpgrt_tmpfile)
MARK_VISIBLE (gpgrt_opaque_set)
MARK_VISIBLE (gpgrt_opaque_get)
@@ -232,6 +235,9 @@ MARK_VISIBLE (gpgrt_set_alloc_func)
#define gpgrt_setvbuf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_setbuf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_set_binary _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_set_nonblock _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_get_nonblock _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_poll _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_tmpfile _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_opaque_set _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_opaque_get _gpgrt_USE_UNDERSCORED_FUNCTION