diff options
author | Werner Koch <wk@gnupg.org> | 2015-09-25 10:45:22 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2015-09-25 11:32:07 +0200 |
commit | 071c2170479869f4c6694ae85d2b113e84482a01 (patch) | |
tree | 00f0ef082a9c2f26b6ee0e26b37c0828d2886f26 /src | |
parent | 61d832c53b7db5367a5542347e3454c882d0bf28 (diff) | |
download | libgpg-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.c | 349 | ||||
-rw-r--r-- | src/gpg-error.def.in | 4 | ||||
-rw-r--r-- | src/gpg-error.h.in | 33 | ||||
-rw-r--r-- | src/gpg-error.vers | 3 | ||||
-rw-r--r-- | src/gpgrt-int.h | 4 | ||||
-rw-r--r-- | src/visibility.c | 18 | ||||
-rw-r--r-- | src/visibility.h | 6 |
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 |