summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorRuss Allbery <eagle@eyrie.org>2014-01-16 22:21:31 -0800
committerRuss Allbery <rra@stanford.edu>2014-01-17 11:13:38 -0800
commite55891929b8d6dee3444079fcba95055ecdfe7d4 (patch)
treeade06ab3c0a7d3bbfed79b7a5ff2de8c35d24f0b /util
parentf531ee6b4956cce34c99696d16f1cab262428fb9 (diff)
Switch the process event loop to libevent
Replace the complex hand-coded client event loop, using select, with a rewritten one using libevent. This fixes race conditions in noticing the client exit properly and removes the need for the five-second polling to see whether the client has exited. It also provides a framework for eventual added complexity in the event loop. Import the buffer utility functions from rra-c-util and use that to manage the client output buffer for now. This will probably be replaced with evbuffers. This is the first cut, using basic libevent functionality. The next step will be to use bufferevents in some places and extend the use of libevent to other event loops in the server. Currently requires libevent 2.x. Portability to libevent 1.4.3 will come in a subsequent commit. Change-Id: Ia8e4cd0a857c710dbc0c348578648084466e886b Reviewed-on: https://gerrit.stanford.edu/1378 Reviewed-by: Russ Allbery <rra@stanford.edu> Tested-by: Russ Allbery <rra@stanford.edu>
Diffstat (limited to 'util')
-rw-r--r--util/buffer.c324
-rw-r--r--util/buffer.h129
2 files changed, 453 insertions, 0 deletions
diff --git a/util/buffer.c b/util/buffer.c
new file mode 100644
index 0000000..3f18511
--- /dev/null
+++ b/util/buffer.c
@@ -0,0 +1,324 @@
+/*
+ * Counted, reusable memory buffer.
+ *
+ * A buffer is an allocated bit of memory with a known size and a separate
+ * data length. It's intended to store strings and can be reused repeatedly
+ * to minimize the number of memory allocations. Buffers increase in
+ * increments of 1K, or double for some operations.
+ *
+ * A buffer contains data that's been used and the data that's been left, used
+ * when the buffer is an I/O buffer where lots of data is buffered and then
+ * slowly processed out of the buffer. The total length of the data is used +
+ * left. If a buffer is just used to store some data, used can be set to 0
+ * and left stores the length of the data.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle@eyrie.org>
+ * Copyright 2011, 2012
+ * The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <util/buffer.h>
+#include <util/xmalloc.h>
+
+
+/*
+ * Allocate a new struct buffer and initialize it.
+ */
+struct buffer *
+buffer_new(void)
+{
+ struct buffer *buffer;
+
+ buffer = xmalloc(sizeof(struct buffer));
+ buffer->size = 0;
+ buffer->used = 0;
+ buffer->left = 0;
+ buffer->data = NULL;
+ return buffer;
+}
+
+
+/*
+ * Free a buffer.
+ */
+void
+buffer_free(struct buffer *buffer)
+{
+ if (buffer->data != NULL)
+ free(buffer->data);
+ free(buffer);
+}
+
+
+/*
+ * Resize a buffer to be at least as large as the provided second argument.
+ * Resize buffers to multiples of 1KB to keep the number of reallocations to a
+ * minimum. Refuse to resize a buffer to make it smaller.
+ */
+void
+buffer_resize(struct buffer *buffer, size_t size)
+{
+ if (size < buffer->size)
+ return;
+ buffer->size = (size + 1023) & ~1023UL;
+ buffer->data = xrealloc(buffer->data, buffer->size);
+}
+
+
+/*
+ * Compact a buffer by moving the data between buffer->used and buffer->left
+ * to the beginning of the buffer, overwriting the already-consumed data.
+ */
+void
+buffer_compact(struct buffer *buffer)
+{
+ if (buffer->used == 0)
+ return;
+ if (buffer->left != 0)
+ memmove(buffer->data, buffer->data + buffer->used, buffer->left);
+ buffer->used = 0;
+}
+
+
+/*
+ * Replace whatever data is currently in the buffer with the provided data.
+ * Resize the buffer if needed.
+ */
+void
+buffer_set(struct buffer *buffer, const char *data, size_t length)
+{
+ if (length > 0) {
+ buffer_resize(buffer, length);
+ memmove(buffer->data, data, length);
+ }
+ buffer->left = length;
+ buffer->used = 0;
+}
+
+
+/*
+ * Append data to a buffer. The new data shows up as additional unused data
+ * at the end of the buffer. Resize the buffer if needed.
+ */
+void
+buffer_append(struct buffer *buffer, const char *data, size_t length)
+{
+ size_t total;
+
+ if (length == 0)
+ return;
+ total = buffer->used + buffer->left;
+ buffer_resize(buffer, total + length);
+ buffer->left += length;
+ memcpy(buffer->data + total, data, length);
+}
+
+
+/*
+ * Print data into a buffer from the supplied va_list, appending to the end.
+ * The new data shows up as unused data at the end of the buffer. The
+ * trailing nul is not added to the buffer.
+ */
+void
+buffer_append_vsprintf(struct buffer *buffer, const char *format, va_list args)
+{
+ size_t total, avail;
+ ssize_t status;
+ va_list args_copy;
+
+ total = buffer->used + buffer->left;
+ avail = buffer->size - total;
+ va_copy(args_copy, args);
+ status = vsnprintf(buffer->data + total, avail, format, args_copy);
+ va_end(args_copy);
+ if (status < 0)
+ return;
+ if ((size_t) status + 1 <= avail) {
+ buffer->left += status;
+ } else {
+ buffer_resize(buffer, total + status + 1);
+ avail = buffer->size - total;
+ status = vsnprintf(buffer->data + total, avail, format, args);
+ if (status < 0 || (size_t) status + 1 > avail)
+ return;
+ buffer->left += status;
+ }
+}
+
+
+/*
+ * Print data into a buffer, appending to the end. The new data shows up as
+ * unused data at the end of the buffer. Resize the buffer if needed. The
+ * trailing nul is not added to the buffer.
+ */
+void
+buffer_append_sprintf(struct buffer *buffer, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ buffer_append_vsprintf(buffer, format, args);
+ va_end(args);
+}
+
+
+/*
+ * Replace the current buffer contents with data printed from the supplied
+ * va_list. The new data shows up as unused data at the end of the buffer.
+ * The trailing nul is not added to the buffer.
+ */
+void
+buffer_vsprintf(struct buffer *buffer, const char *format, va_list args)
+{
+ buffer_set(buffer, NULL, 0);
+ buffer_append_vsprintf(buffer, format, args);
+}
+
+
+/*
+ * Replace the current buffer contents with data printed from the supplied
+ * format string and arguments. The new data shows up as unused data at the
+ * end of the buffer. The trailing nul is not added to the buffer.
+ */
+void
+buffer_sprintf(struct buffer *buffer, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ buffer_vsprintf(buffer, format, args);
+ va_end(args);
+}
+
+
+/*
+ * Swap the contents of two buffers.
+ */
+void
+buffer_swap(struct buffer *one, struct buffer *two)
+{
+ struct buffer tmp;
+
+ tmp = *one;
+ *one = *two;
+ *two = tmp;
+}
+
+
+/*
+ * Find a given string in the unconsumed data in buffer. We know that all the
+ * data prior to start (an offset into the space between buffer->used and
+ * buffer->left) has already been searched. Returns the offset of the string
+ * (with the same meaning as start) in offset if found, and returns true if
+ * the terminator is found and false otherwise.
+ */
+bool
+buffer_find_string(struct buffer *buffer, const char *string, size_t start,
+ size_t *offset)
+{
+ char *terminator, *data;
+ size_t length;
+
+ length = strlen(string);
+ do {
+ data = buffer->data + buffer->used + start;
+ terminator = memchr(data, string[0], buffer->left - start);
+ if (terminator == NULL)
+ return false;
+ start = (terminator - buffer->data) - buffer->used;
+ if (buffer->left - start < length)
+ return false;
+ start++;
+ } while (memcmp(terminator, string, length) != 0);
+ *offset = start - 1;
+ return true;
+}
+
+
+/*
+ * Read from a file descriptor into a buffer, up to the available space in the
+ * buffer, and return the number of characters read.
+ */
+ssize_t
+buffer_read(struct buffer *buffer, int fd)
+{
+ ssize_t count;
+
+ do {
+ size_t used = buffer->used + buffer->left;
+ count = read(fd, buffer->data + used, buffer->size - used);
+ } while (count == -1 && (errno == EAGAIN || errno == EINTR));
+ if (count > 0)
+ buffer->left += count;
+ return count;
+}
+
+
+/*
+ * Read from a file descriptor until end of file is reached, doubling the
+ * buffer size as necessary to hold all of the data. Returns true on success,
+ * false on failure (in which case errno will be set).
+ */
+bool
+buffer_read_all(struct buffer *buffer, int fd)
+{
+ ssize_t count;
+
+ if (buffer->size == 0)
+ buffer_resize(buffer, 1024);
+ do {
+ size_t used = buffer->used + buffer->left;
+ if (buffer->size <= used)
+ buffer_resize(buffer, buffer->size * 2);
+ count = buffer_read(buffer, fd);
+ } while (count > 0);
+ return (count == 0);
+}
+
+
+/*
+ * Read the entire contents of a file into a buffer. This is a slight
+ * optimization over buffer_read_all because it can stat the file descriptor
+ * first and size the buffer appropriately. buffer_read_all will still handle
+ * the case where the file size changes while it's being read. Returns true
+ * on success, false on failure (in which case errno will be set).
+ */
+bool
+buffer_read_file(struct buffer *buffer, int fd)
+{
+ struct stat st;
+ size_t used = buffer->used + buffer->left;
+
+ if (fstat(fd, &st) < 0)
+ return false;
+ buffer_resize(buffer, st.st_size + used);
+ return buffer_read_all(buffer, fd);
+}
diff --git a/util/buffer.h b/util/buffer.h
new file mode 100644
index 0000000..c28a640
--- /dev/null
+++ b/util/buffer.h
@@ -0,0 +1,129 @@
+/*
+ * Counted, reusable memory buffer.
+ *
+ * A buffer is an allocated bit of memory with a known size and a separate
+ * data length. It's intended to store strings and can be reused repeatedly
+ * to minimize the number of memory allocations. Buffers increase in
+ * increments of 1K, or double for some operations.
+ *
+ * A buffer contains a notion of the data that's been used and the data
+ * that's been left, used when the buffer is an I/O buffer where lots of data
+ * is buffered and then slowly processed out of the buffer. The total length
+ * of the data is used + left. If a buffer is just used to store some data,
+ * used can be set to 0 and left stores the length of the data.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle@eyrie.org>
+ * Copyright 2011, 2012
+ * The Board of Trustees of the Leland Stanford Junior University
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UTIL_BUFFER_H
+#define UTIL_BUFFER_H 1
+
+#include <config.h>
+#include <portable/macros.h>
+#include <portable/stdbool.h>
+
+#include <stdarg.h>
+#include <sys/types.h>
+
+struct buffer {
+ size_t size; /* Total allocated length. */
+ size_t used; /* Data already used. */
+ size_t left; /* Remaining unused data. */
+ char *data; /* Pointer to allocated memory. */
+};
+
+BEGIN_DECLS
+
+/* Allocate a new buffer and initialize its contents. */
+struct buffer *buffer_new(void);
+
+/* Free an allocated buffer. */
+void buffer_free(struct buffer *);
+
+/*
+ * Resize a buffer to be at least as large as the provided size. Invalidates
+ * pointers into the buffer.
+ */
+void buffer_resize(struct buffer *, size_t);
+
+/*
+ * Compact a buffer, removing all used data and moving unused data to the
+ * beginning of the buffer. Invalidates pointers into the buffer.
+ */
+void buffer_compact(struct buffer *);
+
+/* Set the buffer contents, ignoring anything currently there. */
+void buffer_set(struct buffer *, const char *data, size_t length);
+
+/*
+ * Set the buffer contents via a sprintf-style format string. No trailing
+ * nul is added.
+ */
+void buffer_sprintf(struct buffer *, const char *, ...)
+ __attribute__((__format__(printf, 2, 3)));
+void buffer_vsprintf(struct buffer *, const char *, va_list);
+
+/* Append data to the buffer. */
+void buffer_append(struct buffer *, const char *data, size_t length);
+
+/* Append via an sprintf-style format string. No trailing nul is added. */
+void buffer_append_sprintf(struct buffer *, const char *, ...)
+ __attribute__((__format__(printf, 2, 3)));
+void buffer_append_vsprintf(struct buffer *, const char *, va_list);
+
+/* Swap the contents of two buffers. */
+void buffer_swap(struct buffer *, struct buffer *);
+
+/*
+ * Find the given string in the unconsumed data in a buffer. start is an
+ * offset into the unused data specifying where to start the search (to save
+ * time with multiple searches). Pass 0 to start the search at the beginning
+ * of the unused data. Returns true if the terminator is found, putting the
+ * offset (into the unused data space) of the beginning of the terminator into
+ * the fourth argument. Returns false if the terminator isn't found.
+ */
+bool buffer_find_string(struct buffer *, const char *, size_t start,
+ size_t *offset);
+
+/*
+ * Read from a file descriptor into a buffer, up to the available space in the
+ * buffer. Return the number of characters read.
+ */
+ssize_t buffer_read(struct buffer *, int fd);
+
+/* Read from a file descriptor into a buffer until end of file is reached. */
+bool buffer_read_all(struct buffer *, int fd);
+
+/*
+ * Read the contents of a file into a buffer. This should be used instead of
+ * buffer_read_all when fstat can be called on the file descriptor.
+ */
+bool buffer_read_file(struct buffer *, int fd);
+
+END_DECLS
+
+#endif /* INN_BUFFER_H */