blob: 1668e0303b8aaede808c18e0a9e78f85af43b302 [file] [log] [blame]
/* libnih
*
* io.c - file and socket input/output handling
*
* Copyright © 2006 Scott James Remnant <scott@netsplit.com>.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.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 "io.h"
/* Prototypes for static functions */
static inline void nih_io_buffer_shrink (NihIoBuffer *buffer, size_t len);
static void nih_io_watcher (NihIo *io, NihIoWatch *watch,
NihIoEvents events);
static void nih_io_closed (NihIo *io);
static void nih_io_error (NihIo *io);
/**
* io_watches;
*
* This is the list of current watches on file descriptors and sockets,
* not sorted into any particular order.
**/
static NihList *io_watches = NULL;
/**
* nih_io_init:
*
* Initialise the list of I/O watches.
**/
static inline void
nih_io_init (void)
{
if (! io_watches)
NIH_MUST (io_watches = nih_list_new ());
}
/**
* nih_io_add_watch:
* @parent: parent of 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 purposes.
*
* The watch structure is allocated using #nih_alloc and stored in a linked
* list, a default destructor is set that removes the watch from the list.
* Removal of the watch can be performed by freeing it.
*
* Returns: the watch structure, or %NULL if insufficient memory.
**/
NihIoWatch *
nih_io_add_watch (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, (NihDestructor)nih_list_destructor);
watch->fd = fd;
watch->events = events;
watch->watcher = watcher;
watch->data = data;
nih_list_add (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 (io_watches, iter) {
NihIoWatch *watch = (NihIoWatch *)iter;
if (watch->events & NIH_IO_READ) {
FD_SET (watch->fd, readfds);
*nfds = MAX (*nfds, watch->fd + 1);
}
if (watch->events & NIH_IO_WRITE) {
FD_SET (watch->fd, writefds);
*nfds = MAX (*nfds, watch->fd + 1);
}
if (watch->events & NIH_IO_EXCEPT) {
FD_SET (watch->fd, exceptfds);
*nfds = 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 (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 of new buffer.
*
* Allocates a new #NihIoBuffer structure containing an empty buffer,
* all functions that use the buffer ensure that the internal data is
* an #nih_alloc child of the buffer, so this may be freed using #nih_free.
*
* Returns: new buffer, or %NULL if insufficient memory.
**/
NihIoBuffer *
nih_io_buffer_new (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, %NULL 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);
nih_assert (grow >= 0);
new_len = buffer->len + grow;
if (! new_len) {
/* No bytes to store, so clean up the buffer */
if (buffer->buf)
nih_free (buffer->buf);
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;
/* Note: don't adjust the length */
buffer->buf = new_buf;
buffer->size = new_size;
return 0;
}
/**
* nih_io_buffer_pop:
* @parent: parent of new pointer,
* @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 newly allocated string. It is
* illegal to request more bytes than are available in the buffer.
*
* The returned string is always %NULL terminated, even if there was
* not a %NULL in the buffer.
*
* Returns: newly allocated data pointer, or %NULL if insufficient memory.
**/
char *
nih_io_buffer_pop (void *parent,
NihIoBuffer *buffer,
size_t len)
{
char *str;
nih_assert (buffer != NULL);
nih_assert (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);
/* Don't worry if this fails, it just means the buffer is larger
* than it needs to be.
*/
nih_io_buffer_resize (buffer, 0);
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.
**/
static inline void
nih_io_buffer_shrink (NihIoBuffer *buffer,
size_t len)
{
nih_assert (buffer != NULL);
nih_assert (len <= buffer->len);
memmove (buffer->buf, buffer->buf + len, buffer->len - len);
buffer->len -= len;
}
/**
* 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, %NULL 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_reopen:
* @parent: parent pointer of new structure,
* @fd: file descriptor to manage,
* @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 and returns an #NihIo structure for managing an already
* opened file descriptor. The file descriptor is set to be non-blocking
* if it hasn't already been and the SIGPIPE signal is set to be ignored.
*
* If @reader is given then all data is automatically read from the
* file descriptor and stored in a buffer and this function is called
* whenever there is new data available. The function is under no
* obligation to remove any data, it's perfectly valid to leave it in the
* buffer until next time.
*
* 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 any 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.
*
* Returns: newly allocated structure, or %NULL if insufficient memory.
**/
NihIo *
nih_io_reopen (void *parent,
int fd,
NihIoReader reader,
NihIoCloseHandler close_handler,
NihIoErrorHandler error_handler,
void *data)
{
NihIo *io;
nih_assert (fd >= 0);
io = nih_new (parent, NihIo);
if (! io)
return NULL;
io->reader = reader;
io->close_handler = close_handler;
io->error_handler = error_handler;
io->data = data;
io->shutdown = FALSE;
io->watch = nih_io_add_watch (io, fd, NIH_IO_READ,
(NihIoWatcher) nih_io_watcher, io);
if (! io->watch)
goto error;
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;
/* 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) {
NihError *err;
err = nih_error_get ();
nih_error ("%s: %s",
_("Unable to set descriptor non-blocking"),
err->message);
nih_free (err);
}
return io;
error:
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 data is read from the file
* descriptor into the recv buffer and the reader called, any data in
* the send buffer is written to the socket and any errors are handled
**/
static void
nih_io_watcher (NihIo *io,
NihIoWatch *watch,
NihIoEvents events)
{
nih_assert (io != NULL);
nih_assert (watch != NULL);
/* There's data to be read */
if (events & NIH_IO_READ) {
ssize_t len;
/* Read directly into the buffer to save hauling temporary
* blocks around; always make sure there's room for at
* least 80 bytes (random minimum read). Make sure we call
* read as many times as necessary to exhaust the socket
* so we can get maximum throughput.
*/
do {
if (nih_io_buffer_resize (io->recv_buf, 80) < 0)
return;
len = read (watch->fd,
io->recv_buf->buf + io->recv_buf->len,
io->recv_buf->size - io->recv_buf->len);
if (len < 0) {
nih_error_raise_system ();
} else if (len > 0) {
io->recv_buf->len += len;
}
} while (len > 0);
/* Call the reader if we have any data in the buffer.
* This could be called simply because we're about to error,
* but it means we give it one last chance to process.
*/
nih_error_push_context();
if (io->recv_buf->len) {
if (io->reader) {
io->reader (io->data, io, io->recv_buf->buf,
io->recv_buf->len);
} else {
/* No reader, just discard */
nih_io_buffer_shrink (io->recv_buf,
io->recv_buf->len);
}
}
nih_error_pop_context();
/* Deal with errors */
if (len < 0) {
NihError *err;
err = nih_error_get ();
if ((err->number != EAGAIN)
&& (err->number != EINTR)) {
nih_error_raise_again (err);
nih_io_error (io);
return;
} else {
nih_free (err);
}
}
/* Deal with socket being closed */
if (! len) {
nih_io_closed (io);
return;
}
}
/* There's room to write data, send as much as we can */
if (events & NIH_IO_WRITE) {
ssize_t len;
/* Write directly from the buffer to save hauling temporary
* blocks around, and call write() as many times as necessary
* to exhaust the buffer so we can get maximum throughput.
*/
while (io->send_buf->len) {
len = write (watch->fd, io->send_buf->buf,
io->send_buf->len);
/* Don't bother checking errors, we catch them
* using read
*/
if (len <= 0)
break;
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;
/* Resize the buffer to avoid memory wastage */
nih_io_buffer_resize (io->send_buf, 0);
}
/* Shut down the socket if it has empty buffers */
if (io->shutdown && (! io->send_buf->len) && (! io->recv_buf->len))
nih_io_closed (io);
}
/**
* 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_io_close (io);
}
}
/**
* nih_io_shutdown:
* @io: structure to be closed.
*
* Marks the #NihIo structure to be closed once the buffers 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_close:
* @io: structure to be closed.
*
* Closes the file descriptor associated with an #NihIo structure and
* frees the structure. If an error is caught by closing the descriptor,
* it is 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.
**/
void
nih_io_close (NihIo *io)
{
nih_assert (io != NULL);
if ((close (io->watch->fd) < 0) && io->error_handler) {
nih_error_raise_system ();
io->error_handler (io->data, io);
}
nih_free (io);
}
/**
* nih_io_read:
* @parent: parent of new string,
* @io: structure to read from,
* @len: number of bytes to read.
*
* Reads @len bytes from the receive buffer of @io and returns the data
* in a newly allocated string which is always %NULL terminated even
* if there was not a %NULL in the buffer.
*
* It is illegal to request more bytes than exist in the bufferr.
*
* Returns: newly allocated string, or %NULL if insufficient memory.
**/
char *
nih_io_read (void *parent,
NihIo *io,
size_t len)
{
nih_assert (io != NULL);
return nih_io_buffer_pop (parent, io->recv_buf, len);
}
/**
* 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, 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)
{
int ret;
nih_assert (io != NULL);
nih_assert (str != NULL);
ret = nih_io_buffer_push (io->send_buf, str, len);
/* If we have data to write, ensure we watch for writability */
if (io->send_buf->len)
io->watch->events |= NIH_IO_WRITE;
return ret;
}
/**
* nih_io_get:
* @parent: parent of new string,
* @io: structure to read from,
* @delim: character to read until.
*
* Reads from the receive buffer of @io until a character in @delim or
* the %NULL terminator is found, and returns the string up to, but not
* including, the delimiter as a newly allocated string.
*
* @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.
*
* Returns: newly allocated string or %NULL if delimiter not found or
* insufficient memory.
**/
char *
nih_io_get (void *parent,
NihIo *io,
const char *delim)
{
size_t i;
nih_assert (io != NULL);
nih_assert (delim != NULL);
for (i = 0; i < io->recv_buf->len; i++) {
/* Found end of string */
if (strchr (delim, io->recv_buf->buf[i])
|| (io->recv_buf->buf[i] == '\0')) {
char *str;
/* Remove the string, and then the delimiter */
str = nih_io_buffer_pop (parent, io->recv_buf, i);
nih_io_buffer_shrink (io->recv_buf, 1);
return str;
}
}
return NULL;
}
/**
* 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, the data will not be sent immediately but
* whenever possible.
*
* Returns: number of bytes written, negative value if insufficient memory.
**/
ssize_t
nih_io_printf (NihIo *io,
const char *format,
...)
{
char *str;
va_list args;
ssize_t len;
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;
len = nih_io_write (io, str, strlen (str));
nih_free (str);
return len;
}
/**
* 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 raised error.
**/
int
nih_io_set_nonblock (int fd)
{
int flags;
nih_assert (fd >= 0);
flags = fcntl (fd, F_GETFL);
if (flags < 0)
nih_return_system_error (-1);
flags |= O_NONBLOCK;
if (fcntl (fd, F_SETFL, flags) < 0)
nih_return_system_error (-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 raised error.
**/
int
nih_io_set_cloexec (int fd)
{
int flags;
nih_assert (fd >= 0);
flags = fcntl (fd, F_GETFD);
if (flags < 0)
nih_return_system_error (-1);
flags |= FD_CLOEXEC;
if (fcntl (fd, F_SETFD, flags) < 0)
nih_return_system_error (-1);
return 0;
}