blob: a5e5f7f29400beefd7e42ab9780a15cf58c127c0 [file] [log] [blame]
/* libnih
*
* io.c - file and socket input/output handling
*
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
* Copyright © 2009 Canonical Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/string.h>
#include <nih/list.h>
#include <nih/signal.h>
#include <nih/logging.h>
#include <nih/error.h>
#include <nih/errors.h>
#include "io.h"
/* Prototypes for static functions */
static void nih_io_watcher (NihIo *io, NihIoWatch *watch,
NihIoEvents events);
static inline ssize_t nih_io_watcher_read (NihIo *io, NihIoWatch *watch)
__attribute__ ((warn_unused_result));
static inline ssize_t nih_io_watcher_write (NihIo *io, NihIoWatch *watch)
__attribute__ ((warn_unused_result));
static void nih_io_closed (NihIo *io);
static void nih_io_error (NihIo *io);
static void nih_io_shutdown_check (NihIo *io);
static NihIoMessage * nih_io_first_message (NihIo *io);
/**
* nih_io_watches;
*
* This is the list of current watches on file descriptors and sockets,
* not sorted into any particular order. Each item is an NihIoWatch
* structure.
**/
NihList *nih_io_watches = NULL;
/**
* nih_io_init:
*
* Initialise the list of I/O watches.
**/
void
nih_io_init (void)
{
if (! nih_io_watches)
nih_io_watches = NIH_MUST (nih_list_new (NULL));
}
/**
* nih_io_add_watch:
* @parent: parent object for new watch,
* @fd: file descriptor or socket to watch,
* @events: events to watch for,
* @watcher: function to call when @events occur on @fd,
* @data: pointer to pass to @watcher.
*
* Adds @fd to the list of file descriptors and sockets to watch, when any
* of @events occur @watcher will be called. @events is a bit mask
* of the different events we care about.
*
* This is the simplest form of watch and satisfies most basic purposes.
*
* The watch structure is allocated using nih_alloc() and stored in a linked
* list; there is no non-allocated version because of this.
*
* Removal of the watch can be performed by freeing it.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned watch. When all parents
* of the returned watch are freed, the returned watch will also be
* freed.
*
* Returns: the watch structure, or NULL if insufficient memory.
**/
NihIoWatch *
nih_io_add_watch (const void *parent,
int fd,
NihIoEvents events,
NihIoWatcher watcher,
void *data)
{
NihIoWatch *watch;
nih_assert (fd >= 0);
nih_assert (watcher != NULL);
nih_io_init ();
watch = nih_new (parent, NihIoWatch);
if (! watch)
return NULL;
nih_list_init (&watch->entry);
nih_alloc_set_destructor (watch, nih_list_destroy);
watch->fd = fd;
watch->events = events;
watch->watcher = watcher;
watch->data = data;
nih_list_add (nih_io_watches, &watch->entry);
return watch;
}
/**
* nih_io_select_fds:
* @nfds: pointer to store highest number in,
* @readfds: pointer to set of descriptors to check for read,
* @writefds: pointer to set of descriptors to check for write,
* @exceptfds: pointer to set of descriptors to check for exceptions.
*
* Fills the given fd_set arrays based on the list of I/O watches.
**/
void
nih_io_select_fds (int *nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds)
{
nih_assert (nfds != NULL);
nih_assert (readfds != NULL);
nih_assert (writefds != NULL);
nih_assert (exceptfds != NULL);
nih_io_init ();
NIH_LIST_FOREACH (nih_io_watches, iter) {
NihIoWatch *watch = (NihIoWatch *)iter;
if (watch->events & NIH_IO_READ) {
FD_SET (watch->fd, readfds);
*nfds = nih_max (*nfds, watch->fd + 1);
}
if (watch->events & NIH_IO_WRITE) {
FD_SET (watch->fd, writefds);
*nfds = nih_max (*nfds, watch->fd + 1);
}
if (watch->events & NIH_IO_EXCEPT) {
FD_SET (watch->fd, exceptfds);
*nfds = nih_max (*nfds, watch->fd + 1);
}
}
}
/**
* nih_io_handle_fds:
* @readfds: pointer to set of descriptors ready for read,
* @writefds: pointer to set of descriptors ready for write,
* @exceptfds: pointer to set of descriptors with exceptions.
*
* Receives arrays of fd_set structures which have been cleared of any
* descriptors which haven't changed and iterates the watch list calling
* the appropriate functions.
*
* It is safe for watches to remove the watch during their call.
**/
void
nih_io_handle_fds (fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds)
{
nih_assert (readfds != NULL);
nih_assert (writefds != NULL);
nih_assert (exceptfds != NULL);
nih_io_init ();
NIH_LIST_FOREACH_SAFE (nih_io_watches, iter) {
NihIoWatch *watch = (NihIoWatch *)iter;
NihIoEvents events;
events = NIH_IO_NONE;
if ((watch->events & NIH_IO_READ)
&& FD_ISSET (watch->fd, readfds))
events |= NIH_IO_READ;
if ((watch->events & NIH_IO_WRITE)
&& FD_ISSET (watch->fd, writefds))
events |= NIH_IO_WRITE;
if ((watch->events & NIH_IO_EXCEPT)
&& FD_ISSET (watch->fd, exceptfds))
events |= NIH_IO_EXCEPT;
if (events)
watch->watcher (watch->data, watch, events);
}
}
/**
* nih_io_buffer_new:
* @parent: parent object for new buffer.
*
* Allocates a new NihIoBuffer structure containing an empty buffer.
*
* The buffer is allocated using nih_alloc() and all functions that use the
* buffer ensure that the internal data is an nih_alloc() child of the buffer
* itself, so this can be freed using nih_free(); there is no non-allocated
* version because of this,
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned buffer. When all parents
* of the returned buffer are freed, the returned buffer will also be
* freed.
*
* Returns: new buffer, or NULL if insufficient memory.
**/
NihIoBuffer *
nih_io_buffer_new (const void *parent)
{
NihIoBuffer *buffer;
buffer = nih_new (parent, NihIoBuffer);
if (! buffer)
return NULL;
buffer->buf = NULL;
buffer->size = 0;
buffer->len = 0;
return buffer;
}
/**
* nih_io_buffer_resize:
* @buffer: buffer to be resized,
* @grow: number of bytes to grow by.
*
* This function resizes the given @buffer so there is enough space for
* both the current data and @grow additional bytes (which may be zero).
* If there is more room than there needs to be, the buffer may actually
* be decreased in size.
*
* Returns: zero on success, negative value if insufficient memory.
**/
int
nih_io_buffer_resize (NihIoBuffer *buffer,
size_t grow)
{
char *new_buf;
size_t new_len, new_size;
nih_assert (buffer != NULL);
new_len = buffer->len + grow;
if (! new_len) {
/* No bytes to store, so clean up the buffer */
if (buffer->buf)
nih_unref (buffer->buf, buffer);
buffer->buf = NULL;
buffer->size = 0;
return 0;
}
/* Round buffer to next largest multiple of BUFSIZ */
new_size = ((new_len - 1) / BUFSIZ) * BUFSIZ + BUFSIZ;
if (new_size == buffer->size)
return 0;
/* Adjust buffer memory */
new_buf = nih_realloc (buffer->buf, buffer, new_size);
if (! new_buf)
return -1;
/* Clear the area between the old and new size; this is because we
* tend to pass these buffers to syscalls, and passing around
* unintialised data upsets people.
*/
if (new_size > buffer->size)
memset (new_buf + buffer->size, '\0', new_size - buffer->size);
/* Note: don't adjust the length */
buffer->buf = new_buf;
buffer->size = new_size;
return 0;
}
/**
* nih_io_buffer_pop:
* @parent: parent object for new object,
* @buffer: buffer to shrink,
* @len: bytes to take.
*
* Takes @len bytes from the start of @buffer, reducing the size if
* necessary, and returns them in a new string allocated with nih_alloc().
* @len is updated to contain the actual number of bytes returned.
*
* The returned string is always NULL terminated, even if there was
* not a NULL in the buffer.
*
* If there are not @len bytes in the buffer, the maximum amount there is
* will be returned, if there is nothing you'll get a zero-length string.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned object. When all parents
* of the returned watch are freed, the returned object will also be
* object.
*
* Returns: newly allocated data pointer, or NULL if insufficient memory.
**/
char *
nih_io_buffer_pop (const void *parent,
NihIoBuffer *buffer,
size_t *len)
{
char *str;
nih_assert (buffer != NULL);
nih_assert (len != NULL);
*len = nih_min (*len, buffer->len);
str = nih_alloc (parent, *len + 1);
if (! str)
return NULL;
/* Copy the data into the new string and add NULL */
memcpy (str, buffer->buf, *len);
str[*len] = '\0';
/* Move the buffer up */
nih_io_buffer_shrink (buffer, *len);
return str;
}
/**
* nih_io_buffer_shrink:
* @buffer: buffer to shrink,
* @len: bytes to remove from the front.
*
* Removes @len bytes from the beginning of @buffer and moves the rest
* of the data up to begin there.
**/
void
nih_io_buffer_shrink (NihIoBuffer *buffer,
size_t len)
{
nih_assert (buffer != NULL);
len = nih_min (len, buffer->len);
memmove (buffer->buf, buffer->buf + len, buffer->len - len);
buffer->len -= len;
/* Don't worry if this fails, it just means the buffer is larger
* than it needs to be.
*/
nih_io_buffer_resize (buffer, 0);
}
/**
* nih_io_buffer_push:
* @buffer: buffer to extend,
* @str: data to push,
* @len: length of @str.
*
* Pushes @len bytes from @str onto the end of @buffer, increasing the size
* if necessary.
*
* Returns: zero on success, negative value if insufficient memory.
**/
int
nih_io_buffer_push (NihIoBuffer *buffer,
const char *str,
size_t len)
{
nih_assert (buffer != NULL);
nih_assert (str != NULL);
if (nih_io_buffer_resize (buffer, len) < 0)
return -1;
/* Copy the data into the buffer */
memcpy (buffer->buf + buffer->len, str, len);
buffer->len += len;
return 0;
}
/**
* nih_io_message_new:
* @parent: parent object for new message.
*
* Allocates a new NihIoMessage structure with empty buffers.
*
* All functions that use the message structure ensure that the internal
* data is an nih_alloc() child of the message or its buffers, so the entire
* message freed using nih_free(); there is no non-allocated version because
* of this.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned message. When all parents
* of the returned message are freed, the returned message will also be
* freed.
*
* @parent should not be the NihIo object this is to be sent with, that
* will take a reference when you send the message.
*
* Returns: new message, or NULL if insufficient memory.
**/
NihIoMessage *
nih_io_message_new (const void *parent)
{
NihIoMessage *message;
message = nih_new (parent, NihIoMessage);
if (! message)
return NULL;
nih_list_init (&message->entry);
nih_alloc_set_destructor (message, nih_list_destroy);
message->addr = NULL;
message->addrlen = 0;
message->data = nih_io_buffer_new (message);
if (! message->data)
goto error;
message->control = nih_new (message, struct cmsghdr *);
if (! message->control)
goto error;
message->control[0] = NULL;
return message;
error:
nih_free (message);
return NULL;
}
/**
* nih_io_message_add_control:
* @message: message to add control message to,
* @level: level of control message,
* @type: protocol-specific type,
* @len: length of control data,
* @data: control data.
*
* Adds a new control message with the @level and @type given to @message.
* The control data is copied from @data into the message and should be @len
* bytes long.
*
* Returns: zero on success, negative value if insufficient memory.
**/
int
nih_io_message_add_control (NihIoMessage *message,
int level,
int type,
socklen_t len,
const void *data)
{
struct cmsghdr *cmsg, **ptr;
size_t cmsglen = 0;
nih_assert (message != NULL);
nih_assert (data != NULL);
/* Allocate the control message first */
cmsg = nih_alloc (message->control, CMSG_SPACE (len));
if (! cmsg)
return -1;
/* Then increase the size of the array, if this fails then we can
* still leave things in a consistent state.
*/
for (ptr = message->control; *ptr; ptr++)
cmsglen++;
ptr = nih_realloc (message->control, message,
sizeof (struct cmsghdr *) * (cmsglen + 2));
if (! ptr) {
nih_free (cmsg);
return -1;
}
message->control = ptr;
message->control[cmsglen++] = cmsg;
message->control[cmsglen] = NULL;
cmsg->cmsg_level = level;
cmsg->cmsg_type = type;
cmsg->cmsg_len = CMSG_LEN (len);
memcpy (CMSG_DATA (cmsg), data, len);
return 0;
}
/**
* nih_io_message_recv:
* @parent: parent object for new message,
* @fd: file descriptor to read from,
* @len: number of bytes read.
*
* Allocates a new NihIoMessage structure and fills it with a message
* received on @fd with recvmsg(). The buffer is increased in size until
* all of the message can fit within it. @len is set to contain the actual
* number of bytes read.
*
* The message structure is allocated using nih_alloc() and normally
* stored in a linked list. Removal of the message can be performed by
* freeing it.
*
* All functions that use the message structure ensure that the internal
* data is an nih_alloc() child of the message or its buffers, so the entire
* message freed using nih_free().
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned message. When all parents
* of the returned message are freed, the returned message will also be
* freed.
*
* Returns: new message, or NULL on raised error.
*/
NihIoMessage *
nih_io_message_recv (const void *parent,
int fd,
size_t *len)
{
NihIoMessage *message;
nih_local NihIoBuffer *ctrl_buf = NULL;
struct msghdr msghdr;
struct iovec iov[1];
struct cmsghdr *cmsg;
ssize_t recv_len;
nih_assert (fd >= 0);
nih_assert (len != NULL);
message = nih_io_message_new (parent);
if (! message)
goto error;
/* Allocate a buffer to store received control messages in */
ctrl_buf = nih_io_buffer_new (NULL);
if (! ctrl_buf)
goto error;
/* Reserve enough space to hold the name based on the socket type */
switch (nih_io_get_family (fd)) {
case PF_UNIX:
message->addrlen = sizeof (struct sockaddr_un);
break;
case PF_INET:
message->addrlen = sizeof (struct sockaddr_in);
break;
case PF_INET6:
message->addrlen = sizeof (struct sockaddr_in6);
break;
default:
message->addrlen = 0;
}
if (message->addrlen) {
message->addr = nih_alloc (message, message->addrlen);
if (! message->addr)
goto error;
msghdr.msg_name = message->addr;
msghdr.msg_namelen = message->addrlen;
} else {
msghdr.msg_name = NULL;
msghdr.msg_namelen = 0;
}
/* Set the flags to indicate a truncated message so that we allocate
* some initial buffer space.
*/
msghdr.msg_flags = MSG_TRUNC | MSG_CTRUNC;
do {
/* Increase the size of the message buffer */
if ((msghdr.msg_flags & MSG_TRUNC)
&& (nih_io_buffer_resize (message->data,
(message->data->size
+ BUFSIZ)) < 0))
goto error;
msghdr.msg_iov = iov;
msghdr.msg_iovlen = 1;
iov[0].iov_base = message->data->buf;
iov[0].iov_len = message->data->size;
/* Increase the size of the control buffer */
if ((msghdr.msg_flags & MSG_CTRUNC)
&& (nih_io_buffer_resize (ctrl_buf, (ctrl_buf->size
+ BUFSIZ)) < 0))
goto error;
msghdr.msg_control = ctrl_buf->buf;
msghdr.msg_controllen = ctrl_buf->size;
msghdr.msg_flags = 0;
/* Peek at the message to determine whether or not it has
* been truncated.
*/
recv_len = recvmsg (fd, &msghdr, MSG_PEEK);
if (recv_len < 0)
goto error;
} while ((msghdr.msg_flags & MSG_TRUNC)
|| (msghdr.msg_flags & MSG_CTRUNC));
/* Receive properly this time */
recv_len = recvmsg (fd, &msghdr, 0);
if (recv_len < 0)
goto error;
/* Update the lengths, both to the caller and of the message structure
* buffers based on what was actually received.
*/
*len = recv_len;
message->data->len = recv_len;
message->addrlen = msghdr.msg_namelen;
/* Copy control messages out of the buffer and add them to the
* message.
*/
for (cmsg = CMSG_FIRSTHDR (&msghdr); cmsg;
cmsg = CMSG_NXTHDR (&msghdr, cmsg)) {
size_t len;
len = cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr));
NIH_ZERO (nih_io_message_add_control (message,
cmsg->cmsg_level,
cmsg->cmsg_type,
len,
CMSG_DATA (cmsg)));
}
return message;
error:
nih_error_raise_system ();
if (message)
nih_free (message);
return NULL;
}
/**
* nih_io_message_send:
* @message: message to be sent,
* @fd: file descriptor to send to.
*
* Send @message, an already allocated and filled NihIoMessage structure
* to the file descriptor or socket @fd using sendmsg().
*
* If @fd is not connected, the destination for the message can be specified
* in the addr and addrlen members of the structure. The message data itself
* should be pushed into the data member, and any control data added to the
* control member (usually using nih_io_message_add_control()).
*
* Returns: length of message sent, negative value on raised error.
**/
ssize_t
nih_io_message_send (NihIoMessage *message,
int fd)
{
nih_local NihIoBuffer *ctrl_buf = NULL;
struct msghdr msghdr;
struct iovec iov[1];
struct cmsghdr **ptr;
ssize_t len;
nih_assert (message != NULL);
nih_assert (fd >= 0);
msghdr.msg_name = message->addr;
msghdr.msg_namelen = message->addrlen;
msghdr.msg_iov = iov;
msghdr.msg_iovlen = 1;
iov[0].iov_base = message->data->buf;
iov[0].iov_len = message->data->len;
/* Allocate a buffer in which we store the control messages that we
* need to send.
*/
ctrl_buf = nih_io_buffer_new (NULL);
if (! ctrl_buf)
nih_return_system_error (-1);
for (ptr = message->control; *ptr; ptr++) {
size_t len;
len = CMSG_SPACE ((*ptr)->cmsg_len
- CMSG_ALIGN (sizeof (struct cmsghdr)));
if (nih_io_buffer_resize (ctrl_buf, len) < 0)
nih_return_system_error (-1);
memcpy (ctrl_buf->buf + ctrl_buf->len, *ptr, (*ptr)->cmsg_len);
ctrl_buf->len += len;
}
msghdr.msg_control = ctrl_buf->buf;
msghdr.msg_controllen = ctrl_buf->len;
msghdr.msg_flags = 0;
len = sendmsg (fd, &msghdr, 0);
if (len < 0)
nih_return_system_error (-1);
return len;
}
/**
* nih_io_reopen:
* @parent: parent object for new structure,
* @fd: file descriptor to manage,
* @type: handling mode,
* @reader: function to call when new data available,
* @close_handler: function to call on close,
* @error_handler: function to call on error,
* @data: data to pass to functions.
*
* This allocates a new NihIo structure using nih_alloc(), used to manage an
* already opened file descriptor. The descriptor is set to be non-blocking
* if it hasn't already been and the SIGPIPE signal is set to be ignored.
* The file descriptor will be closed when the structure is freed.
*
* If @type is NIH_IO_STREAM, the descriptor is managed in stream mode; data
* to be sent and data received are held in a single buffer that is expanded
* and shrunk as required. If @type is NIH_IO_MESSAGE, the descriptor is
* managed in message mode; individual messages are queued to be sent and
* are received into a queue as discreet messages.
*
* Data is automatically read from the file descriptor whenever it is
* available, and stored in the receive buffer or queue. If @reader is
* given, this function is called whenever new data has been received.
* In stream mode, this function is under no obligation to remove any data,
* it's perfectly valid to leave it in the buffer until more data is added.
* In message mode, the message must be processed as even if more messages
* are read, the reader will only be called with the first one in the queue.
*
* If @close_handler is given then it is called whenever the remote end of the
* file descriptor is closed, otherwise the local end is closed and the
* entire structure freed (which may be surprising to you).
*
* If @error_handler is given then it is called whenever any errors are
* raised, otherwise the @close_handler is called or the same action taken
* if that is not given either.
*
* The returned structure is allocated with nih_alloc() and children buffers
* and watches are allocated as children so will be automatically freed;
* there is no non-allocated version because of this.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned structure. When all parents
* of the returned structure are freed, the returned structure will also be
* freed.
*
* Returns: newly allocated structure, or NULL on raised error.
**/
NihIo *
nih_io_reopen (const void *parent,
int fd,
NihIoType type,
NihIoReader reader,
NihIoCloseHandler close_handler,
NihIoErrorHandler error_handler,
void *data)
{
NihIo *io;
nih_assert (fd >= 0);
io = nih_new (parent, NihIo);
if (! io)
nih_return_system_error (NULL);
io->type = type;
io->reader = reader;
io->close_handler = close_handler;
io->error_handler = error_handler;
io->data = data;
io->shutdown = FALSE;
io->free = NULL;
switch (io->type) {
case NIH_IO_STREAM:
io->send_buf = nih_io_buffer_new (io);
if (! io->send_buf)
goto error;
io->recv_buf = nih_io_buffer_new (io);
if (! io->recv_buf)
goto error;
break;
case NIH_IO_MESSAGE:
io->send_q = nih_list_new (io);
if (! io->send_q)
goto error;
io->recv_q = nih_list_new (io);
if (! io->recv_q)
goto error;
break;
default:
nih_assert_not_reached ();
}
/* Watcher function is called at first only if we have data to read */
io->watch = nih_io_add_watch (io, fd, NIH_IO_READ,
(NihIoWatcher)nih_io_watcher, io);
if (! io->watch)
goto error;
/* Irritating signal, means we terminate if the remote end
* disconnects between a read() and a write() ... far better to
* just get an errno!
*/
nih_signal_set_ignore (SIGPIPE);
/* We want to be able to repeatedly call read and write on the
* file descriptor so we always get maximum throughput, and we
* don't want to end up blocking; so set the socket so that
* doesn't happen.
*/
if (nih_io_set_nonblock (fd) < 0)
goto error;
nih_alloc_set_destructor (io, nih_io_destroy);
return io;
error:
nih_error_raise_system ();
nih_free (io);
return NULL;
}
/**
* nih_io_watcher:
* @io: NihIo structure,
* @watch: NihIoWatch for which an event occurred,
* @events: events that occurred.
*
* This is the watcher function associated with all file descriptors being
* managed by NihIo. It ensures that any data or messages available are
* read and placed into the receive buffer or queue, and the reader function
* is called if set.
*
* Any data or messaages in the send buffer or queue are written out if the
* @events includes NIH_IO_WRITE.
*
* Errors are handled when data is read, and result in the error handled
* function being called if set, otherwise the close handler is called if
* set and the socket closed.
**/
static void
nih_io_watcher (NihIo *io,
NihIoWatch *watch,
NihIoEvents events)
{
int caught_free;
nih_assert (io != NULL);
nih_assert (watch != NULL);
caught_free = FALSE;
if (! io->free)
io->free = &caught_free;
/* There's data to be read */
if (events & NIH_IO_READ) {
ssize_t len;
len = nih_io_watcher_read (io, watch);
/* We want to call the reader if we have any data in the
* buffer or messages in the receive queue, even if the
* remote end is closed or there was an error. In the
* latter case, it means we give it once last chance to
* process the messages.
*/
if (io->reader) {
nih_error_push_context();
switch (io->type) {
case NIH_IO_STREAM:
if (io->recv_buf->len)
io->reader (io->data, io,
io->recv_buf->buf,
io->recv_buf->len);
break;
case NIH_IO_MESSAGE: {
NihIoMessage *last = NULL, *message;
/* Call reader until the first message on the
* queue does not change.
*/
last = NULL;
while (((message = nih_io_first_message (io))
!= last) && message) {
io->reader (io->data, io,
message->data->buf,
message->data->len);
if (caught_free)
break;
last = message;
}
break;
}
default:
nih_assert_not_reached();
}
nih_error_pop_context();
}
/* Deal with errors */
if (len < 0) {
NihError *err;
err = nih_error_get ();
switch (err->number) {
case EAGAIN:
case EINTR:
case ENOMEM:
nih_free (err);
break;
default:
if (caught_free)
return;
nih_io_error (io);
if (caught_free)
return;
goto finish;
}
}
/* We might have been freed */
if (caught_free)
return;
/* Deal with socket being closed */
if ((io->type == NIH_IO_STREAM) && (! len)) {
nih_io_closed (io);
if (caught_free)
return;
goto finish;
}
}
/* There's room to write data, send as much as we can */
if (events & NIH_IO_WRITE) {
ssize_t len;
len = nih_io_watcher_write (io, watch);
/* Deal with errors */
if (len < 0) {
NihError *err;
err = nih_error_get ();
switch (err->number) {
case EAGAIN:
case EINTR:
case ENOMEM:
nih_free (err);
break;
default:
nih_io_error (io);
if (caught_free)
return;
goto finish;
}
}
}
finish:
if (io->free == &caught_free)
io->free = NULL;
/* Shut down the socket if it is empty */
nih_io_shutdown_check (io);
}
/**
* nih_io_watcher_read:
* @io: NihIo structure,
* @watch: NihIoWatch for which an event occurred.
*
* Read data from the socket directly into the buffer or receive queue to
* save hauling temporary blocks around. This function will call read()
* or recvmsg() as many times as possible to keep the kernel-side buffers
* small.
*
* It returns once a call errors or returns zero to indicate that the
* remote end closed.
*
* Returns: size of last read, zero if remote end closed and negative
* value on raised error.
**/
static inline ssize_t
nih_io_watcher_read (NihIo *io,
NihIoWatch *watch)
{
ssize_t len = 0;
nih_assert (io != NULL);
nih_assert (watch != NULL);
for (;;) {
NihIoMessage *message;
switch (io->type) {
case NIH_IO_STREAM:
/* Make sure there's room for at least 80 bytes
* (random minimum read).
*/
if (nih_io_buffer_resize (io->recv_buf, 80) < 0)
nih_return_system_error (-1);
len = read (watch->fd,
io->recv_buf->buf + io->recv_buf->len,
io->recv_buf->size - io->recv_buf->len);
if (len < 0) {
nih_return_system_error (-1);
} else if (len > 0) {
io->recv_buf->len += len;
} else {
return 0;
}
break;
case NIH_IO_MESSAGE:
/* Use BUFSIZ as the maximum message size. */
len = BUFSIZ;
message = nih_io_message_recv (io, watch->fd,
(size_t *)&len);
if (! message) {
return -1;
} else {
nih_list_add (io->recv_q, &message->entry);
}
break;
default:
nih_assert_not_reached ();
}
}
return len;
}
/**
* nih_io_watcher_write:
* @io: NihIo structure,
* @watch: NihIoWatch for which an event occurred.
*
* Write data directly from the buffer or receive queue into the socket to
* save hauling temporary blocks around. This function will call write()
* or sendmsg() as many times as possible to keep the buffer or queue
* small.
*
* It returns once a call errors or returns zero to indicate that the
* remote end closed.
*
* Returns: size of last write, zero if remote end closed and negative
* value on raised error.
**/
static inline ssize_t
nih_io_watcher_write (NihIo *io,
NihIoWatch *watch)
{
ssize_t len = 0;
nih_assert (io != NULL);
nih_assert (watch != NULL);
switch (io->type) {
case NIH_IO_STREAM:
while (io->send_buf->len) {
len = write (watch->fd, io->send_buf->buf,
io->send_buf->len);
if (len < 0)
nih_return_system_error (-1);
nih_io_buffer_shrink (io->send_buf, len);
}
/* Don't check for writability if we have nothing to write */
if (! io->send_buf->len)
watch->events &= ~NIH_IO_WRITE;
break;
case NIH_IO_MESSAGE:
while (! NIH_LIST_EMPTY (io->send_q)) {
NihIoMessage *message;
message = (NihIoMessage *)io->send_q->next;
len = nih_io_message_send (message, watch->fd);
if (len < 0)
return -1;
nih_unref (message, io);
}
/* Don't check for writability if we have nothing to write */
if (NIH_LIST_EMPTY (io->send_q))
watch->events &= ~NIH_IO_WRITE;
break;
default:
nih_assert_not_reached ();
}
return len;
}
/**
* nih_io_error:
* @io: structure error occurred for.
*
* This function is called to deal with errors that have occurred on a
* file descriptor being managed by NihIo.
*
* Normally this just calls the error handler, or if not available, it
* behaves as if the remote end was closed.
**/
static void
nih_io_error (NihIo *io)
{
nih_assert (io != NULL);
if (io->error_handler) {
io->error_handler (io->data, io);
} else {
NihError *err;
err = nih_error_get ();
nih_error ("%s: %s", _("Error while reading from descriptor"),
err->message);
nih_free (err);
nih_io_closed (io);
}
}
/**
* nih_io_closed:
* @io: structure to be closed.
*
* This function is called when the local end of a file descriptor being
* managed by NihIo should be closed. Usually this is because the remote
* end has been closed (without error) but it can also be because no
* error handler was given
*
* Normally this just calls the close handler, or if not available, it
* closes the file descriptor and frees the structure (which may be
* surprising if you were hanging on to a pointer of it).
*
* Note that this can result in the error handler getting called if
* there's an error caught by closing the socket.
**/
static void
nih_io_closed (NihIo *io)
{
nih_assert (io != NULL);
if (io->close_handler) {
io->close_handler (io->data, io);
} else {
nih_free (io);
}
}
/**
* nih_io_shutdown:
* @io: structure to be closed.
*
* Marks the NihIo structure to be closed once the buffers or queue have
* been emptied, rather than immediately. Closure is performed by calling
* the close handler if given or nih_io_close().
*
* This is most useful to send a burst of data and discard the structure
* once the data has been sent, without worrying about keeping track of
* the structure in the mean time.
**/
void
nih_io_shutdown (NihIo *io)
{
nih_assert (io != NULL);
io->shutdown = TRUE;
nih_io_shutdown_check (io);
}
/**
* nih_io_shutdown_check:
* @io: structure to be closed.
*
* Checks whether the NihIo structure is set to be shutdown, and has now
* reached the point to be closed. Call whenever you remove data from a
* buffer.
**/
static void
nih_io_shutdown_check (NihIo *io)
{
nih_assert (io != NULL);
if (! io->shutdown)
return;
switch (io->type) {
case NIH_IO_STREAM:
if ((! io->send_buf->len) && (! io->recv_buf->len))
nih_io_closed (io);
break;
case NIH_IO_MESSAGE:
if (NIH_LIST_EMPTY (io->send_q) && NIH_LIST_EMPTY (io->recv_q))
nih_io_closed (io);
break;
default:
nih_assert_not_reached ();
}
}
/**
* nih_io_destroy:
* @io: structure to be destroyed.
*
* Closes the file descriptor associated with an NihIo structure so that
* the structure can be freed. IF an error is caught by closing the
* descriptor, the error handler is called instead of the error being raised;
* this allows you to group your error handling in one place rather than
* special-case close.
*
* Normally used or called from an nih_alloc() destructor.
*
* Returns: zero.
**/
int
nih_io_destroy (NihIo *io)
{
nih_assert (io != NULL);
if (io->free)
*(io->free) = TRUE;
if ((close (io->watch->fd) < 0) && io->error_handler) {
nih_error_raise_system ();
io->error_handler (io->data, io);
}
return 0;
}
/**
* nih_io_first_message:
* @io: structure to read from.
*
* Used to obtain the oldest message in the receive queue of @io, the
* returned message is not removed from the queue, so this can be used
* to "peek" at the message or manipulate it.
*
* The message can be removed from the queue with nih_list_remove() or
* nih_free().
*
* This may only be used when @io is in message mode.
*
* Returns: message in queue, or NULL if the queue is empty.
**/
static NihIoMessage *
nih_io_first_message (NihIo *io)
{
nih_assert (io != NULL);
nih_assert (io->type == NIH_IO_MESSAGE);
if (NIH_LIST_EMPTY (io->recv_q))
return NULL;
return (NihIoMessage *)io->recv_q->next;
}
/**
* nih_io_read_message:
* @parent: parent object for new message,
* @io: structure to read from.
*
* Obtains the oldest message in the receive queue of @io, removes it
* from the queue and returns it reparented.
*
* This may only be used when @io is in message mode.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned message. When all parents
* of the returned message are freed, the returned message will also be
* freed.
*
* Returns: message from queue, or NULL if the queue is empty.
**/
NihIoMessage *
nih_io_read_message (const void *parent,
NihIo *io)
{
NihIoMessage *message;
nih_assert (io != NULL);
nih_assert (io->type == NIH_IO_MESSAGE);
message = nih_io_first_message (io);
if (message) {
nih_list_remove (&message->entry);
nih_unref_only (message, io);
if (parent)
nih_ref (message, parent);
}
nih_io_shutdown_check (io);
return message;
}
/**
* nih_io_send_message:
* @io: structure to write to,
* @message: message to write.
*
* Appends message to the send queue of @io so that it will be sent in
* turn when possible.
*
* @message itself is added to the queue, and freed once written.
*
* When called on a message already in the send queue, this moves it to
* the end of the queue. It's entirely permitted to call this on messages
* taken from the receive queue (usually of another NihIo).
*
* This may only be used when @io is in message mode.
**/
void
nih_io_send_message (NihIo *io,
NihIoMessage *message)
{
nih_assert (io != NULL);
nih_assert (io->type == NIH_IO_MESSAGE);
nih_assert (message != NULL);
nih_list_add (io->send_q, &message->entry);
nih_ref (message, io);
io->watch->events |= NIH_IO_WRITE;
}
/**
* nih_io_read:
* @parent: parent object for new string,
* @io: structure to read from,
* @len: number of bytes to read.
*
* Reads @len bytes from the receive buffer of @io or the oldest message
* in the receive queue and returns the data in a new string allocated with
* nih_alloc() that is always NULL terminated even if there was not a NULL
* in the buffer.
*
* @len is updated to contain the actual number of bytes returned.
*
* If there are not @len bytes in the buffer or message, the maximum amount
* there is will be returned, if there is nothing you'll get a zero-length
* string.
*
* If the message has no more data in the buffer, it is removed from the
* receive queue, and the next call to this function will operate on the
* next oldest message in the queue.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned string. When all parents
* of the returned string are freed, the returned string will also be
* freed.
*
* Returns: newly allocated string, or NULL if insufficient memory.
**/
char *
nih_io_read (const void *parent,
NihIo *io,
size_t *len)
{
NihIoMessage *message;
NihIoBuffer *buf;
char *str;
nih_assert (io != NULL);
nih_assert (len != NULL);
switch (io->type) {
case NIH_IO_STREAM:
message = NULL;
buf = io->recv_buf;
break;
case NIH_IO_MESSAGE:
/* Need to return a zero-length string if there's no message
* in the queue.
*/
message = nih_io_first_message (io);
if (! message) {
*len = 0;
str = nih_strdup (parent, "");
goto finish;
}
buf = message->data;
break;
default:
nih_assert_not_reached ();
}
str = nih_io_buffer_pop (parent, buf, len);
if (message && (! message->data->len))
nih_unref (message, io);
finish:
nih_io_shutdown_check (io);
return str;
}
/**
* nih_io_write:
* @io: structure to write to,
* @str: data to write,
* @len: length of @str.
*
* Writes @len bytes from @str into the send buffer of @io, or into a new
* message placed in the send queue. The data will not be sent immediately
* but whenever possible.
*
* Care should be taken to ensure @len does not include the NULL
* terminator unless you really want that sent.
*
* Returns: zero on success, negative value if insufficient memory.
**/
int
nih_io_write (NihIo *io,
const char *str,
size_t len)
{
NihIoMessage *message;
NihIoBuffer *buf;
nih_assert (io != NULL);
nih_assert (str != NULL);
switch (io->type) {
case NIH_IO_STREAM:
message = NULL;
buf = io->send_buf;
break;
case NIH_IO_MESSAGE:
message = nih_io_message_new (NULL);
if (! message)
return -1;
buf = message->data;
break;
default:
nih_assert_not_reached ();
}
if (nih_io_buffer_push (buf, str, len) < 0) {
if (message)
nih_free (message);
return -1;
}
if (message) {
nih_io_send_message (io, message);
} else if (buf->len) {
io->watch->events |= NIH_IO_WRITE;
}
return 0;
}
/**
* nih_io_get:
* @parent: parent object for new string,
* @io: structure to read from,
* @delim: character to read until.
*
* Reads from the receive buffer of @io or the oldest message in the
* receive queue until a character in @delim or the NULL terminator is
* found, and returns a new string allocated with nih_alloc() that contains
* a copy of the string up to, but not including, the delimiter.
*
* @delim may be the empty string if only the NULL terminator is considered
* a delimiter.
*
* The string and the delimiter are removed from the buffer or message.
*
* If the message has no more data in the buffer, it is removed from the
* receive queue, and the next call to this function will operate on the
* next oldest message in the queue.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned string. When all parents
* of the returned string are freed, the returned string will also be
* freed.
*
* Returns: newly allocated string or NULL if delimiter not found or
* insufficient memory.
**/
char *
nih_io_get (const void *parent,
NihIo *io,
const char *delim)
{
NihIoMessage *message;
NihIoBuffer *buf;
char *str;
size_t i;
nih_assert (io != NULL);
nih_assert (delim != NULL);
str = NULL;
switch (io->type) {
case NIH_IO_STREAM:
message = NULL;
buf = io->recv_buf;
break;
case NIH_IO_MESSAGE:
message = nih_io_first_message (io);
if (! message)
goto finish;
buf = message->data;
break;
default:
nih_assert_not_reached ();
}
/* Find the end of the string */
for (i = 0; i < buf->len; i++) {
if (strchr (delim, buf->buf[i]) || (buf->buf[i] == '\0')) {
/* Remove the string, and then the delimiter */
str = nih_io_buffer_pop (parent, buf, &i);
if (! str)
return NULL;
nih_io_buffer_shrink (buf, 1);
break;
}
}
if (message && (! message->data->len))
nih_unref (message, io);
finish:
nih_io_shutdown_check (io);
return str;
}
/**
* nih_io_printf:
* @io: structure to write to,
* @format: printf format string.
*
* Writes data formatted according to the printf-style @format string to
* the send buffer of @io, or into a new message placed in the send queue.
* The data will not be sent immediately but whenever possible.
*
* Returns: zero on success, negative value if insufficient memory.
**/
int
nih_io_printf (NihIo *io,
const char *format,
...)
{
nih_local char *str = NULL;
va_list args;
int ret;
nih_assert (io != NULL);
nih_assert (format != NULL);
va_start (args, format);
str = nih_vsprintf (NULL, format, args);
va_end (args);
if (! str)
return -1;
ret = nih_io_write (io, str, strlen (str));
return ret;
}
/**
* nih_io_set_nonblock:
* @fd: file descriptor to change.
*
* Change the flags of @fd so that all operations become non-blocking.
*
* Returns: zero on success, negative value on invalid file descriptor.
**/
int
nih_io_set_nonblock (int fd)
{
int flags;
nih_assert (fd >= 0);
flags = fcntl (fd, F_GETFL);
if (flags < 0)
return -1;
flags |= O_NONBLOCK;
if (fcntl (fd, F_SETFL, flags) < 0)
return -1;
return 0;
}
/**
* nih_io_set_cloexec:
* @fd: file descriptor to change.
*
* Change the flags of @fd so that the file descriptor is closed on exec().
*
* Returns: zero on success, negative value on invalid file descriptor.
**/
int
nih_io_set_cloexec (int fd)
{
int flags;
nih_assert (fd >= 0);
flags = fcntl (fd, F_GETFD);
if (flags < 0)
return -1;
flags |= FD_CLOEXEC;
if (fcntl (fd, F_SETFD, flags) < 0)
return -1;
return 0;
}
/**
* nih_io_get_family:
* @fd: file descriptor to check.
*
* Queries the socket so that the family it belongs to (PF_UNIX, PF_INET,
* PF_INET6) can be found.
*
* Returns: the family of the socket, or -1 on error.
**/
ssize_t
nih_io_get_family (int fd)
{
socklen_t socklen;
union {
sa_family_t family;
struct sockaddr_un un;
struct sockaddr_in in;
struct sockaddr_in6 in6;
} addr;
nih_assert (fd >= 0);
socklen = sizeof (addr);
if (getsockname (fd, (struct sockaddr *)&addr, &socklen) < 0)
return -1;
return addr.family;
}