--- /dev/null
+/*
+ * libusual - Utility library for C
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
--- /dev/null
+# config
+DEFS = -DCASSERT
+CC = gcc
+MKAR = ar rcs
+CFLAGS = -O2 -g -Wall $(WFLAGS)
+CPPFLAGS = $(USUAL_CPPFLAGS)
+
+# sources
+USUAL_DIR = .
+USUAL_OBJDIR = obj
+USUAL_MODULES = $(subst .h,, $(notdir $(wildcard usual/*.h)))
+include $(USUAL_DIR)/Setup.mk
+
+# extra warning flags
+WFLAGS = -Wextra -Wno-unused-parameter -Wno-missing-field-initializers \
+ -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement \
+ -Wold-style-definition -Wstrict-prototypes -Wundef -Wformat -Wnonnull \
+ -Wstrict-overflow
+
+# full path for files
+srcs = $(USUAL_SRCS)
+hdrs = $(USUAL_HDRS)
+objs = $(USUAL_OBJS)
+
+# Quiet by default, 'make V=1' shows commands
+V=0
+ifeq ($(V), 0)
+Q = @
+E = @echo
+else
+Q =
+E = @true
+endif
+
+# rules follow
+
+all: libusual.a obj/testcompile
+
+libusual.a: $(objs)
+ $(E) " AR" $@
+ $(Q) $(MKAR) $@ $(objs)
+
+obj/%.o: usual/%.c $(hdrs)
+ @mkdir -p obj
+ $(E) " CC" $<
+ $(Q) $(CC) -c -o $@ $(DEFS) $(CPPFLAGS) $(CFLAGS) $<
+
+obj/testcompile: test/compile.c libusual.a $(hdrs)
+ $(E) " CHECK" $<
+ $(Q) $(CC) -o $@ $(DEFS) $(CPPFLAGS) $(CFLAGS) $< $(USUAL_LDFLAGS) $(USUAL_LIBS)
+
+clean:
+ rm -f libusual.a obj/*.o obj/test*
+
--- /dev/null
+= libusual =
+
+Collection of various code useful for writing server code.
+
--- /dev/null
+
+#USUAL_DIR = .
+#USUAL_MODULES = fileutil logging lookup3 bitswap misc
+#USUAL_OBJDIR
+
+USUAL_HDRS = $(addprefix $(USUAL_DIR)/usual/, $(addsuffix .h, $(USUAL_MODULES)))
+USUAL_CPPFLAGS = -I$(USUAL_DIR)
+
+# target: local objs
+USUAL_OBJDIR ?= .
+USUAL_SRCS = $(wildcard $(addprefix $(USUAL_DIR)/usual/, $(addsuffix .c, $(USUAL_MODULES))))
+USUAL_OBJS = $(addprefix $(USUAL_OBJDIR)/, $(notdir $(USUAL_SRCS:.c=.o)))
+# needs following rule:
+#$(USUAL_OBJDIR)/%.o: $(USUAL_DIR)/usual/%.c $(USUAL_HDRS)
+# $(CC) -c -o $@ $< $(DEFS) $(CPPFLAGS) $(CFLAGS)
+
+# target: libusual.a
+USUAL_LIBS = -lusual
+USUAL_LDFLAGS = -L$(USUAL_DIR)
+
--- /dev/null
+#include <usual/aatree.h>
+#include <usual/alloc.h>
+#include <usual/base.h>
+#include <usual/cbtree.h>
+#include <usual/cfparser.h>
+#include <usual/daemon.h>
+#include <usual/event.h>
+#include <usual/fileutil.h>
+#include <usual/list.h>
+#include <usual/logging.h>
+#include <usual/lookup3.h>
+#include <usual/md5.h>
+#include <usual/safeio.h>
+#include <usual/slab.h>
+#include <usual/socket.h>
+#include <usual/statlist.h>
+#include <usual/string.h>
+#include <usual/time.h>
+
+int main(void)
+{
+ struct AATree aatree;
+ struct CBTree *cbtree;
+ struct md5_ctx md5;
+ char buf[128];
+
+ aatree_init(&aatree, NULL, NULL);
+ cbtree = cbtree_create(NULL);
+ daemonize(NULL, NULL);
+ hash_lookup3("foo", 3);
+ if (!event_init())
+ log_debug("test");
+ if (!parse_ini_file("foo", NULL, NULL))
+ log_debug("test");
+ file_size("foo");
+ md5_reset(&md5);
+ strlcpy(buf, "foo", sizeof(buf));
+ return 0;
+}
+
--- /dev/null
+/*
+ * AA-Tree - Binary tree with embeddable nodes.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Self-balancing binary tree.
+ *
+ * Here is an implementation of AA-tree (Arne Andersson tree)
+ * which is simplification of Red-Black tree.
+ *
+ * Red-black tree has following properties that must be kept:
+ * 1. A node is either red or black.
+ * 2. The root is black.
+ * 3. All leaves (NIL nodes) are black.
+ * 4. Both childen of red node are black.
+ * 5. Every path from root to leaf contains same number of black nodes.
+ *
+ * AA-tree adds additional property:
+ * 6. Red node can exist only as a right node.
+ *
+ * Red-black tree properties quarantee that the longest path is max 2x longer
+ * than shortest path (B-R-B-R-B-R-B vs. B-B-B-B) thus the tree will be roughly
+ * balanced. Also it has good worst-case guarantees for insertion and deletion,
+ * which makes it good tool for real-time applications.
+ *
+ * AA-tree removes most special cases from RB-tree, thus making resulting
+ * code lot simpler. It requires slightly more rotations when inserting
+ * and deleting but also keeps the tree more balanced.
+ */
+
+
+#include "aatree.h"
+
+#include <stddef.h> /* for NULL */
+
+typedef struct AATree Tree;
+typedef struct AANode Node;
+
+/*
+ * NIL node
+ */
+#define NIL (&_nil)
+static struct AANode _nil = { &_nil, &_nil, 0 };
+
+/*
+ * Rebalancing. AA-tree needs only 2 operations
+ * to keep the tree balanced.
+ */
+
+/*
+ * Fix red on left.
+ *
+ * X Y
+ * / --> \
+ * Y X
+ * \ /
+ * a a
+ */
+static inline Node * skew(Node *x)
+{
+ Node *y = x->left;
+ if (x->level == y->level && x != NIL) {
+ x->left = y->right;
+ y->right = x;
+ return y;
+ }
+ return x;
+}
+
+/*
+ * Fix 2 reds on right.
+ *
+ * X Y
+ * \ / \
+ * Y --> X Z
+ * / \ \
+ * a Z a
+ */
+static inline Node * split(Node *x)
+{
+ Node *y = x->right;
+ if (x->level == y->right->level && x != NIL) {
+ x->right = y->left;
+ y->left = x;
+ y->level++;
+ return y;
+ }
+ return x;
+}
+
+/* insert is easy */
+static Node *rebalance_on_insert(Node *current)
+{
+ return split(skew(current));
+}
+
+/* remove is bit more tricky */
+static Node *rebalance_on_remove(Node *current)
+{
+ /*
+ * Removal can create a gap in levels,
+ * fix it by lowering current->level.
+ */
+ if (current->left->level < current->level - 1
+ || current->right->level < current->level - 1)
+ {
+ current->level--;
+
+ /* if ->right is red, change it's level too */
+ if (current->right->level > current->level)
+ current->right->level = current->level;
+
+ /* reshape, ask Arne about those */
+ current = skew(current);
+ current->right = skew(current->right);
+ current->right->right = skew(current->right->right);
+ current = split(current);
+ current->right = split(current->right);
+ }
+ return current;
+}
+
+/*
+ * Recursive insertion
+ */
+
+static Node * insert_sub(Tree *tree, Node *current, long value, Node *node)
+{
+ int cmp;
+
+ if (current == NIL) {
+ /*
+ * Init node as late as possible, to avoid corrupting
+ * the tree in case it is already added.
+ */
+ node->left = node->right = NIL;
+ node->level = 1;
+
+ tree->count++;
+ return node;
+ }
+
+ /* recursive insert */
+ cmp = tree->node_cmp(value, current);
+ if (cmp > 0)
+ current->right = insert_sub(tree, current->right, value, node);
+ else if (cmp < 0)
+ current->left = insert_sub(tree, current->left, value, node);
+ else
+ /* already exists? */
+ return current;
+
+ return rebalance_on_insert(current);
+}
+
+void aatree_insert(Tree *tree, long value, Node *node)
+{
+ tree->root = insert_sub(tree, tree->root, value, node);
+}
+
+/*
+ * Recursive removal
+ */
+
+/* remove_sub could be used for that, but want to avoid comparisions */
+static Node *steal_leftmost(Tree *tree, Node *current, Node **save_p)
+{
+ if (current->left == NIL) {
+ *save_p = current;
+ return current->right;
+ }
+
+ current->left = steal_leftmost(tree, current->left, save_p);
+ return rebalance_on_remove(current);
+}
+
+/* drop this node from tree */
+static Node *drop_this_node(Tree *tree, Node *old)
+{
+ Node *new = NIL;
+
+ if (old->left == NIL)
+ new = old->right;
+ else if (old->right == NIL)
+ new = old->left;
+ else {
+ /*
+ * Picking nearest from right is better than from left,
+ * due to asymmetry of the AA-tree. It will result in
+ * less tree operations in the long run,
+ */
+ old->right = steal_leftmost(tree, old->right, &new);
+
+ /* take old node's place */
+ *new = *old;
+ }
+
+ /* cleanup for old node */
+ if (tree->release_cb)
+ tree->release_cb(old, tree);
+ tree->count--;
+
+ return new;
+}
+
+static Node *remove_sub(Tree *tree, Node *current, long value)
+{
+ int cmp;
+
+ /* not found? */
+ if (current == NIL)
+ return current;
+
+ cmp = tree->node_cmp(value, current);
+ if (cmp > 0)
+ current->right = remove_sub(tree, current->right, value);
+ else if (cmp < 0)
+ current->left = remove_sub(tree, current->left, value);
+ else
+ current = drop_this_node(tree, current);
+
+ return rebalance_on_remove(current);
+}
+
+void aatree_remove(Tree *tree, long value)
+{
+ tree->root = remove_sub(tree, tree->root, value);
+}
+
+/*
+ * Walking all nodes
+ */
+
+static void walk_sub(Node *current, enum AATreeWalkType wtype,
+ aatree_walker_f walker, void *arg)
+{
+ if (current == NIL)
+ return;
+
+ switch (wtype) {
+ case AA_WALK_IN_ORDER:
+ walk_sub(current->left, wtype, walker, arg);
+ walker(current, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ break;
+ case AA_WALK_POST_ORDER:
+ walk_sub(current->left, wtype, walker, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ walker(current, arg);
+ break;
+ case AA_WALK_PRE_ORDER:
+ walker(current, arg);
+ walk_sub(current->left, wtype, walker, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ break;
+ }
+}
+
+/* walk tree in correct order */
+void aatree_walk(Tree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg)
+{
+ walk_sub(tree->root, wtype, walker, arg);
+}
+
+/* walk tree in bottom-up order, so that walker can destroy the nodes */
+void aatree_destroy(Tree *tree)
+{
+ walk_sub(tree->root, AA_WALK_POST_ORDER, tree->release_cb, tree);
+
+ /* reset tree */
+ tree->root = NIL;
+ tree->count = 0;
+}
+
+/* prepare tree */
+void aatree_init(Tree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb)
+{
+ tree->root = NIL;
+ tree->count = 0;
+ tree->node_cmp = cmpfn;
+ tree->release_cb = release_cb;
+}
+
+/*
+ * search function
+ */
+Node *aatree_search(Tree *tree, long value)
+{
+ Node *current = tree->root;
+ while (current != NIL) {
+ int cmp = tree->node_cmp(value, current);
+ if (cmp > 0)
+ current = current->right;
+ else if (cmp < 0)
+ current = current->left;
+ else
+ return current;
+ }
+ return NULL;
+}
+
--- /dev/null
+/*
+ * AA-Tree - Binary tree with embeddable nodes.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_AATREE_H_
+#define _USUAL_AATREE_H_
+
+struct AATree;
+struct AANode;
+
+typedef int (*aatree_cmp_f)(long, struct AANode *node);
+typedef void (*aatree_walker_f)(struct AANode *n, void *arg);
+
+/*
+ * Tree header, for storing helper functions.
+ */
+struct AATree {
+ struct AANode *root;
+ int count;
+ aatree_cmp_f node_cmp;
+ aatree_walker_f release_cb;
+};
+
+/*
+ * Tree node.
+ */
+struct AANode {
+ struct AANode *left; /* smaller values */
+ struct AANode *right; /* larger values */
+ int level; /* number of black nodes to leaf */
+};
+
+/*
+ * walk order
+ */
+enum AATreeWalkType {
+ AA_WALK_IN_ORDER = 0, /* left->self->right */
+ AA_WALK_PRE_ORDER = 1, /* self->left->right */
+ AA_WALK_POST_ORDER = 2, /* left->right->self */
+};
+
+void aatree_init(struct AATree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb);
+struct AANode *aatree_search(struct AATree *tree, long value);
+void aatree_insert(struct AATree *tree, long value, struct AANode *node);
+void aatree_remove(struct AATree *tree, long value);
+void aatree_walk(struct AATree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg);
+void aatree_destroy(struct AATree *tree);
+
+/* aatree does not use NULL pointers */
+static inline int aatree_is_nil_node(struct AANode *node)
+{
+ return (node->left == node);
+}
+
+#endif
--- /dev/null
+/*
+ * Alloc helpers.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_ALLOC_
+#define _USUAL_ALLOC_
+
+#include <usual/base.h>
+
+#include <stdlib.h>
+
+/*
+ * Function: free
+ *
+ * Fix posix bug by accepting const pointer.
+ */
+static inline void sane_free(const void *p)
+{
+ free((void *)p);
+}
+#define free(x) sane_free(x)
+
+/*
+ * Function: zmalloc
+ *
+ * Zeroing malloc
+ */
+_MUSTCHECK
+static inline void *zmalloc(size_t len)
+{
+ return calloc(1, len);
+}
+
+#endif
+
--- /dev/null
+/*
+ * Basic C environment.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_BASE_H_
+#define _USUAL_BASE_H_
+
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/* give offset of a field inside struct */
+#ifndef offsetof
+#define offsetof(type, field) ((unsigned long)&(((type *)0)->field))
+#endif
+
+/* given pointer to field inside struct, return pointer to struct */
+#ifndef container_of
+#define container_of(ptr, type, field) ((type *)((char *)(ptr) - offsetof(type, field)))
+#endif
+
+/* power-of-2 alignment */
+#ifndef CUSTOM_ALIGN
+#define CUSTOM_ALIGN(x, a) (((unsigned long)(x) + (a) - 1) & ~((a) - 1))
+#endif
+
+/* preferred alignment */
+#ifndef ALIGN
+#define ALIGN(x) CUSTOM_ALIGN(x, sizeof(long))
+#endif
+
+
+/*
+ * make compiler do something useful
+ */
+
+#ifndef _MUSTCHECK
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+
+/* additional error checking */
+#define _MUSTCHECK __attribute__((warn_unused_result))
+#define _DEPRECATED __attribute__((deprecated))
+#define _PRINTF(fmtpos, argpos) __attribute__((format(printf, fmtpos, argpos)))
+#define _MALLOC __attribute__((malloc))
+
+/* compiler hints - those do not seem to work well */
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define likely(x) __builtin_expect(!!(x), 1)
+
+#else /* non gcc */
+
+#define _MUSTCHECK
+#define _DEPRECATED
+#define _PRINTF(x,y)
+#define _MALLOC
+#define unlikely(x) x
+#define likely(x) x
+
+#endif
+#endif
+
+/* assert() that uses our logging */
+#ifndef Assert
+#ifdef CASSERT
+#include <stdlib.h>
+void log_fatal(const char *file, int line, const char *func, bool show_perror, const char *s, ...) _PRINTF(5, 6);
+#define Assert(e) \
+ do { \
+ if (unlikely(!(e))) { \
+ log_fatal(__FILE__, __LINE__, __func__, false, \
+ "Assert(%s) failed", #e); \
+ abort(); \
+ } \
+ } while (0)
+#else
+#define Assert(e)
+#endif
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * Crit-bit tree / binary radix tree.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Associates a C string with user pointer (called "obj").
+ *
+ * Requires it's own internal nodes, thus not embeddable
+ * to user structs.
+ */
+
+#include <usual/cbtree.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * - Childs are either other nodes or user pointers.
+ * User pointers have lowest bit set.
+ *
+ * - All nodes have both childs.
+ *
+ * - Keys are handled as having infinite length,
+ * zero-filled after actual end.
+ */
+
+struct Node {
+ struct Node *child[2];
+ unsigned bitpos;
+};
+
+struct CBTree {
+ struct Node *root;
+ const char *(*get_key)(void *obj);
+};
+
+#define SAME_KEY 0xFFFFFFFF
+
+
+/*
+ * Low-level operations.
+ */
+
+/* does ptr point to user object or slot */
+static inline int is_node(void *ptr)
+{
+ return ((uintptr_t)(ptr) & 1) == 0;
+}
+
+/* flag pointer as pointing to user object */
+static inline void *set_external(const void *obj)
+{
+ return (void*)((uintptr_t)(obj) | 1);
+}
+
+/* remove flag from user pointer */
+static inline void *get_external(void *extval)
+{
+ return (void*)((uintptr_t)(extval) & (~1));
+}
+
+/* get specific bit from string */
+static inline unsigned get_bit(unsigned bitpos, const char *key, unsigned klen)
+{
+ unsigned pos = bitpos / 8;
+ unsigned bit = 7 - (bitpos % 8);
+ return (pos < klen) && (key[pos] & (1 << bit));
+}
+
+/* use callback to get key for a stored object */
+static inline const char *get_key(struct CBTree *tree, void *obj)
+{
+ return tree->get_key(obj);
+}
+
+/* Find first differing bit on 2 zero-terminated strings. */
+static unsigned find_crit_bit(const char *a, const char *b)
+{
+ unsigned i, c, pos;
+
+ /* find differing byte */
+ for (i = 0; ; i++) {
+ /* this handles also size difference */
+ if (a[i] != b[i])
+ break;
+
+ /* equal strings? */
+ if (a[i] == 0)
+ return SAME_KEY;
+ }
+
+ /* calculate bits that differ */
+ c = a[i] ^ b[i];
+
+ /* find the first one */
+ pos = i * 8;
+ while ((c & 0x80) == 0) {
+ c <<= 1;
+ pos++;
+ }
+ return pos;
+}
+
+
+/*
+ * Lookup
+ */
+
+/* walk nodes until external pointer is found */
+static void *raw_lookup(struct CBTree *tree, const char *key, unsigned klen)
+{
+ struct Node *node = tree->root;
+ unsigned bit;
+ while (is_node(node)) {
+ bit = get_bit(node->bitpos, key, klen);
+ node = node->child[bit];
+ }
+ return get_external(node);
+}
+
+/* actual lookup. returns obj ptr or NULL of not found */
+void *cbtree_lookup(struct CBTree *tree, const char *key)
+{
+ unsigned klen;
+ void *obj;
+
+ if (!tree->root)
+ return NULL;
+
+ /* find match based on bits we know about */
+ klen = strlen(key);
+ obj = raw_lookup(tree, key, klen);
+
+ /* need to check if the object actually matches */
+ if (strcmp(key, get_key(tree, obj)) == 0)
+ return obj;
+
+ return NULL;
+}
+
+
+/*
+ * Insertion.
+ */
+
+/* node allocation */
+static struct Node *new_node(void)
+{
+ struct Node *node = malloc(sizeof(*node));
+ memset(node, 0, sizeof(*node));
+ return node;
+}
+
+/* insert into empty tree */
+static bool insert_first(struct CBTree *tree, void *obj)
+{
+ tree->root = set_external(obj);
+ return true;
+}
+
+/* insert into specific bit-position */
+static bool insert_at(struct CBTree *tree, unsigned newbit, const char *key, unsigned klen, void *obj)
+{
+ /* location of current node/obj pointer under examination */
+ struct Node **pos = &tree->root;
+ struct Node *node;
+ unsigned bit;
+
+ while (is_node(*pos) && ((*pos)->bitpos < newbit)) {
+ bit = get_bit((*pos)->bitpos, key, klen);
+ pos = &(*pos)->child[bit];
+ }
+
+ bit = get_bit(newbit, key, klen);
+ node = new_node();
+ if (!node)
+ return false;
+ node->bitpos = newbit;
+ node->child[bit] = set_external(obj);
+ node->child[bit ^ 1] = *pos;
+ *pos = node;
+ return true;
+}
+
+/* actual insert: returns true -> insert ok or key found, false -> malloc failure */
+bool cbtree_insert(struct CBTree *tree, void *obj)
+{
+ const char *key;
+ unsigned newbit, klen;
+ void *old_obj;
+
+ if (!tree->root)
+ return insert_first(tree, obj);
+
+ /* match bits we know about */
+ key = get_key(tree, obj);
+ klen = strlen(key);
+ old_obj = raw_lookup(tree, key, klen);
+
+ /* first differing bit is the target position */
+ newbit = find_crit_bit(key, get_key(tree, old_obj));
+ if (newbit == SAME_KEY)
+ return true;
+ return insert_at(tree, newbit, key, klen, obj);
+}
+
+
+/*
+ * Key deletion.
+ */
+
+/* true -> object was found and removed, false -> not found */
+bool cbtree_delete(struct CBTree *tree, const char *key)
+{
+ void *obj, *tmp;
+ unsigned bit = 0;
+ unsigned klen = strlen(key);
+ /* location of current node/obj pointer under examination */
+ struct Node **pos = &tree->root;
+ /* if 'pos' has user obj, prev_pos has internal node pointing to it */
+ struct Node **prev_pos = NULL;
+
+ if (!tree->root)
+ return false;
+
+ /* match bits we know about */
+ while (is_node(*pos)) {
+ bit = get_bit((*pos)->bitpos, key, klen);
+ prev_pos = pos;
+ pos = &(*pos)->child[bit];
+ }
+
+ /* does the key actually matches */
+ obj = get_external(*pos);
+ if (strcmp(key, get_key(tree, obj)) != 0)
+ return false;
+
+ /* drop the internal node pointing to our key */
+ if (prev_pos) {
+ tmp = *prev_pos;
+ *prev_pos = (*prev_pos)->child[bit ^ 1];
+ free(tmp);
+ } else {
+ tree->root = NULL;
+ }
+ return true;
+}
+
+/*
+ * Management.
+ */
+
+/* create takes user function to query object for it's key */
+struct CBTree *cbtree_create(cbtree_getkey_func get_key_fn)
+{
+ struct CBTree *tree = malloc(sizeof(*tree));
+ tree->root = NULL;
+ tree->get_key = get_key_fn;
+ return tree;
+}
+
+/* recursive freeing */
+static void destroy_node(struct Node *node)
+{
+ if (is_node(node->child[0]))
+ destroy_node(node->child[0]);
+ if (is_node(node->child[1]))
+ destroy_node(node->child[1]);
+ free(node);
+}
+
+/* Free tree and all it's internal nodes. */
+void cbtree_destroy(struct CBTree *tree)
+{
+ if (tree->root && is_node(tree->root))
+ destroy_node(tree->root);
+ tree->root = NULL;
+ free(tree);
+}
+
--- /dev/null
+/*
+ * Crit-bit tree / binary radix tree.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_CBTREE_H_
+#define _USUAL_CBTREE_H_
+
+#include <usual/base.h>
+
+typedef const char *(*cbtree_getkey_func)(void *obj);
+
+struct CBTree;
+
+struct CBTree *cbtree_create(cbtree_getkey_func get_key_fn);
+void cbtree_destroy(struct CBTree *tree);
+
+bool cbtree_insert(struct CBTree *tree, void *obj) _MUSTCHECK;
+bool cbtree_delete(struct CBTree *tree, const char *key);
+
+void *cbtree_lookup(struct CBTree *tree, const char *key);
+
+#endif
+
--- /dev/null
+/*
+ * Config file parser.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <usual/cfparser.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <usual/fileutil.h>
+#include <usual/logging.h>
+#include <usual/time.h>
+
+/*
+ * INI file parser.
+ */
+
+static int count_lines(const char *s, const char *end)
+{
+ int lineno = 1;
+ for (; s < end; s++) {
+ if (*s == '\n')
+ lineno++;
+ }
+ return lineno;
+}
+
+bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg)
+{
+ char *buf;
+ char *p, *key, *val;
+ int klen, vlen;
+ char o1, o2;
+ bool ok;
+
+ buf = load_file(fn);
+ if (buf == NULL)
+ return false;
+
+ p = buf;
+ while (*p) {
+ /* space at the start of line - including empty lines */
+ while (*p && isspace(*p)) p++;
+
+ /* skip comment lines */
+ if (*p == '#' || *p == ';') {
+ while (*p && *p != '\n') p++;
+ continue;
+ }
+ /* got new section */
+ if (*p == '[') {
+ key = ++p;
+ while (*p && *p != ']' && *p != '\n') p++;
+ if (*p != ']')
+ goto syntax_error;
+ o1 = *p;
+ *p = 0;
+
+ log_debug("parse_ini_file: [%s]", key);
+ ok = user_handler(arg, CF_SECT, key, NULL);
+ *p++ = o1;
+ if (!ok)
+ goto failed;
+ continue;
+ }
+
+ /* done? */
+ if (*p == 0)
+ break;
+
+ /* read key val */
+ key = p;
+ while (*p && (isalnum(*p) || strchr("_.-*", *p))) p++;
+ klen = p - key;
+
+ /* expect '=', skip it */
+ while (*p && (*p == ' ' || *p == '\t')) p++;
+ if (*p != '=') {
+ goto syntax_error;
+ } else
+ p++;
+ while (*p && (*p == ' ' || *p == '\t')) p++;
+
+ /* now read value */
+ val = p;
+ while (*p && (*p != '\n'))
+ p++;
+ vlen = p - val;
+ /* eat space at end */
+ while (vlen > 0 && isspace(val[vlen - 1]))
+ vlen--;
+
+ /* skip junk */
+ while (*p && isspace(*p)) p++;
+
+ /* our buf is r/w, so take it easy */
+ o1 = key[klen];
+ o2 = val[vlen];
+ key[klen] = 0;
+ val[vlen] = 0;
+
+ log_debug("parse_ini_file: '%s' = '%s'", key, val);
+
+ ok = user_handler(arg, CF_KEY, key, val);
+
+ /* restore data, to keep count_lines() working */
+ key[klen] = o1;
+ val[vlen] = o2;
+
+ if (!ok)
+ goto failed;
+ }
+
+ free(buf);
+ return true;
+
+syntax_error:
+ log_error("syntax error in configuration (%s:%d), stopping loading", fn, count_lines(buf, p));
+failed:
+ free(buf);
+ return false;
+}
+
+struct LoaderCtx {
+ const struct CfSect *sect_list;
+ const struct CfSect *cur_sect;
+ void *target;
+ void *top_arg;
+};
+
+static bool fill_defaults(struct LoaderCtx *ctx)
+{
+ const struct CfKey *k;
+ void *dst;
+ for (k = ctx->cur_sect->key_list; k->key_name; k++) {
+ if (!k->def_value)
+ continue;
+ dst = (char *)ctx->target + k->key_ofs;
+ if (!k->set_fn(dst, k->def_value))
+ return false;
+ }
+ return true;
+}
+
+static bool load_handler(void *arg, enum CfKeyType ktype, const char *key, const char *val)
+{
+ struct LoaderCtx *ctx = arg;
+ const struct CfSect *s;
+ const struct CfKey *k;
+ void *dst;
+
+ if (ktype == CF_SECT) {
+ for (s = ctx->sect_list; s->sect_name; s++) {
+ if (strcmp(s->sect_name, key) != 0)
+ continue;
+ ctx->cur_sect = s;
+ ctx->target = s->create_target_fn(ctx->top_arg);
+ if (!ctx->target)
+ return false;
+ return fill_defaults(ctx);
+ }
+ log_error("load_init_file: unknown section: %s", key);
+ return false;
+ } else if (!ctx->cur_sect) {
+ log_error("load_init_file: value without section: %s", key);
+ return false;
+ } else {
+ for (k = ctx->cur_sect->key_list; k->key_name; k++) {
+ if (strcmp(k->key_name, key) != 0)
+ continue;
+ dst = (char *)ctx->target + k->key_ofs;
+ return k->set_fn(dst, val);
+ }
+ log_error("load_init_file: unknown key: %s", key);
+ return false;
+ }
+
+ return true;
+}
+
+bool load_ini_file(const char *fn, const struct CfSect *sect_list, void *top_arg)
+{
+ struct LoaderCtx ctx = {
+ .top_arg = top_arg,
+ .sect_list = sect_list,
+ .cur_sect = NULL,
+ .target = NULL,
+ };
+
+ return parse_ini_file(fn, load_handler, &ctx);
+}
+
+/*
+ * Various value parsers.
+ */
+
+bool cf_set_int(void *dst, const char *value)
+{
+ int *ptr = dst;
+ *ptr = atoi(value);
+ return true;
+}
+
+bool cf_set_str(void *dst, const char *value)
+{
+ char **dst_p = dst;
+
+ char *tmp = strdup(value);
+ if (!tmp)
+ return false;
+ if (*dst_p)
+ free(*dst_p);
+ *dst_p = tmp;
+ return true;
+}
+
+bool cf_set_time_usec(void *dst, const char *value)
+{
+ usec_t *ptr = dst;
+ *ptr = USEC * atof(value);
+ return true;
+}
+
+bool cf_set_time_double(void *dst, const char *value)
+{
+ double *ptr = dst;
+ *ptr = atof(value);
+ return true;
+}
+
+
--- /dev/null
+/*
+ * Config file parser.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_CFPARSER_H_
+#define _USUAL_CFPARSER_H_
+
+#include <usual/base.h>
+
+/*
+ * Simple line-by-line parser
+ */
+
+enum CfKeyType {
+ CF_SECT,
+ CF_KEY
+};
+
+typedef bool (*cf_handler_f)(void *arg, enum CfKeyType, const char *key, const char *val);
+
+bool parse_ini_file(const char *fn, cf_handler_f user_handler, void *arg) _MUSTCHECK;
+
+/*
+ * Fancier one.
+ */
+
+typedef void *(*cf_create_target_f)(void *top_arg);
+typedef bool (*cf_setter_f)(void *dst_p, const char *value);
+
+struct CfKey {
+ const char *key_name;
+ cf_setter_f set_fn;
+ unsigned key_ofs;
+ const char *def_value;
+};
+
+struct CfSect {
+ const char *sect_name;
+ cf_create_target_f create_target_fn;
+ const struct CfKey *key_list;
+};
+
+bool load_ini_file(const char *fn, const struct CfSect *sect_list, void *top_arg) _MUSTCHECK;
+
+bool cf_set_str(void *dst, const char *value);
+bool cf_set_int(void *dst, const char *value);
+bool cf_set_time_usec(void *dst, const char *value);
+bool cf_set_time_double(void *dst, const char *value);
+
+#endif
--- /dev/null
+/*
+ * Daemonization & pidfile handling.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <usual/daemon.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <usual/logging.h>
+
+/*
+ * pidfile management.
+ */
+
+static char *g_pidfile;
+
+static void remove_pidfile(void)
+{
+ if (!g_pidfile)
+ return;
+ unlink(g_pidfile);
+ free(g_pidfile);
+ g_pidfile = NULL;
+}
+
+static void check_pidfile(const char *pidfile)
+{
+ char buf[128 + 1];
+ struct stat st;
+ pid_t pid = 0;
+ int fd, res;
+
+ if (!pidfile || !pidfile[0])
+ return;
+
+ /* check if pidfile exists */
+ if (stat(pidfile, &st) < 0) {
+ if (errno != ENOENT)
+ fatal_perror("stat");
+ return;
+ }
+
+ /* read old pid */
+ fd = open(pidfile, O_RDONLY);
+ if (fd < 0)
+ goto locked_pidfile;
+ res = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (res <= 0)
+ goto locked_pidfile;
+
+ /* parse pid */
+ buf[res] = 0;
+ pid = atol(buf);
+ if (pid <= 0)
+ goto locked_pidfile;
+
+ /* check if running */
+ if (kill(pid, 0) >= 0)
+ goto locked_pidfile;
+ if (errno != ESRCH)
+ goto locked_pidfile;
+
+ /* seems the pidfile is not in use */
+ log_info("Stale pidfile, removing");
+ unlink(pidfile);
+ return;
+
+locked_pidfile:
+ fatal("pidfile exists, another instance running?");
+}
+
+static void write_pidfile(const char *pidfile)
+{
+ char buf[64];
+ pid_t pid;
+ int res, fd, len;
+
+ if (!pidfile || !pidfile[0])
+ return;
+
+ if (g_pidfile)
+ free(g_pidfile);
+ g_pidfile = strdup(pidfile);
+ if (!g_pidfile)
+ fatal_perror("cannot alloc pidfile");
+
+ pid = getpid();
+ snprintf(buf, sizeof(buf), "%u\n", (unsigned)pid);
+
+ fd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd < 0)
+ fatal_perror("%s", pidfile);
+ len = strlen(buf);
+loop:
+ res = write(fd, buf, len);
+ if (res < 0) {
+ if (errno == EINTR)
+ goto loop;
+ fatal_perror("%s", pidfile);
+ } else if (res < len) {
+ len -= res;
+ goto loop;
+ }
+ close(fd);
+
+ /* only remove when we have it actually written */
+ atexit(remove_pidfile);
+}
+
+/*
+ * Function: daemonize
+ *
+ * Handle pidfile and daemonization.
+ *
+ * If pidfile is given, check if old process is running.
+ *
+ * If going background is required, require non-empty pidfile
+ * and logfile. Then fork to background and write pidfile.
+ */
+void daemonize(const char *pidfile, bool go_background)
+{
+ int pid, fd;
+
+ if (pidfile && pidfile[0]) {
+ check_pidfile(pidfile);
+ if (!go_background)
+ write_pidfile(pidfile);
+ } else if (go_background)
+ fatal("daemon needs pidfile configured");
+
+ if (!go_background)
+ return;
+
+ if (!cf_logfile && !cf_syslog_ident)
+ fatal("daemon needs logging configured");
+
+ /* send stdin, stdout, stderr to /dev/null */
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ fatal_perror("/dev/null");
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2)
+ close(fd);
+
+ /* fork new process */
+ pid = fork();
+ if (pid < 0)
+ fatal_perror("fork");
+ if (pid > 0)
+ _exit(0);
+
+ /* create new session */
+ pid = setsid();
+ if (pid < 0)
+ fatal_perror("setsid");
+
+ /* fork again to avoid being session leader */
+ pid = fork();
+ if (pid < 0)
+ fatal_perror("fork");
+ if (pid > 0)
+ _exit(0);
+
+ write_pidfile(pidfile);
+}
+
--- /dev/null
+/*
+ * Daemonization & pidfile handling.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_DAEMON_H_
+#define _USUAL_DAEMON_H_
+
+#include <stdbool.h>
+
+void daemonize(const char *pidfile, bool go_background);
+
+#endif
+
--- /dev/null
+/*
+ * event.c - libevent compatible event loop.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Small poll()-based async event loop, API-compatible with libevent.
+ *
+ * For sitations where full libevent is not necessary.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <usual/event.h>
+
+#include <sys/socket.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+#include <stdlib.h>
+
+#include <usual/statlist.h>
+#include <usual/socket.h>
+#include <usual/alloc.h>
+
+/* max number of signals we care about */
+#define MAX_SIGNAL 32
+
+/* if tv_sec is larger, it's absolute timeout */
+#define MAX_REL_TIMEOUT (30*24*60*60)
+
+/* if no nearby timeouts, how many seconds to sleep */
+#define MAX_SLEEP 5
+
+/* extra event flag to track if event is added */
+#define EV_ACTIVE 0x80
+
+struct event_base {
+ struct AATree timeout_tree;
+
+ struct StatList fd_list;
+ struct event **pfd_event;
+ struct pollfd *pfd_list;
+ int pfd_size;
+
+ bool loop_break;
+ bool loop_exit;
+
+ /* signal handling */
+ struct List sig_node;
+ struct List sig_waiters[MAX_SIGNAL];
+ int sig_send, sig_recv;
+ struct event sig_ev;
+ unsigned int sig_seen[MAX_SIGNAL];
+};
+
+/* default event base */
+static struct event_base *current_base = NULL;
+
+/* global signal data */
+static volatile unsigned int sig_count[MAX_SIGNAL];
+static bool signal_set_up[MAX_SIGNAL];
+static struct sigaction old_handler[MAX_SIGNAL];
+static LIST(sig_base_list);
+
+
+static bool sig_init(struct event_base *base, int sig);
+static void sig_close(struct event_base *base);
+
+/*
+ * Debugging.
+ */
+
+#ifdef CASSERT
+#include <usual/logging.h>
+#include <usual/time.h>
+#include <stdarg.h>
+#include <stdio.h>
+static void base_dbg(struct event_base *base, const char *s, ...)
+{
+ va_list ap;
+ char buf[1024];
+
+ va_start(ap, s);
+ vsnprintf(buf, sizeof(buf), s, ap);
+ va_end(ap);
+
+ log_noise("event base=%p: fdlist=%u timeouts=%d pfds=%d: %s",
+ base, statlist_count(&base->fd_list),
+ base->timeout_tree.count,
+ base->pfd_size, buf);
+}
+
+static void ev_dbg(struct event *ev, const char *s, ...)
+{
+ va_list ap;
+ char buf[1024], tval[128];
+ const char *typ = (ev->flags & EV_SIGNAL) ? "sig" : "fd";
+
+ va_start(ap, s);
+ vsnprintf(buf, sizeof(buf), s, ap);
+ va_end(ap);
+
+ log_noise("event %s %d (flags=%s%s%s%s%s) [%s]: %s", typ, ev->fd,
+ (ev->flags & EV_ACTIVE) ? "A" : "",
+ (ev->flags & EV_PERSIST) ? "P" : "",
+ (ev->flags & EV_TIMEOUT) ? "T" : "",
+ (ev->flags & EV_READ) ? "R" : "",
+ (ev->flags & EV_WRITE) ? "W" : "",
+ (ev->flags & EV_TIMEOUT)
+ ? format_time_ms(&ev->timeout, tval, sizeof(tval))
+ : "-",
+ buf);
+}
+#else
+#define base_dbg(b, ...)
+#define ev_dbg(b, ...)
+#endif
+
+/*
+ * Helper functions.
+ */
+
+/* convert user tv to absolute tv */
+static void fill_timeout(struct timeval *dst, struct timeval *tv)
+{
+ if (tv->tv_sec < MAX_REL_TIMEOUT) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ timeradd(&now, tv, dst);
+ } else {
+ *dst = *tv;
+ }
+}
+
+/* compare timevals */
+static int cmp_tv(struct timeval *tv1, struct timeval *tv2)
+{
+ if (tv1->tv_sec == tv2->tv_sec) {
+ if (tv1->tv_usec == tv2->tv_usec)
+ return 0;
+ return (tv1->tv_usec < tv2->tv_usec) ? -1 : 1;
+ } else {
+ return (tv1->tv_sec < tv2->tv_sec) ? -1 : 1;
+ }
+
+}
+
+/* compare events by their timeouts */
+static int cmp_timeout(long item, struct AANode *n1)
+{
+ struct AANode *n0 = (void *)item;
+ struct event *ev0 = container_of(n0, struct event, timeout_node);
+ struct event *ev1 = container_of(n1, struct event, timeout_node);
+ int res = cmp_tv(&ev0->timeout, &ev1->timeout);
+ if (res != 0)
+ return res;
+ /* compare pointers, to make same timeouts inequal */
+ if (ev0 == ev1)
+ return 0;
+ return (ev0 < ev1) ? -1 : 1;
+}
+
+/* enlarge pollfd array if needed */
+static bool make_room(struct event_base *base, int need)
+{
+ int total;
+ void *tmp1;
+ void *tmp2;
+
+ if (need < base->pfd_size)
+ return true;
+
+ total = base->pfd_size * 2;
+ if (total < 8) total = 8;
+ while (total < need)
+ total *= 2;
+
+ tmp1 = realloc(base->pfd_list, total * sizeof(struct pollfd));
+ if (!tmp1)
+ return false;
+ base->pfd_list = tmp1;
+
+ tmp2 = realloc(base->pfd_event, total * sizeof(struct event *));
+ if (!tmp2)
+ return false;
+ base->pfd_event = tmp2;
+
+ base->pfd_size = total;
+ return true;
+}
+
+/*
+ * Single base functions.
+ */
+
+int event_loop(int loop_flags)
+{
+ return event_base_loop(current_base, loop_flags);
+}
+
+int event_loopbreak(void)
+{
+ return event_base_loopbreak(current_base);
+}
+
+void event_set(struct event *ev, int fd, short flags, uevent_cb_f cb, void *arg)
+{
+ event_assign(ev, current_base, fd, flags, cb, arg);
+}
+
+int event_once(int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout)
+{
+ return event_base_once(current_base, fd, flags, cb_func, cb_arg, timeout);
+}
+
+int event_loopexit(struct timeval *timeout)
+{
+ return event_base_loopexit(current_base, timeout);
+}
+
+/*
+ * Event base initialization.
+ */
+
+struct event_base *event_init(void)
+{
+ struct event_base *base;
+ int i;
+
+ base = calloc(1, sizeof(*base));
+
+ /* initialize timeout and fd areas */
+ aatree_init(&base->timeout_tree, cmp_timeout, NULL);
+ statlist_init(&base->fd_list, "fd_list");
+
+ /* initialize signal areas */
+ for (i = 0; i < MAX_SIGNAL; i++)
+ list_init(&base->sig_waiters[i]);
+ list_init(&base->sig_node);
+ base->sig_send = base->sig_recv = -1;
+
+ /* allocate pollfds */
+ if (!make_room(base, 8)) {
+ event_base_free(base);
+ return NULL;
+ }
+
+ if (!current_base)
+ current_base = base;
+ return base;
+}
+
+void event_base_free(struct event_base *base)
+{
+ if (!base) {
+ if (!current_base)
+ return;
+ base = current_base;
+ }
+ if (base == current_base)
+ current_base = NULL;
+ free(base->pfd_event);
+ free(base->pfd_list);
+ sig_close(base);
+ free(base);
+}
+
+int event_base_loopbreak(struct event_base *base)
+{
+ base->loop_break = true;
+ return 0;
+}
+
+/*
+ * Multi-base functions.
+ */
+
+void event_assign(struct event *ev, struct event_base *base, int fd, short flags, uevent_cb_f cb, void *arg)
+{
+ Assert(base);
+ Assert((ev->flags & EV_ACTIVE) == 0);
+
+ ev->fd = fd;
+ ev->base = base;
+ ev->flags = flags;
+ ev->cb_func = cb;
+ ev->cb_arg = arg;
+ ev->ev_idx = -1;
+ list_init(&ev->node);
+ ev_dbg(ev, "event_set");
+}
+
+int event_del(struct event *ev)
+{
+ struct event_base *base = ev->base;
+
+ /* allow repeated deletions */
+ if ((ev->flags & EV_ACTIVE) == 0) {
+ ev_dbg(ev, "event_del for inactive event??");
+ return 0;
+ }
+ ev_dbg(ev, "event_del");
+
+ /* remove from fd/signal list */
+ if (ev->flags & EV_SIGNAL)
+ list_del(&ev->node);
+ else if (ev->flags & (EV_READ | EV_WRITE))
+ statlist_remove(&base->fd_list, &ev->node);
+
+ /* remove from timeout tree */
+ if (ev->flags & EV_TIMEOUT) {
+ aatree_remove(&ev->base->timeout_tree, (long)&ev->timeout_node);
+ ev->flags &= ~EV_TIMEOUT;
+ }
+
+ /* clear reference to pollfd area */
+ if (ev->ev_idx >= 0) {
+ ev->base->pfd_event[ev->ev_idx] = NULL;
+ ev->ev_idx = -1;
+ }
+
+ /* tag inactive */
+ ev->flags &= ~EV_ACTIVE;
+
+ return 0;
+}
+
+
+/* handles only relarive timeouts */
+int event_add(struct event *ev, struct timeval *timeout)
+{
+ struct event_base *base = ev->base;
+
+ Assert((ev->flags & EV_ACTIVE) == 0);
+ Assert(base);
+
+ /* timeout sanity check, but dont use it yet */
+ if (timeout) {
+ if (ev->flags & EV_PERSIST) {
+ errno = EINVAL;
+ return -1;
+ }
+ } else if (ev->flags & EV_TIMEOUT) {
+ ev->flags &= ~EV_TIMEOUT;
+ }
+
+ /* setup signal/fd */
+ if (ev->flags & EV_SIGNAL) {
+ if (ev->flags & (EV_READ|EV_WRITE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!sig_init(base, ev->fd))
+ return -1;
+ list_append(&base->sig_waiters[ev->fd], &ev->node);
+ } else if (ev->flags & (EV_READ|EV_WRITE)) {
+ statlist_append(&base->fd_list, &ev->node);
+ }
+
+ /* now act on timeout */
+ if (timeout) {
+ fill_timeout(&ev->timeout, timeout);
+ ev->flags |= EV_TIMEOUT;
+ aatree_insert(&base->timeout_tree, (long)&ev->timeout_node, &ev->timeout_node);
+ }
+ ev->ev_idx = -1;
+ ev->flags |= EV_ACTIVE;
+
+ ev_dbg(ev, "event_add");
+
+ return 0;
+}
+
+/*
+ * Event loop functions.
+ */
+
+static void deliver_event(struct event *ev, short flags)
+{
+ ev_dbg(ev, "deliver_event: %d", flags);
+
+ /* remove non-persitant event before calling user func */
+ if ((ev->flags & EV_PERSIST) == 0)
+ event_del(ev);
+
+ /* now call user func */
+ ev->cb_func(ev->fd, flags, ev->cb_arg);
+}
+
+static inline struct event *get_smallest_timeout(struct event_base *base)
+{
+ struct AANode *node = base->timeout_tree.root;
+ struct AANode *first = NULL;
+ struct event *ev;
+
+ while (!aatree_is_nil_node(node)) {
+ first = node;
+ node = node->left;
+ }
+ if (first) {
+ ev = container_of(first, struct event, timeout_node);
+ return ev;
+ }
+ return NULL;
+}
+
+static int calc_timeout(struct event_base *base)
+{
+ struct event *ev;
+ int res, secs, usecs;
+ struct timeval now;
+
+ ev = get_smallest_timeout(base);
+ if (!ev)
+ return MAX_SLEEP * 1000;
+
+ gettimeofday(&now, NULL);
+
+ if (now.tv_sec + MAX_SLEEP < ev->timeout.tv_sec)
+ return MAX_SLEEP * 1000;
+ if (ev->timeout.tv_sec < now.tv_sec)
+ return 0;
+
+ secs = ev->timeout.tv_sec - now.tv_sec;
+ usecs = ev->timeout.tv_usec - now.tv_usec;
+ if (usecs < 0) {
+ secs--;
+ usecs += 1000000;
+ }
+ res = (secs * 1000) + ((usecs + 999) / 1000);
+ if (res < 0)
+ return 0;
+ return res;
+}
+
+static void process_fds(struct event_base *base, int pf_cnt)
+{
+ int i;
+
+ for (i = 0; i < pf_cnt; i++) {
+ struct pollfd *pf = &base->pfd_list[i];
+ struct event *ev = base->pfd_event[i];
+ if (!ev)
+ continue;
+ base->pfd_event[i] = NULL;
+ ev->ev_idx = -1; // is it needed?
+ if (pf->revents & (POLLIN | POLLOUT | POLLERR | POLLHUP)) {
+ int flags = ev->flags & (EV_READ | EV_WRITE);
+ deliver_event(ev, flags);
+ }
+ if (base->loop_break)
+ break;
+ }
+}
+
+static void process_timeouts(struct event_base *base)
+{
+ struct timeval now;
+ struct event *ev;
+
+ ev = get_smallest_timeout(base);
+ if (!ev)
+ return;
+
+ gettimeofday(&now, NULL);
+
+ while (ev) {
+ if (timercmp(&now, &ev->timeout, <))
+ break;
+ deliver_event(ev, EV_TIMEOUT);
+ if (base->loop_break)
+ break;
+ ev = get_smallest_timeout(base);
+ }
+}
+
+int event_base_loop(struct event_base *base, int loop_flags)
+{
+ int pf_cnt, res, timeout_ms;
+ struct List *node;
+
+ /* don't loop if non-block was requested */
+ if (loop_flags & EVLOOP_NONBLOCK)
+ loop_flags |= EVLOOP_ONCE;
+
+ base->loop_break = false;
+ base->loop_exit = false;
+loop:
+ if (!make_room(base, statlist_count(&base->fd_list)))
+ return -1;
+
+ pf_cnt = 0;
+ statlist_for_each(node, &base->fd_list) {
+ struct event *ev = container_of(node, struct event, node);
+ struct pollfd *pf;
+
+ ev->ev_idx = pf_cnt++;
+ base->pfd_event[ev->ev_idx] = ev;
+ pf = &base->pfd_list[ev->ev_idx];
+
+ pf->events = 0;
+ pf->revents = 0;
+ pf->fd = ev->fd;
+ if (ev->flags & EV_READ)
+ pf->events |= POLLIN;
+ if (ev->flags & EV_WRITE)
+ pf->events |= POLLOUT;
+ }
+
+ if (loop_flags & EVLOOP_NONBLOCK)
+ timeout_ms = 0;
+ else
+ timeout_ms = calc_timeout(base);
+
+ res = poll(base->pfd_list, pf_cnt, timeout_ms);
+ base_dbg(base, "poll(%d, timeout=%d) = res=%d errno=%d",
+ pf_cnt, timeout_ms, res, res < 0 ? errno : 0);
+
+ if (res == -1 && errno != EINTR)
+ return -1;
+
+ if (res > 0) {
+ process_fds(base, pf_cnt);
+ if (base->loop_break)
+ return 0;
+ }
+
+ process_timeouts(base);
+
+ if (base->loop_break)
+ return 0;
+
+ if (loop_flags & EVLOOP_ONCE)
+ return 0;
+
+ if (base->loop_exit)
+ return 0;
+
+ goto loop;
+}
+
+/*
+ * Signal handling.
+ */
+
+/* global signal handler registered via sigaction() */
+static void uevent_sig_handler(int sig, siginfo_t *si, void *arg)
+{
+ struct List *node, *tmp;
+ struct event_base *base;
+ uint8_t byte = sig;
+ int res;
+
+ if (sig < 0 || sig >= MAX_SIGNAL)
+ return;
+ sig_count[sig]++;
+
+ list_for_each_safe(node, &sig_base_list, tmp) {
+ base = container_of(node, struct event_base, sig_node);
+ if (base->sig_send >= 0) {
+ loop:
+ res = send(base->sig_send, &byte, 1, MSG_NOSIGNAL);
+ if (res == -1 && (errno == EINTR))
+ goto loop;
+ }
+ }
+}
+
+/* close signal resources on one base */
+static void sig_close(struct event_base *base)
+{
+ list_del(&base->sig_node);
+ if (base->sig_send >= 0)
+ close(base->sig_send);
+ if (base->sig_recv >= 0)
+ close(base->sig_recv);
+ base->sig_recv = base->sig_send = -1;
+}
+
+/* call all handlers waiting for specific signal */
+static void deliver_signal(struct event_base *base, int sig)
+{
+ struct List *node, *tmp;
+
+ list_for_each_safe(node, &base->sig_waiters[sig], tmp) {
+ struct event *ev = container_of(node, struct event, node);
+ deliver_event(ev, EV_SIGNAL);
+ }
+}
+
+/* reader from sig socket, calls actual signal handlers */
+static void sig_reader(int fd, short flags, void *arg)
+{
+ struct event_base *base = arg;
+ uint8_t buf[128];
+ int res, sig;
+
+ /* drain the socket */
+loop:
+ res = recv(fd, buf, sizeof(buf), 0);
+ if (res < 0) {
+ if (errno == EINTR)
+ goto loop;
+ if (errno != EAGAIN)
+ goto some_error;
+ } else if (res == 0)
+ goto some_error;
+
+ /* now check for new signals */
+ for (sig = 0; sig < MAX_SIGNAL; sig++) {
+ unsigned glob, local;
+ if (list_empty(&base->sig_waiters[sig]))
+ continue;
+ glob = sig_count[sig];
+ local = base->sig_seen[sig];
+ if (glob != local) {
+ base->sig_seen[sig] = glob;
+ deliver_signal(base, sig);
+ }
+ }
+ return;
+
+some_error:
+ //log_warning("signal reading got error: res=%d err=%s", res, strerror(errno));
+ sig_close(base);
+}
+
+static bool sig_init(struct event_base *base, int sig)
+{
+ int spair[2];
+
+ if (sig < 0 || sig >= MAX_SIGNAL) {
+ errno = EINVAL;
+ return false;
+ }
+
+ /* global handler setup */
+ if (!signal_set_up[sig]) {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = uevent_sig_handler;
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ if (sigaction(sig, &sa, &old_handler[sig]) != 0)
+ return false;
+ }
+
+ /* local handler for base */
+ if (list_empty(&base->sig_node)) {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != 0)
+ return false;
+ if (!socket_setup(spair[0], true))
+ goto failed;
+ if (!socket_setup(spair[1], true))
+ goto failed;
+ event_assign(&base->sig_ev, base, spair[1], EV_READ|EV_PERSIST, sig_reader, base);
+ if (event_add(&base->sig_ev, NULL) != 0)
+ goto failed;
+ base->sig_send = spair[0];
+ base->sig_recv = spair[1];
+ list_append(&sig_base_list, &base->sig_node);
+ }
+
+ /* if first waiter, then ignore previous signals */
+ if (list_empty(&base->sig_waiters[sig]))
+ base->sig_seen[sig] = sig_count[sig];
+ return true;
+
+failed:
+ close(spair[0]);
+ close(spair[1]);
+ return false;
+}
+
+/*
+ * One-time events.
+ */
+
+struct once_event {
+ struct event ev;
+ uevent_cb_f cb_func;
+ void *cb_arg;
+};
+
+static void once_handler(int fd, short flags, void *arg)
+{
+ struct once_event *once = arg;
+ uevent_cb_f cb_func = once->cb_func;
+ void *cb_arg = once->cb_arg;
+
+ free(once);
+ cb_func(fd, flags, cb_arg);
+}
+
+int event_base_once(struct event_base *base, int fd, short flags,
+ uevent_cb_f cb_func, void *cb_arg,
+ struct timeval *timeout)
+{
+ struct once_event *once;
+
+ if (flags & EV_PERSIST) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ once = zmalloc(sizeof(*once));
+ if (!once)
+ return -1;
+
+ event_assign(&once->ev, base, fd, flags, once_handler, once);
+ if (event_add(&once->ev, timeout) != 0) {
+ free(once);
+ return -1;
+ }
+ return 0;
+}
+
+static void loopexit_handler(int fd, short flags, void *arg)
+{
+ struct event_base *base = arg;
+ base->loop_exit = true;
+}
+
+int event_base_loopexit(struct event_base *base, struct timeval *timeout)
+{
+ return event_base_once(base, -1, 0, loopexit_handler, base, timeout);
+}
+
+int event_base_set(struct event_base *base, struct event *ev)
+{
+ if (ev->flags & EV_ACTIVE) {
+ errno = EINVAL;
+ return -1;
+ }
+ ev->base = base;
+ return 0;
+}
+
+/*
+ * Is activated.
+ */
+
+int is_event_initialized(struct event *ev)
+{
+ return (ev->flags & EV_ACTIVE) ? 1 : 0;
+}
+
--- /dev/null
+/*
+ * event.h - libevent compatible event loop.
+ *
+ * Copyright (c) 2009 Marko Kreen
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_EVENT_H_
+#define _USUAL_EVENT_H_
+
+#include <sys/time.h>
+
+#include <usual/aatree.h>
+#include <usual/list.h>
+
+enum EventFlags {
+ EV_TIMEOUT = 1,
+ EV_READ = 2,
+ EV_WRITE = 4,
+ EV_SIGNAL = 8,
+ EV_PERSIST = 16,
+};
+
+enum EventLoopType {
+ EVLOOP_ONCE = 1,
+ EVLOOP_NONBLOCK = 2,
+};
+
+struct event_base;
+
+typedef void (*uevent_cb_f)(int fd, short flags, void *arg);
+
+struct event {
+ struct List node;
+
+ struct timeval timeout;
+ struct AANode timeout_node;
+
+ int ev_idx;
+ struct event_base *base;
+
+ uevent_cb_f cb_func;
+ void *cb_arg;
+
+ int fd;
+ short flags;
+};
+
+struct event_base *event_init(void) _MUSTCHECK;
+void event_base_free(struct event_base *base);
+
+void event_set(struct event *ev, int fd, short flags, uevent_cb_f cb, void *arg);
+int event_loop(int loop_flags) _MUSTCHECK;
+int event_loopbreak(void);
+
+int event_add(struct event *ev, struct timeval *timeout) _MUSTCHECK;
+int event_del(struct event *ev);
+
+void event_assign(struct event *ev, struct event_base *base,
+ int fd, short flags, uevent_cb_f cb, void *cb_arg);
+
+int event_base_loop(struct event_base *base, int loop_flags) _MUSTCHECK;
+int event_base_loopbreak(struct event_base *base);
+
+#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
+#define evtimer_add(ev, tv) event_add(ev, tv)
+
+#define signal_set(ev, sig, cb, arg) event_set(ev, sig, EV_SIGNAL | EV_PERSIST, cb, arg)
+#define signal_add(ev, tv) event_add(ev, tv)
+
+/* random compat */
+int event_once(int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout);
+int event_base_once(struct event_base *base, int fd, short flags, uevent_cb_f cb_func, void *cb_arg, struct timeval *timeout);
+int event_loopexit(struct timeval *timeout);
+int event_base_loopexit(struct event_base *base, struct timeval *timeout);
+int event_base_set(struct event_base *base, struct event *ev);
+
+/* pointless compat */
+#define event_initialized(ev) is_event_initialized(ev)
+#define signal_initialized(ev) is_event_initialized(ev)
+#define evtimer_initialized(ev) is_event_initialized(ev)
+#define event_dispatch() event_loop(0)
+#define event_base_dispatch(base) event_base_loop(base, 0)
+int is_event_initialized(struct event *ev);
+
+#endif
+
--- /dev/null
+/*
+ * File access utils.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <usual/fileutil.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/*
+ * Load text file into C string.
+ */
+
+char *load_file(const char *fn)
+{
+ struct stat st;
+ char *buf = NULL;
+ int res;
+ FILE *f;
+
+ res = stat(fn, &st);
+ if (res < 0)
+ return NULL;
+
+ buf = malloc(st.st_size + 1);
+ if (!buf)
+ return NULL;
+
+ f = fopen(fn, "r");
+ if (!f)
+ return NULL;
+
+ if ((res = fread(buf, 1, st.st_size, f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+
+ fclose(f);
+ buf[st.st_size] = 0;
+
+ return buf;
+}
+
+/*
+ * Read file line-by-line, call user func on each.
+ */
+
+bool foreach_line(const char *fn, procline_cb proc_line, void *arg)
+{
+ char *ln = NULL;
+ size_t len = 0;
+ ssize_t res;
+ FILE *f = fopen(fn, "rb");
+ if (!f)
+ return false;
+ while (1) {
+ res = getline(&ln, &len, f);
+ if (res < 0)
+ break;
+ proc_line(arg, ln, res);
+ }
+ fclose(f);
+ free(ln);
+ return true;
+}
+
+/*
+ * Find file size.
+ */
+
+ssize_t file_size(const char *fn)
+{
+ struct stat st;
+ if (stat(fn, &st) < 0)
+ return -1;
+ return st.st_size;
+}
+
+/*
+ * Map a file into mem.
+ */
+
+int map_file(struct MappedFile *m, const char *fname, int rw)
+{
+ struct stat st;
+ m->fd = open(fname, rw ? O_RDWR : O_RDONLY);
+ if (m->fd < 0)
+ return -1;
+ if (fstat(m->fd, &st) < 0) {
+ close(m->fd);
+ return -1;
+ }
+ m->len = st.st_size;
+ m->ptr = mmap(NULL, m->len, PROT_READ | (rw ? PROT_WRITE : 0),
+ MAP_SHARED, m->fd, 0);
+ if (m->ptr == MAP_FAILED) {
+ close(m->fd);
+ return -1;
+ }
+ return 0;
+}
+
+void unmap_file(struct MappedFile *m)
+{
+ munmap(m->ptr, m->len);
+ close(m->fd);
+ m->ptr = NULL;
+ m->fd = 0;
+}
+
--- /dev/null
+/*
+ * File access utils.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_FILEUTIL_H_
+#define _USUAL_FILEUTIL_H_
+
+#include <usual/base.h>
+
+struct MappedFile {
+ int fd;
+ unsigned len;
+ void *ptr;
+};
+
+typedef void (*procline_cb)(void *arg, const char *line, ssize_t len);
+
+char *load_file(const char *fn);
+
+bool foreach_line(const char *fn, procline_cb proc_line, void *arg);
+
+ssize_t file_size(const char *fn);
+
+int map_file(struct MappedFile *m, const char *fname, int rw) _MUSTCHECK;
+void unmap_file(struct MappedFile *m);
+
+#endif
+
--- /dev/null
+/*
+ * Circular doubly linked list implementation.
+ *
+ * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
+ *
+ * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_LIST_H_
+#define _USUAL_LIST_H_
+
+#include <usual/base.h>
+
+/* list type */
+struct List {
+ struct List *next;
+ struct List *prev;
+};
+
+#define LIST(var) struct List var = { &var, &var }
+
+/* initialize struct */
+static inline void list_init(struct List *list)
+{
+ list->next = list->prev = list;
+}
+
+/* is list empty? */
+static inline int list_empty(const struct List *list)
+{
+ return list->next == list;
+}
+
+/* add item to the start of the list */
+static inline struct List *list_prepend(struct List *list, struct List *item)
+{
+ item->next = list->next;
+ item->prev = list;
+ list->next->prev = item;
+ list->next = item;
+ return item;
+}
+
+/* add item to the end of the list */
+static inline struct List *list_append(struct List *list, struct List *item)
+{
+ item->next = list;
+ item->prev = list->prev;
+ list->prev->next = item;
+ list->prev = item;
+ return item;
+}
+
+/* remove item from list */
+static inline struct List *list_del(struct List *item)
+{
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+ item->next = item->prev = item;
+ return item;
+}
+
+/* remove first from list and return */
+static inline struct List *list_pop(struct List *list)
+{
+ if (list_empty(list))
+ return NULL;
+ return list_del(list->next);
+}
+
+/* remove first from list and return */
+static inline struct List *list_first(const struct List *list)
+{
+ if (list_empty(list))
+ return NULL;
+ return list->next;
+}
+
+/* remove first elem from list and return with casting */
+#define list_pop_type(list, typ, field) \
+ (list_empty(list) ? NULL \
+ : container_of(list_del((list)->next), typ, field))
+
+/* loop over list */
+#define list_for_each(item, list) \
+ for ((item) = (list)->next; \
+ (item) != (list); \
+ (item) = (item)->next)
+
+/* loop over list and allow removing item */
+#define list_for_each_safe(item, list, tmp) \
+ for ((item) = (list)->next, (tmp) = (list)->next->next; \
+ (item) != (list); \
+ (item) = (tmp), (tmp) = (tmp)->next)
+
+#endif
+
--- /dev/null
+/*
+ * Logging for unix service.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <usual/logging.h>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <usual/time.h>
+
+int cf_quiet = 0;
+int cf_verbose = 0;
+const char *cf_logfile = NULL;
+const char *cf_syslog_ident = NULL;
+
+static FILE *log_file = NULL;
+static bool syslog_started = false;
+
+struct LevelInfo {
+ const char *tag;
+ int syslog_prio;
+};
+
+static const struct LevelInfo log_level_list[] = {
+ { "FATAL", LOG_CRIT },
+ { "ERROR", LOG_ERR },
+ { "WARNING", LOG_WARNING },
+ { "LOG", LOG_INFO },
+ { "DEBUG", LOG_DEBUG },
+ { "NOISE", LOG_DEBUG },
+};
+
+void reset_logging(void)
+{
+ if (log_file) {
+ fclose(log_file);
+ log_file = NULL;
+ }
+ if (syslog_started) {
+ closelog();
+ syslog_started = 0;
+ }
+}
+
+
+static void start_syslog(void)
+{
+ openlog(cf_syslog_ident, LOG_PID, LOG_DAEMON);
+ syslog_started = 1;
+}
+
+static void open_logfile(void)
+{
+ FILE *f = fopen(cf_logfile, "a");
+ if (f)
+ log_file = f;
+ setvbuf(f, NULL, _IONBF, 0);
+}
+
+
+void log_generic(enum LogLevel level, const char *fmt, ...)
+{
+ char buf[2048];
+ char timebuf[64];
+ const struct LevelInfo *lev = &log_level_list[level];
+ unsigned pid = getpid();
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ format_time_ms(NULL, timebuf, sizeof(timebuf));
+
+ if (!cf_quiet)
+ fprintf(stderr, "%s %u %s %s\n", timebuf, pid, lev->tag, buf);
+ if (cf_logfile) {
+ if (!log_file) {
+ open_logfile();
+ if (!log_file)
+ goto no_logfile;
+ }
+ fprintf(log_file, "%s %u %s %s\n", timebuf, pid, lev->tag, buf);
+ }
+no_logfile:
+
+ if (cf_syslog_ident) {
+ if (!syslog_started)
+ start_syslog();
+ syslog(lev->syslog_prio, "%s", buf);
+ }
+}
+
+
+void log_fatal(const char *file, int line, const char *func, bool show_perror, const char *fmt, ...)
+{
+ char buf[2048];
+ const char *estr = NULL;
+ int old_errno = 0;
+ va_list ap;
+
+ if (show_perror) {
+ old_errno = errno;
+ estr = strerror(errno);
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (show_perror) {
+ log_generic(LG_FATAL, "@%s:%d in function %s(): %s: %s [%d]",
+ file, line, func, buf, estr, old_errno);
+ } else {
+ log_generic(LG_FATAL, "@%s:%d in function %s(): %s",
+ file, line, func, buf);
+ }
+}
+
--- /dev/null
+/*
+ * Logging for unix service.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_LOGGING_H_
+#define _USUAL_LOGGING_H_
+
+#include <usual/base.h>
+
+#include <stdlib.h>
+
+/*
+ * 0 - show only info level msgs (default)
+ * 1 - show debug msgs (log_debug)
+ * 2 - show noise msgs (log_noise)
+ */
+extern int cf_verbose;
+
+/* If logging to stdout is allowed (default: 1) */
+/* daemon.c turns this off if goes to background */
+extern int cf_quiet;
+
+/* logfile location, default NULL */
+extern const char *cf_logfile;
+
+/* ident for syslog, if NULL syslog is disabled (default) */
+extern const char *cf_syslog_ident;
+
+/*
+ * Quick API overview:
+ *
+ * void log_error(const char *s, ...);
+ * void log_warning(const char *s, ...);
+ * void log_info(const char *s, ...);
+ * void log_debug(const char *s, ...);
+ * void log_noise(const char *s, ...);
+ * void fatal(const char *s, ...);
+ * void fatal_perror(const char *s, ...);
+ * void die(const char *s, ...);
+ *
+ * close_logfile(void);
+ */
+
+/*
+ * Internal logging function.
+ */
+enum LogLevel {
+ LG_FATAL = 0,
+ LG_ERROR = 1,
+ LG_WARNING = 2,
+ LG_INFO = 3,
+ LG_DEBUG = 4,
+ LG_NOISE = 5,
+};
+void log_generic(enum LogLevel level, const char *s, ...) _PRINTF(2, 3);
+
+/* macros for plain logging */
+#define log_error(args...) log_generic(LG_ERROR, ## args)
+#define log_warning(args...) log_generic(LG_WARNING, ## args)
+#define log_info(args...) log_generic(LG_INFO, ## args)
+
+/* move printf arg setup out-of-line for debug macros */
+#define log_debug(args...) do { \
+ if (unlikely(cf_verbose > 0)) \
+ log_generic(LG_DEBUG, ## args); \
+ } while (0)
+#define log_noise(args...) do { \
+ if (unlikely(cf_verbose > 1)) \
+ log_generic(LG_NOISE, ## args); \
+ } while (0)
+
+/* this is also defined in base.h for Assert() */
+void log_fatal(const char *file, int line, const char *func, bool show_perror, const char *s, ...) _PRINTF(5, 6);
+
+/* fatal loggers should also log location */
+#define fatal(args...) do { \
+ log_fatal(__FILE__, __LINE__, __func__, false, ## args); \
+ exit(1); } while (0)
+#define fatal_perror(args...) do { \
+ log_fatal(__FILE__, __LINE__, __func__, true, ## args); \
+ exit(1); } while (0)
+
+/* less verbose fatal() */
+#define die(msg, args...) do { \
+ log_generic(LG_FATAL, msg, ## args); \
+ exit(1); } while (0)
+
+void reset_logging(void);
+
+#endif
+
--- /dev/null
+/*
+ * The contents of this file are public domain.
+ *
+ * Based on: lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ */
+
+/*
+ * Compact version of Bob Jenkins' lookup3.c hash.
+ */
+
+#include <usual/lookup3.h>
+
+#include <string.h>
+
+#define rot(x, k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+#define mix(a, b, c) do { \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+} while (0)
+
+#define final(a, b, c) do { \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c, 4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+} while (0)
+
+/* variable length copy of ~6 bytes, avoid call to libc */
+static inline void simple_memcpy(void *dst_, const void *src_, size_t len)
+{
+ const uint8_t *src = src_;
+ uint8_t *dst = dst_;
+ while (len--)
+ *dst++ = *src++;
+}
+
+uint64_t hash_lookup3(const void *data, size_t len)
+{
+ uint32_t a, b, c;
+ uint32_t buf[3];
+ const uint8_t *p = data;
+
+ a = b = c = 0xdeadbeef + len;
+ if (len == 0)
+ goto done;
+
+ while (len > 12) {
+ memcpy(buf, p, 12);
+ a += buf[0];
+ b += buf[1];
+ c += buf[2];
+ mix(a, b, c);
+ p += 12;
+ len -= 12;
+ }
+
+ buf[0] = buf[1] = buf[2] = 0;
+ simple_memcpy(buf, p, len);
+ a += buf[0];
+ b += buf[1];
+ c += buf[2];
+ final(a, b, c);
+done:
+ return ((uint64_t)b << 32) | c;
+}
+
--- /dev/null
+#ifndef _USUAL_LOOKUP3_H_
+#define _USUAL_LOOKUP3_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+uint64_t hash_lookup3(const void *data, size_t len);
+
+#endif
--- /dev/null
+/*
+ * MD5 implementation based on RFC1321.
+ *
+ * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <usual/md5.h>
+
+#include <string.h>
+#include <endian.h>
+
+/* check endianess */
+#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
+#error "md5.c need sane endian.h"
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define WORDS_BIGENDIAN
+#endif
+
+/*
+ * Support functions.
+ */
+
+#define bufpos(ctx) ((ctx)->nbytes & (MD5_BLOCK_LENGTH - 1))
+
+static inline uint32_t rol(uint32_t v, int s)
+{
+ return (v << s) | (v >> (32 - s));
+}
+
+static inline void swap_words(uint32_t *w, int n)
+{
+#ifdef WORDS_BIGENDIAN
+ for (; n > 0; w++, n--) {
+ uint32_t v = rol(*w, 16);
+ *w = ((v >> 8) & 0x00FF00FF) | ((v << 8) & 0xFF00FF00);
+ }
+#endif
+}
+
+static inline void put_word(uint8_t *dst, uint32_t val)
+{
+#ifdef WORDS_BIGENDIAN
+ dst[0] = val;
+ dst[1] = val >> 8;
+ dst[2] = val >> 16;
+ dst[3] = val >> 24;
+#else
+ memcpy(dst, &val, 4);
+#endif
+}
+
+/*
+ * MD5 core.
+ */
+
+#define F(X,Y,Z) ((X & Y) | ((~X) & Z))
+#define G(X,Y,Z) ((X & Z) | (Y & (~Z)))
+#define H(X,Y,Z) (X ^ Y ^ Z)
+#define I(X,Y,Z) (Y ^ (X | (~Z)))
+
+#define OP(fn, a, b, c, d, k, s, T_i) \
+ a = b + rol(a + fn(b, c, d) + X[k] + T_i, s)
+
+static void md5_mix(struct md5_ctx *ctx, const uint32_t *X)
+{
+ uint32_t a, b, c, d;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ /* Round 1. */
+ OP(F, a, b, c, d, 0, 7, 0xd76aa478);
+ OP(F, d, a, b, c, 1, 12, 0xe8c7b756);
+ OP(F, c, d, a, b, 2, 17, 0x242070db);
+ OP(F, b, c, d, a, 3, 22, 0xc1bdceee);
+ OP(F, a, b, c, d, 4, 7, 0xf57c0faf);
+ OP(F, d, a, b, c, 5, 12, 0x4787c62a);
+ OP(F, c, d, a, b, 6, 17, 0xa8304613);
+ OP(F, b, c, d, a, 7, 22, 0xfd469501);
+ OP(F, a, b, c, d, 8, 7, 0x698098d8);
+ OP(F, d, a, b, c, 9, 12, 0x8b44f7af);
+ OP(F, c, d, a, b, 10, 17, 0xffff5bb1);
+ OP(F, b, c, d, a, 11, 22, 0x895cd7be);
+ OP(F, a, b, c, d, 12, 7, 0x6b901122);
+ OP(F, d, a, b, c, 13, 12, 0xfd987193);
+ OP(F, c, d, a, b, 14, 17, 0xa679438e);
+ OP(F, b, c, d, a, 15, 22, 0x49b40821);
+
+ /* Round 2. */
+ OP(G, a, b, c, d, 1, 5, 0xf61e2562);
+ OP(G, d, a, b, c, 6, 9, 0xc040b340);
+ OP(G, c, d, a, b, 11, 14, 0x265e5a51);
+ OP(G, b, c, d, a, 0, 20, 0xe9b6c7aa);
+ OP(G, a, b, c, d, 5, 5, 0xd62f105d);
+ OP(G, d, a, b, c, 10, 9, 0x02441453);
+ OP(G, c, d, a, b, 15, 14, 0xd8a1e681);
+ OP(G, b, c, d, a, 4, 20, 0xe7d3fbc8);
+ OP(G, a, b, c, d, 9, 5, 0x21e1cde6);
+ OP(G, d, a, b, c, 14, 9, 0xc33707d6);
+ OP(G, c, d, a, b, 3, 14, 0xf4d50d87);
+ OP(G, b, c, d, a, 8, 20, 0x455a14ed);
+ OP(G, a, b, c, d, 13, 5, 0xa9e3e905);
+ OP(G, d, a, b, c, 2, 9, 0xfcefa3f8);
+ OP(G, c, d, a, b, 7, 14, 0x676f02d9);
+ OP(G, b, c, d, a, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP(H, a, b, c, d, 5, 4, 0xfffa3942);
+ OP(H, d, a, b, c, 8, 11, 0x8771f681);
+ OP(H, c, d, a, b, 11, 16, 0x6d9d6122);
+ OP(H, b, c, d, a, 14, 23, 0xfde5380c);
+ OP(H, a, b, c, d, 1, 4, 0xa4beea44);
+ OP(H, d, a, b, c, 4, 11, 0x4bdecfa9);
+ OP(H, c, d, a, b, 7, 16, 0xf6bb4b60);
+ OP(H, b, c, d, a, 10, 23, 0xbebfbc70);
+ OP(H, a, b, c, d, 13, 4, 0x289b7ec6);
+ OP(H, d, a, b, c, 0, 11, 0xeaa127fa);
+ OP(H, c, d, a, b, 3, 16, 0xd4ef3085);
+ OP(H, b, c, d, a, 6, 23, 0x04881d05);
+ OP(H, a, b, c, d, 9, 4, 0xd9d4d039);
+ OP(H, d, a, b, c, 12, 11, 0xe6db99e5);
+ OP(H, c, d, a, b, 15, 16, 0x1fa27cf8);
+ OP(H, b, c, d, a, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP(I, a, b, c, d, 0, 6, 0xf4292244);
+ OP(I, d, a, b, c, 7, 10, 0x432aff97);
+ OP(I, c, d, a, b, 14, 15, 0xab9423a7);
+ OP(I, b, c, d, a, 5, 21, 0xfc93a039);
+ OP(I, a, b, c, d, 12, 6, 0x655b59c3);
+ OP(I, d, a, b, c, 3, 10, 0x8f0ccc92);
+ OP(I, c, d, a, b, 10, 15, 0xffeff47d);
+ OP(I, b, c, d, a, 1, 21, 0x85845dd1);
+ OP(I, a, b, c, d, 8, 6, 0x6fa87e4f);
+ OP(I, d, a, b, c, 15, 10, 0xfe2ce6e0);
+ OP(I, c, d, a, b, 6, 15, 0xa3014314);
+ OP(I, b, c, d, a, 13, 21, 0x4e0811a1);
+ OP(I, a, b, c, d, 4, 6, 0xf7537e82);
+ OP(I, d, a, b, c, 11, 10, 0xbd3af235);
+ OP(I, c, d, a, b, 2, 15, 0x2ad7d2bb);
+ OP(I, b, c, d, a, 9, 21, 0xeb86d391);
+
+ ctx->a += a;
+ ctx->b += b;
+ ctx->c += c;
+ ctx->d += d;
+}
+
+/*
+ * Public API.
+ */
+
+void md5_reset(struct md5_ctx *ctx)
+{
+ ctx->nbytes = 0;
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+}
+
+void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len)
+{
+ unsigned int n;
+ const uint8_t *ptr = data;
+ uint8_t *buf = (uint8_t *)ctx->buf;
+
+ while (len > 0) {
+ n = MD5_BLOCK_LENGTH - bufpos(ctx);
+ if (n > len)
+ n = len;
+ memcpy(buf + bufpos(ctx), ptr, n);
+ ptr += n;
+ len -= n;
+ ctx->nbytes += n;
+ if (bufpos(ctx) == 0) {
+ swap_words(ctx->buf, 16);
+ md5_mix(ctx, ctx->buf);
+ }
+ }
+}
+
+void md5_final(uint8_t *dst, struct md5_ctx *ctx)
+{
+ static const uint8_t padding[MD5_BLOCK_LENGTH] = { 0x80 };
+ uint64_t final_len = ctx->nbytes * 8;
+ int pad_len, pos = bufpos(ctx);
+
+ /* add padding */
+ pad_len = MD5_BLOCK_LENGTH - 8 - pos;
+ if (pad_len <= 0)
+ pad_len += MD5_BLOCK_LENGTH;
+ md5_update(ctx, padding, pad_len);
+
+ /* add length directly */
+ swap_words(ctx->buf, 14);
+ ctx->buf[14] = final_len;
+ ctx->buf[15] = final_len >> 32;
+
+ /* final result */
+ md5_mix(ctx, ctx->buf);
+ put_word(dst, ctx->a);
+ put_word(dst + 4, ctx->b);
+ put_word(dst + 8, ctx->c);
+ put_word(dst + 12, ctx->d);
+}
+
--- /dev/null
+/*
+ * MD5 implementation based on RFC1321.
+ *
+ * Copyright (c) 2008 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_MD5_H_
+#define _USUAL_MD5_H_
+
+#include <stdint.h>
+
+#define MD5_BLOCK_LENGTH 64
+#define MD5_DIGEST_LENGTH 16
+
+struct md5_ctx {
+ uint64_t nbytes;
+ uint32_t a, b, c, d;
+ uint32_t buf[16];
+};
+
+void md5_reset(struct md5_ctx *ctx);
+void md5_update(struct md5_ctx *ctx, const void *data, unsigned int len);
+void md5_final(uint8_t *dst, struct md5_ctx *ctx);
+
+#ifdef MD5_COMPAT
+typedef struct md5_ctx MD5_CTX;
+#define MD5_Init(c) md5_reset(c)
+#define MD5_Update(c, d, l) md5_update(c, d, l)
+#define MD5_Final(d, c) md5_final(d, c)
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * libusual - Utility library for C
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Wrappers around regular I/O functions (send/recv/read/write)
+ * that survive EINTR and also can log problems.
+ */
+
+#include <usual/safeio.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+#include <usual/logging.h>
+
+int safe_read(int fd, void *buf, int len)
+{
+ int res;
+loop:
+ res = read(fd, buf, len);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ return res;
+}
+
+int safe_write(int fd, const void *buf, int len)
+{
+ int res;
+loop:
+ res = write(fd, buf, len);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ return res;
+}
+
+int safe_recv(int fd, void *buf, int len, int flags)
+{
+ int res;
+loop:
+ res = recv(fd, buf, len, flags);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ if (res < 0)
+ log_noise("safe_recv(%d, %d) = %s", fd, len, strerror(errno));
+ else if (cf_verbose > 2)
+ log_noise("safe_recv(%d, %d) = %d", fd, len, res);
+ return res;
+}
+
+int safe_send(int fd, const void *buf, int len, int flags)
+{
+ int res;
+loop:
+ res = send(fd, buf, len, flags);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ if (res < 0)
+ log_noise("safe_send(%d, %d) = %s", fd, len, strerror(errno));
+ else if (cf_verbose > 2)
+ log_noise("safe_send(%d, %d) = %d", fd, len, res);
+ return res;
+}
+
+int safe_close(int fd)
+{
+ int res;
+loop:
+ /* by manpage, the close() could be interruptable
+ although it seems that at least in linux it cannot happen */
+#ifndef WIN32
+ res = close(fd);
+#else
+ /* Pending(this is necessary to wait for FIN of a client.) */
+ log_debug("closesocket(%d)",fd);
+ res = closesocket(fd);
+#endif
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ return res;
+}
+
+int safe_recvmsg(int fd, struct msghdr *msg, int flags)
+{
+ int res;
+loop:
+ res = recvmsg(fd, msg, flags);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ if (res < 0)
+ log_warning("safe_recvmsg(%d, msg, %d) = %s", fd, flags, strerror(errno));
+ else if (cf_verbose > 2)
+ log_noise("safe_recvmsg(%d, msg, %d) = %d", fd, flags, res);
+ return res;
+}
+
+int safe_sendmsg(int fd, const struct msghdr *msg, int flags)
+{
+ int res;
+ int msgerr_count = 0;
+loop:
+ res = sendmsg(fd, msg, flags);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+
+ if (res < 0) {
+ log_warning("safe_sendmsg(%d, msg[%d,%d], %d) = %s", fd,
+ (int)msg->msg_iov[0].iov_len,
+ (int)msg->msg_controllen,
+ flags, strerror(errno));
+
+ /* with ancillary data on blocking socket OSX returns
+ * EMSGSIZE instead of blocking. try to solve it by waiting */
+ if (errno == EMSGSIZE && msgerr_count < 20) {
+ struct timeval tv = {1, 0};
+ log_warning("trying to sleep a bit");
+ select(0, NULL, NULL, NULL, &tv);
+ msgerr_count++;
+ goto loop;
+ }
+ } else if (cf_verbose > 2)
+ log_noise("safe_sendmsg(%d, msg, %d) = %d", fd, flags, res);
+ return res;
+}
+
+static const char *sa2str(const struct sockaddr *sa)
+{
+ static char buf[256];
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *in = (struct sockaddr_in *)sa;
+ snprintf(buf, sizeof(buf), "%s:%d", inet_ntoa(in->sin_addr), ntohs(in->sin_port));
+ } if (sa->sa_family == AF_UNIX) {
+ struct sockaddr_un *un = (struct sockaddr_un *)sa;
+ snprintf(buf, sizeof(buf), "unix:%s", un->sun_path);
+ } else {
+ snprintf(buf, sizeof(buf), "sa2str: unknown proto");
+ }
+ return buf;
+}
+
+int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len)
+{
+ int res;
+loop:
+ res = connect(fd, sa, sa_len);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ if (res < 0 && (errno != EINPROGRESS || cf_verbose > 2))
+ log_noise("connect(%d, %s) = %s", fd, sa2str(sa), strerror(errno));
+ else if (cf_verbose > 2)
+ log_noise("connect(%d, %s) = %d", fd, sa2str(sa), res);
+ return res;
+}
+
+int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len_p)
+{
+ int res;
+loop:
+ res = accept(fd, sa, sa_len_p);
+ if (res < 0 && errno == EINTR)
+ goto loop;
+ if (res < 0)
+ log_noise("safe_accept(%d) = %s", fd, strerror(errno));
+ else if (cf_verbose > 2)
+ log_noise("safe_accept(%d) = %d (%s)", fd, res, sa2str(sa));
+ return res;
+}
+
--- /dev/null
+/*
+ * libusual - Utility library for C
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_SAFEIO_H_
+#define _USUAL_SAFEIO_H_
+
+#include <usual/base.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int safe_read(int fd, void *buf, int len) _MUSTCHECK;
+int safe_write(int fd, const void *buf, int len) _MUSTCHECK;
+int safe_recv(int fd, void *buf, int len, int flags) _MUSTCHECK;
+int safe_send(int fd, const void *buf, int len, int flags) _MUSTCHECK;
+int safe_close(int fd);
+int safe_recvmsg(int fd, struct msghdr *msg, int flags) _MUSTCHECK;
+int safe_sendmsg(int fd, const struct msghdr *msg, int flags) _MUSTCHECK;
+int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len) _MUSTCHECK;
+int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len) _MUSTCHECK;
+
+#endif
--- /dev/null
+/*
+ * Primitive slab allocator.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Basic behaviour:
+ * - On each alloc initializer is called.
+ * - if init func is not given, memset() is done
+ *
+ * ATM custom 'align' larger than malloc() alignment does not work.
+ */
+
+#include <usual/slab.h>
+
+#include <sys/param.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <usual/statlist.h>
+
+/*
+ * Store for pre-initialized objects of one type.
+ */
+struct Slab {
+ struct List head;
+ struct StatList freelist;
+ struct StatList fraglist;
+ char name[32];
+ unsigned final_size;
+ unsigned total_count;
+ slab_init_fn init_func;
+};
+
+/*
+ * Header for each slab.
+ */
+struct SlabFrag {
+ struct List head;
+};
+
+/* keep track of all active slabs */
+static STATLIST(slab_list);
+
+/* cache for slab headers */
+static struct Slab *slab_headers = NULL;
+
+/* fill struct contents */
+static void init_slab(struct Slab *slab, const char *name, unsigned obj_size,
+ unsigned align, slab_init_fn init_func)
+{
+ unsigned slen = strlen(name);
+
+ list_init(&slab->head);
+ statlist_init(&slab->freelist, name);
+ statlist_init(&slab->fraglist, name);
+ slab->total_count = 0;
+ slab->init_func = init_func;
+ statlist_append(&slab_list, &slab->head);
+
+ if (slen >= sizeof(slab->name))
+ slen = sizeof(slab->name) - 1;
+ memcpy(slab->name, name, slen);
+ slab->name[slen] = 0;
+
+ if (align == 0)
+ slab->final_size = ALIGN(obj_size);
+ else
+ slab->final_size = CUSTOM_ALIGN(obj_size, align);
+}
+
+/* make new slab */
+struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align,
+ slab_init_fn init_func)
+{
+ struct Slab *slab;
+
+ /* header cache */
+ if (!slab_headers) {
+ slab_headers = malloc(sizeof(struct Slab));
+ if (!slab_headers)
+ return NULL;
+ init_slab(slab_headers, "slab_header",
+ sizeof(struct Slab), 0, NULL);
+ }
+
+ /* new slab object */
+ slab = slab_alloc(slab_headers);
+ if (slab)
+ init_slab(slab, name, obj_size, align, init_func);
+ return slab;
+}
+
+/* free all storage associated by slab */
+void slab_destroy(struct Slab *slab)
+{
+ struct List *item, *tmp;
+ struct SlabFrag *frag;
+
+ statlist_for_each_safe(item, &slab->fraglist, tmp) {
+ frag = container_of(item, struct SlabFrag, head);
+ free(frag);
+ }
+ statlist_remove(&slab_list, &slab->head);
+ memset(slab, 0, sizeof(*slab));
+ slab_free(slab_headers, slab);
+}
+
+/* add new block of objects to slab */
+static void grow(struct Slab *slab)
+{
+ unsigned count, i, size;
+ char *area;
+ struct SlabFrag *frag;
+
+ /* calc new slab size */
+ count = slab->total_count;
+ if (count < 50)
+ count = 16 * 1024 / slab->final_size;
+ if (count < 50)
+ count = 50;
+ size = count * slab->final_size;
+
+ /* allocate & init */
+ frag = malloc(size + sizeof(struct SlabFrag));
+ if (!frag)
+ return;
+ list_init(&frag->head);
+ area = (char *)frag + sizeof(struct SlabFrag);
+ memset(area, 0, size);
+
+ /* init objects */
+ for (i = 0; i < count; i++) {
+ void *obj = area + i * slab->final_size;
+ struct List *head = (struct List *)obj;
+ list_init(head);
+ statlist_append(&slab->freelist, head);
+ }
+
+ /* register to slab */
+ slab->total_count += count;
+ statlist_append(&slab->fraglist, &frag->head);
+}
+
+/* get free object from slab */
+void *slab_alloc(struct Slab *slab)
+{
+ struct List *item = statlist_pop(&slab->freelist);
+ if (!item) {
+ grow(slab);
+ item = statlist_pop(&slab->freelist);
+ }
+ if (item) {
+ if (slab->init_func)
+ slab->init_func(item);
+ else
+ memset(item, 0, slab->final_size);
+ }
+ return item;
+}
+
+/* put object back to slab */
+void slab_free(struct Slab *slab, void *obj)
+{
+ struct List *item = obj;
+ list_init(item);
+ statlist_prepend(&slab->freelist, item);
+}
+
+/* total number of objects allocated from slab */
+int slab_total_count(const struct Slab *slab)
+{
+ return slab->total_count;
+}
+
+/* free objects in slab */
+int slab_free_count(const struct Slab *slab)
+{
+ return statlist_count(&slab->freelist);
+}
+
+/* number of objects in use */
+int slab_active_count(const struct Slab *slab)
+{
+ return slab_total_count(slab) - slab_free_count(slab);
+}
+
+static void run_slab_stats(struct Slab *slab, slab_stat_fn cb_func, void *cb_arg)
+{
+ unsigned free = statlist_count(&slab->freelist);
+ cb_func(cb_arg, slab->name, slab->final_size, free, slab->total_count);
+}
+
+/* call a function for all active slabs */
+void slab_stats(slab_stat_fn cb_func, void *cb_arg)
+{
+ struct Slab *slab;
+ struct List *item;
+
+ statlist_for_each(item, &slab_list) {
+ slab = container_of(item, struct Slab, head);
+ run_slab_stats(slab, cb_func, cb_arg);
+ }
+}
+
--- /dev/null
+/*
+ * Primitive slab allocator.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_SLAB_H_
+#define _USUAL_SLAB_H_
+
+#include <usual/base.h>
+
+struct Slab;
+
+typedef void (*slab_init_fn)(void *obj);
+
+struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align,
+ slab_init_fn init_func);
+void slab_destroy(struct Slab *slab);
+
+void * slab_alloc(struct Slab *slab) _MALLOC _MUSTCHECK;
+void slab_free(struct Slab *slab, void *obj);
+
+int slab_total_count(const struct Slab *slab);
+int slab_free_count(const struct Slab *slab);
+int slab_active_count(const struct Slab *slab);
+
+typedef void (*slab_stat_fn)(void *arg, const char *slab_name,
+ unsigned size, unsigned free,
+ unsigned total);
+void slab_stats(slab_stat_fn cb_func, void *cb_arg);
+
+#endif
+
--- /dev/null
+/*
+ * socket helpers.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <usual/socket.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <usual/logging.h>
+
+/* toggle non-blocking flag */
+bool socket_set_nonblocking(int fd, bool non_block)
+{
+ int flags;
+
+ /* get old flags */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0)
+ return false;
+
+ /* flip O_NONBLOCK */
+ if (non_block)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ /* set new flags */
+ if (fcntl(fd, F_SETFL, flags) < 0)
+ return false;
+ return true;
+}
+
+/* initial socket setup */
+bool socket_setup(int sock, bool non_block)
+{
+ int res;
+
+ /* close fd on exec */
+ res = fcntl(sock, F_SETFD, FD_CLOEXEC);
+ if (res < 0)
+ return false;
+
+#ifdef SO_NOSIGPIPE
+ /* disallow SIGPIPE, if possible */
+ val = 1;
+ res = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val));
+ if (res < 0)
+ return false;
+#endif
+
+ /* when no data available, return EAGAIN instead blocking */
+ if (!socket_set_nonblocking(sock, non_block))
+ return false;
+
+ return true;
+}
+
--- /dev/null
+/*
+ * socket helpers.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_SOCKET_H_
+#define _USUAL_SOCKET_H_
+
+#include <usual/base.h>
+#include <sys/socket.h>
+
+bool socket_setup(int sock, bool non_block);
+bool socket_set_nonblocking(int sock, bool non_block);
+
+#endif
+
--- /dev/null
+/*
+ * Wrapper for list.h that keeps track of number of items.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_STATLIST_H_
+#define _USUAL_STATLIST_H_
+
+#include <usual/list.h>
+
+struct StatList {
+ struct List head;
+ int cur_count;
+#ifdef LIST_DEBUG
+ const char *name;
+#endif
+};
+
+#ifdef LIST_DEBUG
+#define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0, #var }
+#else
+#define STATLIST(var) struct StatList var = { {&var.head, &var.head}, 0 }
+#endif
+
+static inline void statlist_prepend(struct StatList *list, struct List *item)
+{
+ list_prepend(&list->head, item);
+ list->cur_count++;
+}
+
+static inline void statlist_append(struct StatList *list, struct List *item)
+{
+ list_append(&list->head, item);
+ list->cur_count++;
+}
+
+static inline void statlist_remove(struct StatList *list, struct List *item)
+{
+ list_del(item);
+ list->cur_count--;
+
+ //Assert(list->cur_count >= 0);
+}
+
+static inline void statlist_init(struct StatList *list, const char *name)
+{
+ list_init(&list->head);
+ list->cur_count = 0;
+#ifdef LIST_DEBUG
+ list->name = name;
+#endif
+}
+
+static inline int statlist_count(const struct StatList *list)
+{
+ //Assert(list->cur_count > 0 || list_empty(&list->head));
+ return list->cur_count;
+}
+
+static inline struct List *statlist_pop(struct StatList *list)
+{
+ struct List *item = list_pop(&list->head);
+
+ if (item)
+ list->cur_count--;
+
+ //Assert(list->cur_count >= 0);
+
+ return item;
+}
+
+static inline struct List *statlist_first(const struct StatList *list)
+{
+ return list_first(&list->head);
+}
+
+static inline bool statlist_empty(const struct StatList *list)
+{
+ return list_empty(&list->head);
+}
+
+#define statlist_for_each(item, list) list_for_each(item, &((list)->head))
+#define statlist_for_each_safe(item, list, tmp) list_for_each_safe(item, &((list)->head), tmp)
+
+#endif /* __LIST_H_ */
+
--- /dev/null
+/*
+ * Basic C strings.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_STRING_H_
+#define _USUAL_STRING_H_
+
+#include <string.h>
+
+/*
+ * Minimal spec-conforming implementations of strlcpy(), strlcat().
+ */
+
+#ifndef HAVE_STRLCPY
+#define strlcpy(a,b,c) usual_strlcpy(a,b,c)
+
+static inline size_t strlcpy(char *dst, const char *src, size_t n)
+{
+ size_t len = strlen(src);
+ if (len < n) {
+ memcpy(dst, src, len + 1);
+ } else if (n > 0) {
+ memcpy(dst, src, n - 1);
+ dst[n - 1] = 0;
+ }
+ return len;
+}
+
+#endif /* !HAVE_STRLCPY */
+
+#ifndef HAVE_STRLCAT
+#define strlcat(a,b,c) usual_strlcat(a,b,c)
+
+static inline size_t strlcat(char *dst, const char *src, size_t n)
+{
+ size_t pos = 0;
+ while (pos < n && dst[pos])
+ pos++;
+ return pos + strlcpy(dst + pos, src, n - pos);
+}
+
+#endif /* !HAVE_STRLCAT */
+
+#endif
+
--- /dev/null
+/*
+ * Common time functions.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <usual/time.h>
+
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+
+char *format_time_ms(const struct timeval *tv, char *dst, unsigned dstlen)
+{
+ struct tm *tm, tmbuf;
+ struct timeval tvbuf;
+ time_t sec;
+
+ if (tv == NULL) {
+ gettimeofday(&tvbuf, NULL);
+ tv = &tvbuf;
+ }
+
+ sec = tv->tv_sec;
+ tm = localtime_r(&sec, &tmbuf);
+ snprintf(dst, dstlen, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ (int)(tv->tv_usec / 1000));
+ return dst;
+}
+
+/* read current time */
+usec_t get_time_usec(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (usec_t)tv.tv_sec * USEC + tv.tv_usec;
+}
+
+static usec_t _time_cache;
+
+/* read cached time */
+usec_t get_cached_time(void)
+{
+ if (!_time_cache)
+ _time_cache = get_time_usec();
+ return _time_cache;
+}
+
+/* forget cached time, let next read fill it */
+void reset_time_cache(void)
+{
+ _time_cache = 0;
+}
+
--- /dev/null
+/*
+ * Common time functions.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 _USUAL_TIME_H_
+#define _USUAL_TIME_H_
+
+#include <usual/base.h>
+
+typedef uint64_t usec_t;
+#define USEC 1000000ULL
+
+char *format_time_ms(const struct timeval *tv, char *dst, unsigned dstlen);
+
+usec_t get_time_usec(void);
+
+usec_t get_cached_time(void);
+void reset_time_cache(void);
+
+#endif