From: Marko Kreen Date: Mon, 13 Apr 2009 09:59:18 +0000 (+0300) Subject: Yet another utility library X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=0d56938940272b5c50a281de0e9cb5d9cb91da9f;p=libusual.git Yet another utility library --- 0d56938940272b5c50a281de0e9cb5d9cb91da9f diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 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 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 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 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 index 0000000..36430da --- /dev/null +++ b/test/compile.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..b44361a --- /dev/null +++ b/usual/aatree.c @@ -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 /* 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 index 0000000..8ff779a --- /dev/null +++ b/usual/aatree.h @@ -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 index 0000000..781dd69 --- /dev/null +++ b/usual/alloc.h @@ -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 + +#include + +/* + * 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 index 0000000..90bcee5 --- /dev/null +++ b/usual/base.h @@ -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 +#include +#include +#include + +/* 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 +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 index 0000000..f94d407 --- /dev/null +++ b/usual/cbtree.c @@ -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 + +#include +#include +#include +#include +#include + +/* + * - 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 index 0000000..cf62e48 --- /dev/null +++ b/usual/cbtree.h @@ -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 + +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 index 0000000..919a461 --- /dev/null +++ b/usual/cfparser.c @@ -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 + +#include +#include +#include + +#include +#include +#include + +/* + * 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 index 0000000..e383aa1 --- /dev/null +++ b/usual/cfparser.h @@ -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 + +/* + * 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 index 0000000..2f3b0f0 --- /dev/null +++ b/usual/daemon.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * 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 index 0000000..a79d20f --- /dev/null +++ b/usual/daemon.h @@ -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 + +void daemonize(const char *pidfile, bool go_background); + +#endif + diff --git a/usual/event.c b/usual/event.c new file mode 100644 index 0000000..9b51a7d --- /dev/null +++ b/usual/event.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* 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 +#include +#include +#include +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 index 0000000..d18f432 --- /dev/null +++ b/usual/event.h @@ -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 + +#include +#include + +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 index 0000000..40a15eb --- /dev/null +++ b/usual/fileutil.c @@ -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 + +#include +#include +#include +#include +#include + +/* + * 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 index 0000000..753cc97 --- /dev/null +++ b/usual/fileutil.h @@ -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 + +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 index 0000000..f190249 --- /dev/null +++ b/usual/list.h @@ -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 + +/* 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 index 0000000..681bd59 --- /dev/null +++ b/usual/logging.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 index 0000000..027ca94 --- /dev/null +++ b/usual/logging.h @@ -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 + +#include + +/* + * 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 index 0000000..93ed254 --- /dev/null +++ b/usual/lookup3.c @@ -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 + +#include + +#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 index 0000000..5bf64f7 --- /dev/null +++ b/usual/lookup3.h @@ -0,0 +1,9 @@ +#ifndef _USUAL_LOOKUP3_H_ +#define _USUAL_LOOKUP3_H_ + +#include +#include + +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 index 0000000..737a7ec --- /dev/null +++ b/usual/md5.c @@ -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 + +#include +#include + +/* 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 index 0000000..759416a --- /dev/null +++ b/usual/md5.h @@ -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 + +#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 index 0000000..39e5c8d --- /dev/null +++ b/usual/safeio.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 index 0000000..2dbcb1b --- /dev/null +++ b/usual/safeio.h @@ -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 + +#include +#include + +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 index 0000000..0d8ff32 --- /dev/null +++ b/usual/slab.c @@ -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 + +#include +#include +#include + +#include + +/* + * 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 index 0000000..dcf3f8e --- /dev/null +++ b/usual/slab.h @@ -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 + +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 index 0000000..972ce5d --- /dev/null +++ b/usual/socket.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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 index 0000000..fd593a3 --- /dev/null +++ b/usual/socket.h @@ -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 +#include + +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 index 0000000..04a16c1 --- /dev/null +++ b/usual/statlist.h @@ -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 + +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 index 0000000..771e478 --- /dev/null +++ b/usual/string.h @@ -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 + +/* + * 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 index 0000000..3296353 --- /dev/null +++ b/usual/time.c @@ -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 + +#include +#include +#include + +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 index 0000000..f5634d5 --- /dev/null +++ b/usual/time.h @@ -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 + +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