Yet another utility library
authorMarko Kreen <markokr@gmail.com>
Mon, 13 Apr 2009 09:59:18 +0000 (12:59 +0300)
committerMarko Kreen <markokr@gmail.com>
Mon, 13 Apr 2009 09:59:18 +0000 (12:59 +0300)
36 files changed:
COPYRIGHT [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
Setup.mk [new file with mode: 0644]
test/compile.c [new file with mode: 0644]
usual/aatree.c [new file with mode: 0644]
usual/aatree.h [new file with mode: 0644]
usual/alloc.h [new file with mode: 0644]
usual/base.h [new file with mode: 0644]
usual/cbtree.c [new file with mode: 0644]
usual/cbtree.h [new file with mode: 0644]
usual/cfparser.c [new file with mode: 0644]
usual/cfparser.h [new file with mode: 0644]
usual/daemon.c [new file with mode: 0644]
usual/daemon.h [new file with mode: 0644]
usual/event.c [new file with mode: 0644]
usual/event.h [new file with mode: 0644]
usual/fileutil.c [new file with mode: 0644]
usual/fileutil.h [new file with mode: 0644]
usual/list.h [new file with mode: 0644]
usual/logging.c [new file with mode: 0644]
usual/logging.h [new file with mode: 0644]
usual/lookup3.c [new file with mode: 0644]
usual/lookup3.h [new file with mode: 0644]
usual/md5.c [new file with mode: 0644]
usual/md5.h [new file with mode: 0644]
usual/safeio.c [new file with mode: 0644]
usual/safeio.h [new file with mode: 0644]
usual/slab.c [new file with mode: 0644]
usual/slab.h [new file with mode: 0644]
usual/socket.c [new file with mode: 0644]
usual/socket.h [new file with mode: 0644]
usual/statlist.h [new file with mode: 0644]
usual/string.h [new file with mode: 0644]
usual/time.c [new file with mode: 0644]
usual/time.h [new file with mode: 0644]

diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..6cbf1ba
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b6e8d76
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,54 @@
+# 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*
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..427fab6
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+= libusual =
+
+Collection of various code useful for writing server code.
+
diff --git a/Setup.mk b/Setup.mk
new file mode 100644 (file)
index 0000000..860ee9f
--- /dev/null
+++ b/Setup.mk
@@ -0,0 +1,20 @@
+
+#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)
+
diff --git a/test/compile.c b/test/compile.c
new file mode 100644 (file)
index 0000000..36430da
--- /dev/null
@@ -0,0 +1,40 @@
+#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;
+}
+
diff --git a/usual/aatree.c b/usual/aatree.c
new file mode 100644 (file)
index 0000000..b44361a
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * 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;
+}
+
diff --git a/usual/aatree.h b/usual/aatree.h
new file mode 100644 (file)
index 0000000..8ff779a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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
diff --git a/usual/alloc.h b/usual/alloc.h
new file mode 100644 (file)
index 0000000..781dd69
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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
+
diff --git a/usual/base.h b/usual/base.h
new file mode 100644 (file)
index 0000000..90bcee5
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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
+
diff --git a/usual/cbtree.c b/usual/cbtree.c
new file mode 100644 (file)
index 0000000..f94d407
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * 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);
+}
+
diff --git a/usual/cbtree.h b/usual/cbtree.h
new file mode 100644 (file)
index 0000000..cf62e48
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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
+
diff --git a/usual/cfparser.c b/usual/cfparser.c
new file mode 100644 (file)
index 0000000..919a461
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * 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;
+}
+
+
diff --git a/usual/cfparser.h b/usual/cfparser.h
new file mode 100644 (file)
index 0000000..e383aa1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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
diff --git a/usual/daemon.c b/usual/daemon.c
new file mode 100644 (file)
index 0000000..2f3b0f0
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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);
+}
+
diff --git a/usual/daemon.h b/usual/daemon.h
new file mode 100644 (file)
index 0000000..a79d20f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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
+
diff --git a/usual/event.c b/usual/event.c
new file mode 100644 (file)
index 0000000..9b51a7d
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ * 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;
+}
+
diff --git a/usual/event.h b/usual/event.h
new file mode 100644 (file)
index 0000000..d18f432
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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
+
diff --git a/usual/fileutil.c b/usual/fileutil.c
new file mode 100644 (file)
index 0000000..40a15eb
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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;
+}
+
diff --git a/usual/fileutil.h b/usual/fileutil.h
new file mode 100644 (file)
index 0000000..753cc97
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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
+
diff --git a/usual/list.h b/usual/list.h
new file mode 100644 (file)
index 0000000..f190249
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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
+
diff --git a/usual/logging.c b/usual/logging.c
new file mode 100644 (file)
index 0000000..681bd59
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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);
+       }
+}
+
diff --git a/usual/logging.h b/usual/logging.h
new file mode 100644 (file)
index 0000000..027ca94
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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
+
diff --git a/usual/lookup3.c b/usual/lookup3.c
new file mode 100644 (file)
index 0000000..93ed254
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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;
+}
+
diff --git a/usual/lookup3.h b/usual/lookup3.h
new file mode 100644 (file)
index 0000000..5bf64f7
--- /dev/null
@@ -0,0 +1,9 @@
+#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
diff --git a/usual/md5.c b/usual/md5.c
new file mode 100644 (file)
index 0000000..737a7ec
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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);
+}
+
diff --git a/usual/md5.h b/usual/md5.h
new file mode 100644 (file)
index 0000000..759416a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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
+
diff --git a/usual/safeio.c b/usual/safeio.c
new file mode 100644 (file)
index 0000000..39e5c8d
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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;
+}
+
diff --git a/usual/safeio.h b/usual/safeio.h
new file mode 100644 (file)
index 0000000..2dbcb1b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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
diff --git a/usual/slab.c b/usual/slab.c
new file mode 100644 (file)
index 0000000..0d8ff32
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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);
+       }
+}
+
diff --git a/usual/slab.h b/usual/slab.h
new file mode 100644 (file)
index 0000000..dcf3f8e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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
+
diff --git a/usual/socket.c b/usual/socket.c
new file mode 100644 (file)
index 0000000..972ce5d
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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;
+}
+
diff --git a/usual/socket.h b/usual/socket.h
new file mode 100644 (file)
index 0000000..fd593a3
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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
+
diff --git a/usual/statlist.h b/usual/statlist.h
new file mode 100644 (file)
index 0000000..04a16c1
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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_ */
+
diff --git a/usual/string.h b/usual/string.h
new file mode 100644 (file)
index 0000000..771e478
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+
diff --git a/usual/time.c b/usual/time.c
new file mode 100644 (file)
index 0000000..3296353
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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;
+}
+
diff --git a/usual/time.h b/usual/time.h
new file mode 100644 (file)
index 0000000..f5634d5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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