From: <ag...@us...> - 2012-06-30 18:06:57
|
Revision: 1983 http://nagios.svn.sourceforge.net/nagios/?rev=1983&view=rev Author: ageric Date: 2012-06-30 18:06:51 +0000 (Sat, 30 Jun 2012) Log Message: ----------- Add lib/test-squeue And also add the infrastructure needed for adding more tests following the same pattern later. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/Makefile.in Added Paths: ----------- nagioscore/trunk/lib/test-squeue.c Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-06-30 18:06:31 UTC (rev 1982) +++ nagioscore/trunk/lib/Makefile.in 2012-06-30 18:06:51 UTC (rev 1983) @@ -9,9 +9,17 @@ all: $(LIBNAME) SNPRINTF_O=@SNPRINTF_O@ -SRC_C := squeue.c pqueue.c +TESTED_SRC_C := squeue.c +SRC_C := $(TESTED_SRC_C) pqueue.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) +TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) +test: $(TESTS) + @for t in $(TESTS); do echo $$t:; ./$$t; echo; done + +test-squeue: pqueue.o test-squeue.o + $(CC) $^ -o $@ + %.o: %.c %.h Makefile $(CC) $(ALL_CFLAGS) -c $< -o $@ Added: nagioscore/trunk/lib/test-squeue.c =================================================================== --- nagioscore/trunk/lib/test-squeue.c (rev 0) +++ nagioscore/trunk/lib/test-squeue.c 2012-06-30 18:06:51 UTC (rev 1983) @@ -0,0 +1,213 @@ +/* + * we include the c source file to get at the opaque types and + * api functions + */ +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <sys/time.h> +#include "squeue.c" + +typedef struct test_suite { + int pass; + int fail; + int latest; +} test_suite; + +static void squeue_foreach(squeue_t *q, int (*walker)(squeue_event *, void *), void *arg) +{ + squeue_t *dup; + void *e, *dup_d; + + dup = squeue_create(q->size); + dup_d = dup->d; + memcpy(dup, q, sizeof(*q)); + dup->d = dup_d; + memcpy(dup->d, q->d, (q->size * sizeof(void *))); + + while ((e = pqueue_pop(dup))) { + walker(e, arg); + } + squeue_destroy(dup, 0); +} + +int t_print(test_suite *ts) +{ + printf("total: %d; passed: %d; failed: %d; status: %s\n", + ts->pass + ts->fail, ts->pass, ts->fail, ts->fail ? "FAILED" : "PASSED"); + return ts->fail ? -1 : 0; +} + +#define TEST_PASS 0 +#define TEST_FAIL 1 + +#define t(expr, args...) \ + if ((expr)) { \ + ts->pass++; \ + ts->latest = TEST_PASS; \ + if (getenv("TEST_VERBOSE")) \ + printf("pass: " #expr "\n"); \ + } else { \ + ts->fail++; \ + ts->latest = TEST_FAIL; \ + printf("fail (@%s:%d):\n ##\t" #expr "\n", __FILE__, __LINE__); \ + printf(" " args); \ + kill(getpid(), SIGSEGV); \ + return -1; /* this will leak memory in some tests */ \ + } + +typedef struct sq_test_event { + unsigned long id; + squeue_event *evt; +} sq_test_event; + +static unsigned long sq_high = 0; +static int sq_walker(squeue_event *evt, void *arg) +{ + static int walks = 0; + + walks++; + test_suite *ts = (test_suite *)arg; + t(sq_high <= evt->when.tv_sec, "sq_high: %lu; evt->when: %lu\n", + sq_high, evt->when.tv_sec); + sq_high = (unsigned long)evt->when.tv_sec; + + return 0; +} + +#define EVT_ARY 65101 +static int sq_test_random(squeue_t *sq, test_suite *ts) +{ + unsigned long size, i; + unsigned long long numbers[EVT_ARY], *d, max = 0; + struct timeval now; + + size = squeue_size(sq); + now.tv_sec = time(NULL); + srand((int)now.tv_sec); + for (i = 0; i < EVT_ARY; i++) { + now.tv_usec = (time_t)rand(); + numbers[i] = evt_compute_pri(&now); + squeue_add_tv(sq, &now, &numbers[i]); + t(squeue_size(sq) == i + 1 + size); + } + + /* + * make sure we pop events in increasing "priority", + * since we calculate priority based on time and later + * is lower prio + */ + for (i = 0; i < EVT_ARY; i++) { + d = (unsigned long long *)squeue_pop(sq); + t(max <= *d, "popping randoms. i: %lu; delta: %lld; max: %llu; *d: %llu\n", + i, max - *d, max, *d); + max = *d; + t(squeue_size(sq) == size + (EVT_ARY - i - 1)); + } + + return 0; +} + +#if 0 +static void print_squeue_event(FILE *f, void *e) +{ + sq_test_event *te = ((squeue_event *)e)->data; + te = (sq_test_event *)squeue_event_data((squeue_event *)e); + + fprintf(f, "%lu\n", te->id); +} +#endif + +static int test_squeue(test_suite *ts) +{ + squeue_t *sq; + struct timeval tv; + sq_test_event a, b, c, d, *x; + + a.id = 1; + b.id = 2; + c.id = 3; + d.id = 4; + + gettimeofday(&tv, NULL); + /* Order in is a, b, c, d, but we should get b, c, d, a out. */ + srand(tv.tv_usec ^ tv.tv_sec); + t((sq = squeue_create(1024)) != NULL); + t(squeue_size(sq) == 0); + + /* we fill and empty the squeue completely once before testing */ + sq_test_random(sq, ts); + t(squeue_size(sq) == 0, "Size should be 0 after first sq_test_random"); + + t((a.evt = squeue_add(sq, time(NULL) + 9, &a)) != NULL); + t(squeue_size(sq) == 1); + t((b.evt = squeue_add(sq, time(NULL) + 3, &b)) != NULL); + t(squeue_size(sq) == 2); + t((c.evt = squeue_add(sq, time(NULL) + 5, &c)) != NULL); + t(squeue_size(sq) == 3); + t((d.evt = squeue_add(sq, time(NULL) + 6, &d)) != NULL); + t(squeue_size(sq) == 4); + + /* add and remove lots. remainder should be what we have above */ + sq_test_random(sq, ts); + + /* testing squeue_peek() */ + t((x = (sq_test_event *)squeue_peek(sq)) != NULL); + t(x == &b, "x: %p; a: %p; b: %p; c: %p; d: %p\n", x, &a, &b, &c, &d); + t(x->id == b.id); + t(squeue_size(sq) == 4); + + /* testing squeue_remove() and re-add */ + t(squeue_remove(sq, b.evt) == 0); + t(squeue_size(sq) == 3); + t((x = squeue_peek(sq)) != NULL); + t(x == &c); + t((b.evt = squeue_add(sq, time(NULL) + 3, &b)) != NULL); + t(squeue_size(sq) == 4); + + /* peek should now give us the &b event (again) */ + t((x = squeue_peek(sq)) != NULL); + if (x != &b) { + printf("about to fail pretty fucking hard...\n"); + printf("ea: %p; &b: %p; &c: %p; ed: %p; x: %p\n", + &a, &b, &c, &d, x); + } + t(x == &b); + t(x->id == b.id); + t(squeue_size(sq) == 4); + + /* testing squeue_pop(), lifo manner */ + t((x = squeue_pop(sq)) != NULL); + t(squeue_size(sq) == 3, + "squeue_size(sq) = %d\n", squeue_size(sq)); + t(x == &b, "x: %p; &b: %p\n", x, &b); + t(x->id == b.id, "x->id: %lu; d.id: %lu\n", x->id, d.id); + + /* Test squeue_pop() */ + t((x = squeue_pop(sq)) != NULL); + t(squeue_size(sq) == 2); + t(x == &c, "x->id: %lu; c.id: %lu\n", x->id, c.id); + t(x->id == c.id, "x->id: %lu; c.id: %lu\n", x->id, c.id); + + /* this should fail gracefully (-1 return from squeue_remove()) */ + t(squeue_remove(NULL, NULL) == -1); + t(squeue_remove(NULL, a.evt) == -1); + + squeue_foreach(sq, sq_walker, ts); + + /* clean up to prevent false valgrind positives */ + squeue_destroy(sq, 0); + + return t_print(ts); +} + +int main(int argc, char **argv) +{ + test_suite ts = { 0 }; + test_squeue(&ts); +// test_kvvec(); + return 0; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-01 01:11:21
|
Revision: 1984 http://nagios.svn.sourceforge.net/nagios/?rev=1984&view=rev Author: ageric Date: 2012-07-01 01:11:08 +0000 (Sun, 01 Jul 2012) Log Message: ----------- libnagios: Add key/value vector API This is a (very) simple and straightforward api used to gather and managed arrays of key/value pairs in a CPU-efficient manner, which includes array-to-buffer conversion and buffer-to-array conversion, making it highly useful for IPC. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/Makefile.in Added Paths: ----------- nagioscore/trunk/lib/kvvec.c nagioscore/trunk/lib/kvvec.h nagioscore/trunk/lib/test-kvvec.c Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-06-30 18:06:51 UTC (rev 1983) +++ nagioscore/trunk/lib/Makefile.in 2012-07-01 01:11:08 UTC (rev 1984) @@ -9,7 +9,7 @@ all: $(LIBNAME) SNPRINTF_O=@SNPRINTF_O@ -TESTED_SRC_C := squeue.c +TESTED_SRC_C := squeue.c kvvec.c SRC_C := $(TESTED_SRC_C) pqueue.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) Added: nagioscore/trunk/lib/kvvec.c =================================================================== --- nagioscore/trunk/lib/kvvec.c (rev 0) +++ nagioscore/trunk/lib/kvvec.c 2012-07-01 01:11:08 UTC (rev 1984) @@ -0,0 +1,270 @@ +/* + * key+value vector library + * + * Small and simple, but pretty helpful when parsing configurations + * from random formats into something a program can easily make sense + * of. + * + * The main type (struct kvvec *) should possibly be opaque since + * all callers should use the kvvec_foreach() variable to trudge + * around in the key/value vector. + */ +#include <stdlib.h> +#include <string.h> +#include "kvvec.h" + +struct kvvec *kvvec_init(int hint) +{ + struct kvvec *kvv; + + kvv = calloc(1, sizeof(*kvv)); + if (!kvv) + return NULL; + + kvvec_grow(kvv, hint); + return kvv; +} + +int kvvec_grow(struct kvvec *kvv, int hint) +{ + struct key_value *kv; + + if (!kvv) + return -1; + if (hint < kvv->kv_alloc) + return 0; + + kv = realloc(kvv->kv, sizeof(struct key_value) * hint); + if (!kv) + return -1; + + memset(&kv[kvv->kv_alloc], 0, hint - kvv->kv_alloc); + kvv->kv = kv; + kvv->kv_alloc = hint; + return 0; +} + +int kvvec_addkv_wlen(struct kvvec *kvv, char *key, int keylen, char *value, int valuelen) +{ + struct key_value *kv; + + if (!kvv || !key) + return -1; + + if (kvv->kv_pairs >= kvv->kv_alloc - 1) { + if (kvvec_grow(kvv, kvv->kv_pairs + 5)) + return -1; + } + + kv = &kvv->kv[kvv->kv_pairs++]; + kv->key = key; + kv->key_len = keylen; + kv->value = value; + kv->value_len = valuelen; + + if (!keylen) { + kv->key_len = strlen(key); + } + if (value) { + if (!valuelen) { + kv->value_len = strlen(value); + } + } else { + kv->value_len = 0; + kv->value = '\0'; + } + kvv->kvv_sorted = 0; + + return 0; +} + +static int kv_compare(const void *a_, const void *b_) +{ + const struct key_value *a = (const struct key_value *)a_; + const struct key_value *b = (const struct key_value *)b_; + int ret = 0; + + ret = strcmp(a->key, b->key); + if (ret) + return ret; + + if (!a->value && !b->value) { + return 0; + } + if (a->value && !b->value) + return -1; + if (!a->value && b->value) + return 1; + + return strcmp(a->value, b->value); +} + +int kvvec_sort(struct kvvec *kvv) +{ + qsort(kvv->kv, kvv->kv_pairs, sizeof(struct key_value), kv_compare); + kvv->kvv_sorted = 1; + return 0; +} + +int kvvec_foreach(struct kvvec *kvv, void *arg, int (*callback)(struct key_value *,void *)) +{ + int i; + + if (!kvv) + return 0; + + for (i = 0; i < kvv->kv_pairs; i++) { + callback(&kvv->kv[i], arg); + } + return 0; +} + +int kvvec_destroy(struct kvvec *kvv, int flags) +{ + int i; + + if (flags & (KVVEC_FREE_KEYS | KVVEC_FREE_VALUES)) { + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv = &kvv->kv[i]; + if (flags & KVVEC_FREE_KEYS) { + free(kv->key); + } + /* + * kv->value is always set, since we strdup("") on NULL + * ones, so free it regardless of length + */ + if (flags & KVVEC_FREE_VALUES && kv->value) { + free(kv->value); + } + } + } + + free(kvv->kv); + free(kvv); + return 0; +} + +/* + * Caller can tell us to over-allocate the buffer if he/she wants + * to put extra stuff at the end of it. + */ +struct kvvec_buf *kvvec2buf(struct kvvec *kvv, char kv_sep, char pair_sep, int overalloc) +{ + struct kvvec_buf *kvvb; + int i; + unsigned long len = 0; + + if (!kvv) + return NULL; + + kvvb = malloc(sizeof(struct kvvec_buf)); + if (!kvvb) + return NULL; + + /* overalloc + (kv_sep_size * kv_pairs) + (pair_sep_size * kv_pairs) */ + kvvb->bufsize = overalloc + (kvv->kv_pairs * 2); + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv = &kvv->kv[i]; + kvvb->bufsize += kv->key_len + kv->value_len; + } + + kvvb->buf = malloc(kvvb->bufsize); + if (!kvvb->buf) { + free(kvvb); + return NULL; + } + + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv = &kvv->kv[i]; + memcpy(kvvb->buf + len, kv->key, kv->key_len); + len += kv->key_len; + kvvb->buf[len++] = kv_sep; + if (kv->value_len) { + memcpy(kvvb->buf + len, kv->value, kv->value_len); + len += kv->value_len; + } + kvvb->buf[len++] = pair_sep; + } + memset(kvvb->buf + len, 0, kvvb->bufsize - len); + kvvb->buflen = len; + return kvvb; +} + +/* + * Converts a buffer of random bytes to a key/value vector. + * This requires a fairly rigid format in the input data to be of + * much use, but it's nifty for ipc where only computers are + * involved, and it will parse the kvvec2buf() produce nicely. + */ +struct kvvec *buf2kvvec(const char *str, unsigned int len, + const char kvsep, const char pair_sep) +{ + struct kvvec *kvv; + unsigned int num_pairs = 0, i, offset = 0; + + if (!str || !len) + return NULL; + + /* first we count the number of key/value pairs */ + for (;;) { + const char *ptr = memchr(str + offset, pair_sep, len - offset); + if (!ptr) + break; + num_pairs++; + ptr++; + offset += (unsigned long)ptr - ((unsigned long)str + offset); + } + + if (!num_pairs) { + return NULL; + } + + kvv = kvvec_init(num_pairs); + if (!kvv) { + return NULL; + } + + kvv->kv_pairs = num_pairs; + + offset = 0; + for (i = 0; i < num_pairs; i++) { + struct key_value *kv; + char *key_end_ptr, *kv_end_ptr; + + /* keys can't begin with nul bytes */ + if (offset && str[offset] == '\0') { + return kvv; + } + + key_end_ptr = memchr(str + offset, kvsep, len - offset); + if (!key_end_ptr) { + break; + } + kv_end_ptr = memchr(key_end_ptr + 1, pair_sep, len - ((unsigned long)key_end_ptr - (unsigned long)str)); + if (!kv_end_ptr) { + break; + } + + kv = &kvv->kv[i]; + kv->key_len = (unsigned long)key_end_ptr - ((unsigned long)str + offset); + kv->key = malloc(kv->key_len + 1); + memcpy(kv->key, str + offset, kv->key_len); + kv->key[kv->key_len] = 0; + + offset += kv->key_len + 1; + + if (str[offset] == pair_sep) { + kv->value_len = 0; + kv->value = strdup(""); + } else { + kv->value_len = (unsigned long)kv_end_ptr - ((unsigned long)str + offset); + kv->value = malloc(kv->value_len + 1); + kv->value[kv->value_len] = 0; + memcpy(kv->value, str + offset, kv->value_len); + } + + offset += kv->value_len + 1; + } + + return kvv; +} Added: nagioscore/trunk/lib/kvvec.h =================================================================== --- nagioscore/trunk/lib/kvvec.h (rev 0) +++ nagioscore/trunk/lib/kvvec.h 2012-07-01 01:11:08 UTC (rev 1984) @@ -0,0 +1,122 @@ +#ifndef INCLUDE_kvvec_h__ +#define INCLUDE_kvvec_h__ +struct key_value { + char *key, *value; + int key_len, value_len; +}; + +struct kvvec_buf { + char *buf; + unsigned long buflen; + unsigned long bufsize; +}; +typedef struct kvvec_buf kvvec_buf; + +struct kvvec { + struct key_value *kv; + int kv_alloc; + int kv_pairs; + int kvv_sorted; + int combined_len; /* used for kvvec_buf to avoid one loop */ +}; +typedef struct kvvec kvvec; + +/** Parameters for kvvec_destroy() */ +#define KVVEC_FREE_KEYS 1 +#define KVVEC_FREE_VALUES 2 +#define KVVEC_FREE_ALL (KVVEC_FREE_KEYS | KVVEC_FREE_VALUES) + +/** + * Initialize a key/value vector + * @param hint Number of key/value pairs we expect to store + * @return Pointer to a struct kvvec, properly initialized + */ +extern kvvec *kvvec_init(int hint); + +/** + * Grow a key/value vector. Used internally as needed by + * the kvvec api. + * + * @param kvv The key/value vector to grow + * @param hint The new size we should grow to + * @return 0 on success, < 0 on errors + */ +extern int kvvec_grow(kvvec *kvv, int hint); + +/** + * Sort a key/value vector alphabetically by key name + * @param kvv The key/value vector to sort + * @return 0 + */ +extern int kvvec_sort(kvvec *kvv); + +/** + * Add a key/value pair to an existing key/value vector, with + * lengths of strings already calculated + * @param kvv The key/value vector to add this key/value pair to + * @param key The key + * @param keylen Length of the key + * @param value The value + * @param valuelen Length of the value + * @return 0 on success, < 0 on errors + */ +extern int kvvec_addkv_wlen(kvvec *kvv, char *key, int keylen, char *value, int valuelen); + +/** + * Shortcut to kvvec_addkv_wlen() when lengths aren't known + * @param kvv The key/value vector to add this key/value pair to + * @param key The key + * @param value The value + * @return 0 on success, < 0 on errors + */ +#define kvvec_addkv(kvv, key, value) kvvec_addkv_wlen(kvv, key, 0, value, 0) + +/** + * Walk each key/value pair in a key/value vector, sending them + * as arguments to a callback function. The callback function has + * no control over the iteration process and must not delete or + * modify the key/value vector it's operating on. + * @param kvv The key/value vector to walk + * @param arg Extra argument to the callback function + * @param callback Callback function + * @return 0 on success, < 0 on errors + */ +extern int kvvec_foreach(kvvec *kvv, void *arg, int (*callback)(struct key_value *, void *)); + +/** + * Destroy a key/value vector + * @param kvv The key/value vector to destroy + * @param flags or'ed combination of KVVEC_FREE_{KEYS,VALUES}, or KVVEC_FREE_ALL + * @return 0 on success, < 0 on errors + */ +extern int kvvec_destroy(kvvec *kvv, int flags); + +/** + * Create a linear buffer of all the key/value pairs and + * return it as a kvvec_buf. The caller must free() all + * pointers in the returned kvvec_buf + * (FIXME: add kvvec_buf_destroy(), or move this and its counterpart + * out of the kvvec api into a separate one) + * + * @param kvv The key/value vector to convert + * @param kv_sep Character separating keys and their values + * @param pair_sep Character separating key/value pairs + * @param overalloc Integer determining how much extra data we should + * allocate. The overallocated memory is filled with + * nul bytes. + * @return A pointer to a newly created kvvec_buf structure + */ +extern kvvec_buf *kvvec2buf(kvvec *kvv, char kv_sep, char pair_sep, int overalloc); + +/** + * Create a key/value vector from a pre-parsed buffer. Immensely + * useful for ipc in combination with kvvec2buf(). + * + * @param str The buffer to convert to a key/value vector + * @param len Length of buffer to convert + * @param kv_sep Character separating key and value + * @param pair_sep Character separating key/value pairs + */ +extern kvvec *buf2kvvec(const char *str, unsigned int len, const char kvsep, const char pair_sep); + +#endif /* INCLUDE_kvvec_h__ */ Added: nagioscore/trunk/lib/test-kvvec.c =================================================================== --- nagioscore/trunk/lib/test-kvvec.c (rev 0) +++ nagioscore/trunk/lib/test-kvvec.c 2012-07-01 01:11:08 UTC (rev 1984) @@ -0,0 +1,110 @@ +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "kvvec.c" + +static int walking_steps, walks; + +static int walker(struct key_value *kv, void *discard) +{ + static struct kvvec *vec = (void *)1; + static int step; + + walking_steps++; + + if (vec != discard) { + walks++; + vec = (struct kvvec *)discard; + step = 0; + } + + if (discard && vec) { + if (!kv_compare(&vec->kv[step], kv)) + printf(" PASS: step %d on walk %d matches\n", step, walks); + } + + step++; + + return 0; +} + +#define KVSEP '=' +#define PAIRSEP '\0' +#define OVERALLOC 2 + +static const char *test_data[] = { + "lala=trudeldudel", + "foo=bar", + "LOTS AND LOTS OF CAPS WITH SPACES=weird", + "key=value", + "something-random=pre-determined luls", + "string=with\nnewlines\n\n\nand\nlots\nof\nthem\ntoo\n", + "tabs= this and that and three in a row", + NULL, +}; + +static void add_vars(struct kvvec *kvv, const char **ary, int len) +{ + int i; + + for (i = 0; i < len && ary[i]; i++) { + char *arg = strdup(test_data[i]); + char *eq = strchr(arg, '='); + if (eq) { + *eq++ = 0; + } + kvvec_addkv(kvv, strdup(arg), eq ? strdup(eq) : NULL); + free(arg); + } +} + +int main(int argc, char **argv) +{ + int i; + struct kvvec *kvv, *kvv2; + struct kvvec_buf *kvvb, *kvvb2; + + kvv = kvvec_init(1); + add_vars(kvv, test_data, 1239819); + add_vars(kvv, (const char **)argv + 1, argc - 1); + + kvvec_sort(kvv); + kvvec_foreach(kvv, NULL, walker); + + /* kvvec2buf -> buf2kvvec -> kvvec2buf -> buf2kvvec conversion */ + kvvb = kvvec2buf(kvv, KVSEP, PAIRSEP, OVERALLOC); + kvv2 = buf2kvvec(kvvb->buf, kvvb->buflen, KVSEP, PAIRSEP); + kvvec_foreach(kvv2, kvv, walker); + kvvb2 = kvvec2buf(kvv2, KVSEP, PAIRSEP, OVERALLOC); + + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv1, *kv2; + kv1 = &kvv->kv[i]; + if (i >= kvv2->kv_pairs) { + printf("%d failed: Not present in kvv2\n", i); + printf("[%s=%s] (%d+%d)\n", kv1->key, kv1->value, kv1->key_len, kv1->value_len); + continue; + } + kv2 = &kvv2->kv[i]; + if (kv_compare(kv1, kv2)) { + printf("%d failed: [%s=%s] (%d+%d) != [%s=%s (%d+%d)]\n", + i, + kv1->key, kv1->value, kv1->key_len, kv1->value_len, + kv2->key, kv2->value, kv2->key_len, kv2->value_len); + } + } + if (kvvb2->buflen == kvvb->buflen && kvvb2->bufsize == kvvb->bufsize && + !memcmp(kvvb2->buf, kvvb->buf, kvvb->bufsize)) + { + printf("PASS: kvvec -> buf -> kvvec conversion works flawlessly\n"); + } else { + printf("FAIL: kvvec -> buf -> kvvec conversion failed :'(\n"); + return EXIT_FAILURE; + } + + free(kvvb->buf); + free(kvvb); + kvvec_destroy(kvv, 1); + return 0; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-01 01:11:44
|
Revision: 1985 http://nagios.svn.sourceforge.net/nagios/?rev=1985&view=rev Author: ageric Date: 2012-07-01 01:11:37 +0000 (Sun, 01 Jul 2012) Log Message: ----------- libnagios: Add io cache api It's actually slightly misnamed, as it basically only contains helpers to read large chunks of data and then partition that data into the desired bits and pieces. It currently supports getting either fixed-size chunks or chunks by end-of-message sentinel byte sequences. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/Makefile.in Added Paths: ----------- nagioscore/trunk/lib/iocache.c nagioscore/trunk/lib/iocache.h nagioscore/trunk/lib/test-iocache.c Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-07-01 01:11:08 UTC (rev 1984) +++ nagioscore/trunk/lib/Makefile.in 2012-07-01 01:11:37 UTC (rev 1985) @@ -9,7 +9,7 @@ all: $(LIBNAME) SNPRINTF_O=@SNPRINTF_O@ -TESTED_SRC_C := squeue.c kvvec.c +TESTED_SRC_C := squeue.c kvvec.c iocache.c SRC_C := $(TESTED_SRC_C) pqueue.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) Added: nagioscore/trunk/lib/iocache.c =================================================================== --- nagioscore/trunk/lib/iocache.c (rev 0) +++ nagioscore/trunk/lib/iocache.c 2012-07-01 01:11:37 UTC (rev 1985) @@ -0,0 +1,161 @@ +#include "iocache.h" +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +void iocache_destroy(iocache *ioc) +{ + if (!ioc) + return; + + if (ioc->ioc_buf) + free(ioc->ioc_buf); + + free(ioc); +} + +/** + * Attempts to move data from the end to the beginning + * of the ioc_buf, expelling old data to make more room + * for new reads. + */ +static inline void iocache_move_data(iocache *ioc) +{ + unsigned long available; + + if (!ioc->ioc_offset) + return; /* nothing to do */ + + available = iocache_available(ioc); + memmove(ioc->ioc_buf, ioc->ioc_buf + ioc->ioc_offset, available); + ioc->ioc_offset = 0; + ioc->ioc_buflen = available; +} + +int iocache_resize(iocache *ioc, unsigned long new_size) +{ + char *buf; + + if (!ioc) + return -1; + + iocache_move_data(ioc); + + buf = realloc(ioc->ioc_buf, new_size); + if (!buf) + return -1; + return 0; +} + +unsigned long iocache_capacity(iocache *ioc) +{ + if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize) + return 0; + + iocache_move_data(ioc); + + return ioc->ioc_bufsize - ioc->ioc_buflen; +} + +unsigned long iocache_available(iocache *ioc) +{ + if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen) + return 0; + + return ioc->ioc_buflen - ioc->ioc_offset; +} + +char *iocache_use_size(iocache *ioc, unsigned long size) +{ + char *ret; + + if (!ioc || !ioc->ioc_buf) + return NULL; + if (ioc->ioc_bufsize < size || iocache_available(ioc) < size) + return NULL; + + ret = ioc->ioc_buf + ioc->ioc_offset; + ioc->ioc_offset += size; + return ret; +} + +char *iocache_use_delim(iocache *ioc, const char *delim, size_t delim_len, unsigned long *size) +{ + char *ptr = NULL; + char *buf; + unsigned long remains; + + if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen) + return NULL; + + buf = &ioc->ioc_buf[ioc->ioc_offset]; + remains = iocache_available(ioc); + while (remains >= delim_len) { + unsigned long jump; + ptr = memchr(buf, *delim, remains - (delim_len - 1)); + if (!ptr) { + return NULL; + } + if (delim_len == 1 || !memcmp(ptr, delim, delim_len)) { + unsigned long ioc_start; + + /* ptr must point *after* the delimiter */ + ptr += delim_len; + ioc_start = (unsigned long)ioc->ioc_buf + ioc->ioc_offset; + *size = (unsigned long)ptr - ioc_start; + + return iocache_use_size(ioc, *size); + } + jump = 1 + (unsigned long)ptr - (unsigned long)buf; + remains -= jump; + buf += jump; + } + return NULL; +} + +iocache *iocache_create(unsigned long size) +{ + iocache *ioc; + + ioc = calloc(1, sizeof(*ioc)); + if (ioc && size) { + ioc->ioc_buf = calloc(1, size); + if (!ioc->ioc_buf) { + free(ioc); + return NULL; + } + ioc->ioc_bufsize = size; + } + + return ioc; +} + +int iocache_read(iocache *ioc, int fd) +{ + int to_read, bytes_read; + + if (!ioc || !ioc->ioc_buf || fd < 0) + return -1; + + /* + * Check if we've managed to read our fill and the caller + * has parsed all data. Otherwise we might end up in a state + * where we can't read anything but there's still new data + * queued on the socket + */ + if (ioc->ioc_offset >= ioc->ioc_buflen) + ioc->ioc_offset = ioc->ioc_buflen = 0; + + /* we make sure we've got as much room as possible */ + iocache_move_data(ioc); + + /* calculate the size we should read */ + to_read = ioc->ioc_bufsize - ioc->ioc_buflen; + + bytes_read = read(fd, ioc->ioc_buf + ioc->ioc_buflen, to_read); + if (bytes_read > 0) { + ioc->ioc_buflen += bytes_read; + } + + return bytes_read; +} Added: nagioscore/trunk/lib/iocache.h =================================================================== --- nagioscore/trunk/lib/iocache.h (rev 0) +++ nagioscore/trunk/lib/iocache.h 2012-07-01 01:11:37 UTC (rev 1985) @@ -0,0 +1,107 @@ +#ifndef INCLUDE_iocache_h__ +#define INCLUDE_iocache_h__ +#include <stdlib.h> +#include <limits.h> +struct iocache { + char *ioc_buf; /* the data */ + unsigned long ioc_offset; /* where we're reading in the buffer */ + unsigned long ioc_buflen; /* the amount of data read into the buffer */ + unsigned long ioc_bufsize; /* size of the buffer */ +}; +typedef struct iocache iocache; + +static inline unsigned long iocache_used(iocache *ioc) +{ + if (!ioc) + return 0; + return ioc->ioc_buflen - ioc->ioc_offset; +} + +static inline unsigned long iocache_free(iocache *ioc) +{ + if (!ioc) + return 0; + return ioc->ioc_bufsize - ioc->ioc_buflen; +} + +static inline int iocache_grow(iocache *ioc, unsigned long add_size) +{ + if (!ioc) + return -1; + ioc->ioc_bufsize += add_size; + ioc->ioc_buf = realloc(ioc->ioc_buf, ioc->ioc_bufsize); + if (ioc->ioc_buf) + return 0; + + return -1; +} + +/** + * Destroys an iocache object, freeing all memory allocated to it. + * @param ioc The iocache object to destroy + */ +extern void iocache_destroy(iocache *ioc); + +/** + * Resizes the buffer in an io cache + * @param ioc The io cache to resize + * @param new_size The new size of the io cache + * @return 0 on success, -1 on errors + */ +extern int iocache_resize(iocache *ioc, unsigned long new_size); + +/** + * Returns remaining read capacity of the io cache + * @param ioc The io cache to operate on + * @return The number of bytes available to read + */ +extern unsigned long iocache_capacity(iocache *ioc); + +/** + * Return the amount of unread but stored data in the io cache + * @param ioc The io cache to operate on + * @return Number of bytes available to read + */ +extern unsigned long iocache_available(iocache *ioc); + +/** + * Use a chunk of data from iocache based on size. The caller + * must take care not to write beyond the end of the requested + * buffer, or Bad Things(tm) will happen. + * + * @param ioc The io cache we should use data from + * @param size The size of the data we want returned + * @return NULL on errors (insufficient data, fe). pointer on success + */ +extern char *iocache_use_size(iocache *ioc, unsigned long size); + +/** + * Use a chunk of data from iocache based on delimiter. The + * caller must take care not to write beyond the end of the + * requested buffer, if any is returned, or Bad Things(tm) will + * happen. + * + * @param ioc The io cache to use data from + * @param delim The delimiter + * @param delim_len Length of the delimiter + * @param size Length of the returned buffer + * @return NULL on errors (delimiter not found, insufficient data). pointer on success + */ +extern char *iocache_use_delim(iocache *ioc, const char *delim, size_t delim_len, unsigned long *size); + +/** + * Creates the iocache object, initializing it with the given size + * @param size Initial size of the iocache buffer + * @return Pointer to a valid iocache object + */ +extern iocache *iocache_create(unsigned long size); + +/** + * Read data into the iocache buffer + * @param ioc The io cache we should read into + * @param fd The filedescriptor we should read from + * @return The number of bytes read on success. < 0 on errors + */ +extern int iocache_read(iocache *ioc, int fd); + +#endif /* INCLUDE_iocache_h__ */ Added: nagioscore/trunk/lib/test-iocache.c =================================================================== --- nagioscore/trunk/lib/test-iocache.c (rev 0) +++ nagioscore/trunk/lib/test-iocache.c 2012-07-01 01:11:37 UTC (rev 1985) @@ -0,0 +1,82 @@ +#include <stdio.h> +#include <stdarg.h> +#include "iocache.c" + +struct strcode { + char *str; + unsigned int len; +}; +#define ADDSTR(str) { str, sizeof(str) - 1 } +static int test_delimiter(const char *delim, unsigned int delim_len) +{ + struct strcode sc[] = { + ADDSTR("Charlie Chaplin"), + ADDSTR("Madonna Something something"), + ADDSTR("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla turpis augue, laoreet eleifend ultricies et, tincidunt non felis. Suspendisse vitae accumsan dolor. Vivamus posuere venenatis dictum. Integer hendrerit est eget turpis scelerisque porttitor. Donec ullamcorper sodales purus, sed bibendum odio porttitor sit amet. Donec pretium sem ac sapien iaculis feugiat. Quisque commodo consequat quam, ac cursus est sodales euismod. Sed nec massa felis, sit amet varius dui. Morbi fermentum varius tellus, eget tempus felis imperdiet quis. Praesent congue auctor ligula, a tempor ipsum malesuada at. Proin pharetra tempor adipiscing. Aenean egestas tellus vitae arcu sagittis non ultrices turpis cursus."), + ADDSTR("Emma Blomqvist"), + ADDSTR("Random message"), + ADDSTR("Random\0message\0with\0nuls\0embedded"), + { NULL, 0, }, + }; + int i; + iocache *ioc; + + ioc = iocache_create(65536); + + for (i = 0; sc[i].str; i++) { + memcpy(&ioc->ioc_buf[ioc->ioc_buflen], sc[i].str, sc[i].len); + ioc->ioc_buflen += sc[i].len; + memcpy(ioc->ioc_buf + ioc->ioc_buflen, delim, delim_len); + ioc->ioc_buflen += delim_len; + } + + for (i = 0; sc[i].str; i++) { + char *ptr; + unsigned long len; + int error = 0; + ptr = iocache_use_delim(ioc, delim, delim_len, &len); + if (!ptr) { + printf("Null pointer. What weird shit is this??\n"); + exit(1); + } + if (len != sc[i].len + delim_len) { + printf("########## len error\n"); + error = 1; + } else if (memcmp(ptr, sc[i].str, len - delim_len)) { + printf("########## memcmp() error\n"); + error = 2; + } + + if (error) { + printf("delim_len: %d. i: %d; len: %lu; sc[i].len: %d\n", + delim_len, i, len, sc[i].len); + printf("sc[i].str: %s\n", sc[i].str); + printf("ptr : %s\n", ptr); + printf("strlen(sc[i].str): %lu\n", strlen(sc[i].str)); + printf("strlen(ptr) : %lu\n", strlen(ptr)); + exit(1); + } + } + iocache_destroy(ioc); + return 0; +} + +int main(int argc, char **argv) +{ + int i; + struct strcode sc[] = { + ADDSTR("\n"), + ADDSTR("\0\0"), + ADDSTR("XXXxXXX"), + ADDSTR("LALALALALALALAKALASBALLE\n"), + { NULL, 0 }, + }; + + for (i = 0; sc[i].str; i++) { + printf(" Testing delimiter of len %d\n", sc[i].len); + test_delimiter(sc[i].str, sc[i].len); + } + + printf("All tests passed\n"); + return 0; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-01 01:12:49
|
Revision: 1988 http://nagios.svn.sourceforge.net/nagios/?rev=1988&view=rev Author: ageric Date: 2012-07-01 01:12:42 +0000 (Sun, 01 Jul 2012) Log Message: ----------- libnagios: Add I/O broker library Severely nifty for watching filedescriptors for input, and a lot lighter on memory usage and cpu than both libev and libevent, although only for relatively small sets of watched filedescriptors and without support for timers, signal watchers and other jazz that lib{ev,event} bring along and we don't need. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/.gitignore nagioscore/trunk/lib/Makefile.in Added Paths: ----------- nagioscore/trunk/lib/iobroker.c nagioscore/trunk/lib/iobroker.h nagioscore/trunk/lib/test-iobroker.c Modified: nagioscore/trunk/lib/.gitignore =================================================================== --- nagioscore/trunk/lib/.gitignore 2012-07-01 01:12:14 UTC (rev 1987) +++ nagioscore/trunk/lib/.gitignore 2012-07-01 01:12:42 UTC (rev 1988) @@ -1,3 +1,4 @@ test-squeue test-kvvec test-iocache +test-iobroker Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-07-01 01:12:14 UTC (rev 1987) +++ nagioscore/trunk/lib/Makefile.in 2012-07-01 01:12:42 UTC (rev 1988) @@ -9,7 +9,7 @@ all: $(LIBNAME) SNPRINTF_O=@SNPRINTF_O@ -TESTED_SRC_C := squeue.c kvvec.c iocache.c +TESTED_SRC_C := squeue.c kvvec.c iocache.c iobroker.c SRC_C := $(TESTED_SRC_C) pqueue.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) Added: nagioscore/trunk/lib/iobroker.c =================================================================== --- nagioscore/trunk/lib/iobroker.c (rev 0) +++ nagioscore/trunk/lib/iobroker.c 2012-07-01 01:12:42 UTC (rev 1988) @@ -0,0 +1,421 @@ +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <errno.h> +#include <features.h> +#include <stdio.h> +#include <string.h> +#include "iobroker.h" + +/* + * epoll_*() is linux specific and was added to glibc 2.3.2, so we + * check for 2.4 and use epoll() if we're on that version or later. + */ +#if defined(__linux) && __GLIBC_PREREQ(2, 4) && !defined(IOBROKER_USES_SELECT) && !defined(IOBROKER_USES_POLL) +#define IOBROKER_USES_EPOLL +#endif + +#ifdef IOBROKER_USES_EPOLL +#include <sys/epoll.h> +/* these were added later */ +#ifndef EPOLLRDHUP +# define EPOLLRDHUP 0 +#endif +#ifndef EPOLLONESHOT +# define EPOLLONESHOT 0 +#endif +#elif !defined(IOBROKER_USES_SELECT) +#include <poll.h> +#else +#include <sys/select.h> +#endif + +#if defined(IOBROKER_USES_EPOLL) && defined(IOBROKER_USES_POLL) +# error "iobroker can't use both epoll() and poll()" +#elif defined(IOBROKER_USES_EPOLL) && defined(IOBROKER_USES_SELECT) +# error "iobroker can't use both epoll() and select()" +#elif defined(IOBROKER_USEES_POLL) && defined(IOBROKER_USES_SELECT) +# error "iobroker can't use both poll() and select()" +#endif + +typedef struct { + int fd; /* the file descriptor */ + int flags; /* various flags for the buffer */ + int (*handler)(int, int, void *); /* where we send data */ + void *arg; /* the argument we send to the input handler */ +} iobroker_fd; + + +struct iobroker_set { + iobroker_fd **iobroker_fds; + int max_fds; /* max number of sockets we can accept */ + int num_fds; /* number of sockets we're currently brokering for */ +#ifdef IOBROKER_USES_EPOLL + int epfd; + struct epoll_event *ep_events; +#elif !defined(IOBROKER_USES_SELECT) + struct pollfd *pfd; +#endif +}; + +static struct { + int code; + char *string; +} iobroker_errors[] = { + { IOBROKER_SUCCESS, "Success" }, + { IOBROKER_ENOSET, "IOB set is NULL" }, + { IOBROKER_ENOINIT, "IOB set not initialized" }, +}; +static const char *iobroker_unknown_error = "unknown error"; + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif +const char *iobroker_strerror(int error) +{ + if (error == IOBROKER_ELIB) + return strerror(errno); + error = ~error; + if (error < 0) { + return iobroker_unknown_error; + } + if (error >= ARRAY_SIZE(iobroker_errors)) + return strerror(error); + + return iobroker_errors[~error].string; +} + +void iobroker_print_set(int fd, iobroker_set *iobs) +{ + dprintf(fd, "iobroker_set:\n"); + dprintf(fd, "\tmax_fds: %d\n", iobs->max_fds); + dprintf(fd, "\tnum_fds: %d\n", iobs->num_fds); +#ifdef IOBROKER_USES_EPOLL + dprintf(fd, "\tepfd: %d\n", iobs->epfd); +#endif +} + + +int iobroker_max_usable_fds(void) +{ +#if defined(RLIMIT_NOFILE) + struct rlimit rlim; + getrlimit(RLIMIT_NOFILE, &rlim); + return (unsigned long)rlim.rlim_cur; +#elif defined(_SC_OPEN_MAX) + return (unsigned long)sysconf(_SC_OPEN_MAX); +#elif defined(OPEN_MAX) + return (unsigned long)OPEN_MAX; +#elif defined(_POSIX_OPEN_MAX) + return (unsigned long)_POSIX_OPEN_MAX; +#else + /* + * No sysconf macros, no rlimit and no hopefully-sane + * defaults so we just guess. This might be completely + * wrong and could cause segfaults + */ + return 256UL; +#endif +} + + +int iobroker_get_max_fds(iobroker_set *iobs) +{ + if (!iobs) + return IOBROKER_ENOSET; + return iobs->max_fds; +} + +int iobroker_get_num_fds(iobroker_set *iobs) +{ + if (!iobs) + return IOBROKER_ENOSET; + return iobs->num_fds; +} + +struct iobroker_set *iobroker_create(void) +{ + iobroker_set *iobs = NULL; + + iobs = calloc(1, sizeof(*iobs)); + if (!iobs) { + goto error_out; + } + + iobs->max_fds = iobroker_max_usable_fds(); + iobs->iobroker_fds = calloc(iobs->max_fds, sizeof(iobroker_fd *)); + if (!iobs->iobroker_fds) { + goto error_out; + } + +#ifdef IOBROKER_USES_EPOLL + { + int flags; + + iobs->ep_events = calloc(iobs->max_fds, sizeof(struct epoll_event)); + if (!iobs->ep_events) { + goto error_out; + } + + iobs->epfd = epoll_create(iobs->max_fds); + if (iobs->epfd < 0) { + goto error_out; + } + + flags = fcntl(iobs->epfd, F_GETFD); + flags |= FD_CLOEXEC; + fcntl(iobs->epfd, F_SETFD, flags); + } +#elif !defined(IOBROKER_USES_SELECT) + iobs->pfd = calloc(iobs->max_fds, sizeof(struct pollfd)); + if (!iobs->pfd) + goto error_out; +#endif + + return iobs; + +error_out: + if (iobs) { +#ifdef IOBROKER_USES_EPOLL + close(iobs->epfd); + if (iobs->ep_events) + free(iobs->ep_events); +#endif + if (iobs->iobroker_fds) + free(iobs->iobroker_fds); + free(iobs); + } + return NULL; +} + + +int iobroker_register(iobroker_set *iobs, int fd, void *arg, int (*handler)(int, int, void *)) +{ + iobroker_fd *s; + + if (!iobs) { + return IOBROKER_ENOSET; + } + if (fd < 0 || fd > iobs->max_fds) + return IOBROKER_EINVAL; + +#ifdef IOBROKER_USES_EPOLL + { + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLRDHUP; + ev.data.ptr = arg; + ev.data.fd = fd; + if (epoll_ctl(iobs->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) { + return IOBROKER_ENOSET; + } + } +#endif + + s = calloc(1, sizeof(iobroker_fd)); + s->handler = handler; + s->flags = s->flags; + s->fd = fd; + s->arg = arg; + iobs->iobroker_fds[fd] = s; + iobs->num_fds++; + + return 0; +} + + +int iobroker_unregister(iobroker_set *iobs, int fd) +{ + if (!iobs) + return IOBROKER_ENOSET; + + if (fd < 0 || fd >= iobs->max_fds || !iobs->iobroker_fds[fd]) + return IOBROKER_EINVAL; + + if (!iobs->iobroker_fds) + return IOBROKER_ENOINIT; + + free(iobs->iobroker_fds[fd]); + iobs->iobroker_fds[fd] = NULL; + iobs->num_fds--; + +#ifdef IOBROKER_USES_EPOLL + { + /* + * This needs to be set for linux <= 2.6.9 even though + * it's ignored even then. + */ + struct epoll_event ev; + + return epoll_ctl(iobs->epfd, EPOLL_CTL_DEL, fd, &ev); + } +#endif + return 0; +} + + +int iobroker_deregister(iobroker_set *iobs, int fd) +{ + return iobroker_unregister(iobs, fd); +} + + +int iobroker_close(iobroker_set *iobs, int fd) +{ + int result; + + result = iobroker_unregister(iobs, fd); + (void)close(fd); + return result; +} + + +void iobroker_destroy(iobroker_set *iobs, int flags) +{ + int i; + int (*dereg)(iobroker_set *, int) = iobroker_unregister; + + if (!iobs) + return; + + if (flags & IOBROKER_CLOSE_SOCKETS) { + dereg = iobroker_close; + } + +#ifdef IOBROKER_USES_EPOLL + if (iobs->epfd >= 0) + close(iobs->epfd); +#elif !defined(IOBROKER_USES_SELECT) + if (iobs->pfd) + free(iobs->pfd); +#endif + + if (!iobs->iobroker_fds) + return; + + for (i = 0; i < iobs->max_fds; i++) { + dereg(iobs, i); + } + free(iobs->iobroker_fds); +#ifdef IOBROKER_USES_EPOLL + free(iobs->ep_events); + close(iobs->epfd); +#endif + free(iobs); +} + + +int iobroker_poll(iobroker_set *iobs, int timeout) +{ + int i, nfds; + + if (!iobs) + return IOBROKER_ENOSET; + + if (!iobs->num_fds) + return IOBROKER_ENOINIT; + +#if defined(IOBROKER_USES_EPOLL) + nfds = epoll_wait(iobs->epfd, iobs->ep_events, iobs->num_fds, timeout); + if (nfds < 0) { + return nfds; + } + + for (i = 0; i < nfds; i++) { + int fd; + iobroker_fd *s = NULL; + + fd = iobs->ep_events[i].data.fd; + if (fd < 0 || fd > iobs->max_fds) { + continue; + } + s = iobs->iobroker_fds[fd]; + + if (s) { + s->handler(fd, iobs->ep_events[i].events, s->arg); + } + } +#elif defined(IOBROKER_USES_SELECT) + /* + * select() is the (last) fallback, as it's the least + * efficient by quite a huge margin, so it has to be + * specified specially (in CFLAGS) and should only be + * used if epoll() or poll() doesn't work properly. + */ + { + fd_set read_fds; + int num_fds = 0; + struct timeval tv; + + FD_ZERO(&read_fds); + for (i = 0; i < iobs->max_fds; i++) { + if (!iobs->iobroker_fds[i]) + continue; + num_fds++; + FD_SET(iobs->iobroker_fds[i]->fd, &read_fds); + if (num_fds == iobs->num_fds) + break; + } + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + nfds = select(iobs->max_fds, &read_fds, NULL, NULL, &tv); + } else { /* timeout of -1 means poll indefinitely */ + nfds = select(iobs->max_fds, &read_fds, NULL, NULL, NULL); + } + if (nfds < 0) { + return IOBROKER_ELIB; + } + num_fds = 0; + for (i = 0; i < iobs->max_fds; i++) { + if (!iobs->iobroker_fds[i]) + continue; + if (FD_ISSET(iobs->iobroker_fds[i]->fd, &read_fds)) { + iobroker_fd *s = iobs->iobroker_fds[i]; + if (!s) { + /* this should be logged somehow */ + continue; + } + s->handler(s->fd, POLLIN, s->arg); + } + } + } +#else + /* + * poll(2) is an acceptable fallback if level-triggered epoll() + * isn't available. + */ + { + int p = 0; + + for (i = 0; i < iobs->max_fds; i++) { + if (!iobs->iobroker_fds[i]) + continue; + iobs->pfd[p].fd = iobs->iobroker_fds[i]->fd; + iobs->pfd[p].events = POLLIN; + p++; + } + nfds = poll(iobs->pfd, iobs->num_fds, timeout); + if (nfds < 0) { + return IOBROKER_ELIB; + } + for (i = 0; i < iobs->num_fds; i++) { + iobroker_fd *s; + if ((iobs->pfd[i].revents & POLLIN) != POLLIN) { + continue; + } + + s = iobs->iobroker_fds[iobs->pfd[i].fd]; + if (!s) { + /* this should be logged somehow */ + continue; + } + s->handler(s->fd, (int)iobs->pfd[i].revents, s->arg); + } + } +#endif + + return 0; +} Added: nagioscore/trunk/lib/iobroker.h =================================================================== --- nagioscore/trunk/lib/iobroker.h (rev 0) +++ nagioscore/trunk/lib/iobroker.h 2012-07-01 01:12:42 UTC (rev 1988) @@ -0,0 +1,139 @@ +#ifndef INCLUDE_iobroker_h__ +#define INCLUDE_iobroker_h__ + +#if (_POSIX_C_SOURCE - 0) >= 200112L +#include <poll.h> +# define IOBROKER_POLLIN POLLIN +# define IOBROKER_POLLPRI POLLPRI +# define IOBROKER_POLLOUT POLLOUT + +# define IOBROKER_POLLERR POLLERR +# define IOBROKER_POLLHUP POLLHUP +# define IOBROKER_POLLNVAL POLLNVAL +#else +# define IOBROKER_POLLIN 0x001 /* there is data to read */ +# define IOBROKER_POLLPRI 0x002 /* there is urgent data to read */ +# define IOBROKER_POLLOUT 0x004 /* writing now will not block */ + +# define IOBROKER_POLLERR 0x008 /* error condition */ +# define IOBROKER_POLLHUP 0x010 /* hung up */ +# define IOBROKER_POLLNVAL 0x020 /* invalid polling request */ +#endif + +/** return codes */ +#define IOBROKER_SUCCESS 0 +#define IOBROKER_ENOSET -1 +#define IOBROKER_ENOINIT -2 +#define IOBROKER_ELIB -3 +#define IOBROKER_EINVAL -EINVAL + + +/** Flags for iobroker_destroy() */ +#define IOBROKER_CLOSE_SOCKETS 1 + +/* Opaque type. Callers needn't worry about this */ +struct iobroker_set; +typedef struct iobroker_set iobroker_set; + +/** + * Get a string describing the error in the last iobroker call. + * The returned string must not be free()'d. + * @param error The error code + * @return A string describing the meaning of the error code + */ +extern const char *iobroker_strerror(int error); + +/** + * Write an io broker set to the named filedescriptor + * @param fd The filedescriptor to write to (using dprintf()) + * @param iobs The io broker set to print + */ +extern void iobroker_print_set(int fd, iobroker_set *iobs); + +/** + * Create a new socket set + * @return An iobroker_set on success. NULL on errors. + */ +extern iobroker_set *iobroker_create(void); + +/** + * Published utility function used to determine the max number of + * file descriptors this process can keep open at any one time. + * @return Max number of filedescriptors we can keep open + */ +extern int iobroker_max_usable_fds(void); + +/** + * Register a socket for input polling with the broker. + * + * @param iobs The socket set to add the socket to. + * @param sd The socket descriptor to add + * @param arg Argument passed to input handler on available input + * @param input_handler The callback function to call when input is available + * + * @return 0 on succes. < 0 on errors. + */ +extern int iobroker_register(iobroker_set *iobs, int sd, void *arg, int (*handler)(int, int, void *)); + + +/** + * Getter function for number of file descriptors registered in + * the set specified. + * @param iobs The io broker set to query + * @return Number of file descriptors registered in the set + */ +extern int iobroker_get_num_fds(iobroker_set *iobs); + +/** + * Getter function for the maximum amount of file descriptors this + * set can handle. + * @param iobs The io broker set to query + * @return Max file descriptor capacity for the set + */ +extern int iobroker_get_max_fds(iobroker_set *iobs); + +/** + * Unregister a socket for input polling with the broker. + * + * @param iobs The socket set to remove the socket from + * @param sd The socket descriptor to remove + * @return 0 on succes. < 0 on errors. + */ +extern int iobroker_unregister(iobroker_set *iobs, int sd); + +/** + * Deregister a socket for input polling with the broker + * (this is identical to iobroker_unregister()) + * @param iobs The socket set to remove the socket from + * @param sd The socket descriptor to remove + * @return 0 on success. < 0 on errors. + */ +extern int iobroker_deregister(iobroker_set *iobs, int sd); + +/** + * Unregister and close(2) a socket registered for input with the + * broker. This is a convenience function which exists only to avoid + * doing multiple calls when read() returns 0, as closed sockets must + * always be removed from the socket set to avoid consuming tons of + * cpu power from iterating "too fast" over the file descriptors. + * + * @param iobs The socket set to remove the socket from + * @param sd The socket descriptor to remove and close + * @return 0 on success. < 0 on errors + */ +extern int iobroker_close(iobroker_set *iobs, int sd); + +/** + * Destroy a socket set as created by iobroker_create + * @param iobs The socket set to destroy + * @param close close(2) all available sockets + */ +extern void iobroker_destroy(iobroker_set *iobs, int flags); + +/** + * Wait for input on any of the registered sockets. + * @param iobs The socket set to wait for. + * @param timeout Timeout in milliseconds. -1 is "wait indefinitely" + */ +extern int iobroker_poll(iobroker_set *iobs, int timeout); +#endif /* INCLUDE_iobroker_h__ */ Added: nagioscore/trunk/lib/test-iobroker.c =================================================================== --- nagioscore/trunk/lib/test-iobroker.c (rev 0) +++ nagioscore/trunk/lib/test-iobroker.c 2012-07-01 01:12:42 UTC (rev 1988) @@ -0,0 +1,208 @@ +#include <signal.h> +#include <stdio.h> +#include <malloc.h> +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> + +#include "iobroker.c" + +static int fail, pass; +static iobroker_set *iobs; +#define CHECKPOINT(x__) \ + do { \ + fprintf(stderr, "ALIVE @ %s:%s:%d\n", __FILE__, __func__, __LINE__); \ + } while(0) + +#define TRY(x__) \ + do { \ + fprintf(stderr, "Trying '%s;'\n", #x__); \ + x__; \ + fprintf(stderr, "Seems to have worked out ok\n"); \ + } while (0) + +static char *msg[] = { + "Welcome to the echo service!\n", + "test msg nr 2", + "random piece of string\nwith\nlots\nof\nnewlines\n\n\n\n\0", + "another totally random message\n", + "and this one's from emma. She's alright, really", + NULL, +}; + +static int echo_service(int fd, int events, void *arg) +{ + char buf[1024]; + int len; + + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + perror("read"); + iobroker_close(iobs, fd); + return 0; + } + /* zero read means we're disconnected */ + if (!len) { + iobroker_close(iobs, fd); + return 0; + } + + write(fd, buf, len); + + return 0; +} + +static int connected_handler(int fd, int events, void *arg) +{ + int *counter = (int *)arg; + int i; + + i = *counter; + + if (events == IOBROKER_POLLIN) { + char buf[1024]; + int len = read(fd, buf, sizeof(buf)); + + buf[len] = 0; + + if (len != strlen(msg[i]) || memcmp(buf, msg[i], len)) { + printf("fd: %d, i: %d; len: %d; buf: %s\n", fd, i, len, buf); + fprintf(stderr, "Upping fail at #1\n"); + fail++; + } else { + pass++; + } + } + + i++; + + if (i < 0 || i >= ARRAY_SIZE(msg)) { + fprintf(stderr, "i = %d in connected_handler(). What's up with that?\n", i); + return 0; + } + + if (!msg[i]) { + //printf("OK: %d messages sent on socket %d\n", i, fd); + iobroker_close(iobs, fd); + return 0; + } + + write(fd, msg[i], strlen(msg[i])); + *counter = i; + + return 0; +} + +static int listen_handler(int fd, int events, void *arg) +{ + int sock; + struct sockaddr_in sain; + socklen_t addrlen; + + if (!arg || arg != iobs) { + printf("Argument passing seems to fail spectacularly\n"); + } + + //printf("listen_handler(%d, %d, %p) called\n", fd, events, arg); + sock = accept(fd, (struct sockaddr *)&sain, &addrlen); + if (sock < 0) { + perror("accept"); + return -1; + } + + write(sock, msg[0], strlen(msg[0])); + iobroker_register(iobs, sock, iobs, echo_service); + return 0; +} + +int sighandler(int sig) +{ + /* test failed */ + fprintf(stderr, "Caught signal %d (%sSIGALRM). Fail = %d\n", + sig, sig == SIGALRM ? "" : "not ", fail); + exit(1); +} + + +#define NUM_PROCS 500 +static int proc_counter[NUM_PROCS]; +static int conn_spam(struct sockaddr_in *sain) +{ + int i; + + signal(SIGALRM, (__sighandler_t)sighandler); + signal(SIGINT, (__sighandler_t)sighandler); + signal(SIGPIPE, SIG_IGN); + alarm(20); + + for (i = 0; i < NUM_PROCS; i++) { + int fd, sockopt = 1; + + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + proc_counter[i] = 0; + iobroker_register(iobs, fd, (void *)&proc_counter[i], connected_handler); + if (connect(fd, (struct sockaddr *)sain, sizeof(*sain))) { + perror("connect"); + } + iobroker_poll(iobs, -1); + } + printf("%d connections spammed\n", i); + return 0; +} + +int main(int argc, char **argv) +{ + int listen_fd, flags, sockopt = 1; + struct sockaddr_in sain; + + iobs = iobroker_create(); + listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + flags = fcntl(listen_fd, F_GETFD); + flags |= FD_CLOEXEC; + fcntl(listen_fd, F_SETFD, flags); + + (void)setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + + memset(&sain, 0, sizeof(sain)); + sain.sin_port = ntohs(9123); + sain.sin_family = AF_INET; + bind(listen_fd, (struct sockaddr *)&sain, sizeof(sain)); + printf("Listening on %s:%d with socket %d\n", + inet_ntoa(sain.sin_addr), ntohs(sain.sin_port), listen_fd); + listen(listen_fd, 128); + printf("Registering listener socket %d with I/O broker\n", listen_fd); + iobroker_register(iobs, listen_fd, iobs, listen_handler); + + if (argc == 1) + conn_spam(&sain); + + for (;;) { + iobroker_poll(iobs, -1); + if (iobroker_get_num_fds(iobs) <= 1) { + break; + } + } + + iobroker_close(iobs, listen_fd); + printf("Destroying iobs\n"); + iobroker_destroy(iobs, 0); + + if (fail) { + printf("FAIL: %d tests failed\n", fail); + return 1; + } + + if (pass) { + if (pass == (ARRAY_SIZE(msg) - 1) * NUM_PROCS) { + printf("PASS: All %d tests ran just fine\n", pass); + } else { + printf("PASS: %d tests passed, with connection problems\n", pass); + } + } + return 0; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-02 01:01:17
|
Revision: 1991 http://nagios.svn.sourceforge.net/nagios/?rev=1991&view=rev Author: ageric Date: 2012-07-02 01:01:07 +0000 (Mon, 02 Jul 2012) Log Message: ----------- libnagios: Add tests for and fix bugs in iobroker_strerror() Having such easily tested functions untested was just plain dumb, which was proven quite quickly. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iobroker.c nagioscore/trunk/lib/test-iobroker.c Modified: nagioscore/trunk/lib/iobroker.c =================================================================== --- nagioscore/trunk/lib/iobroker.c 2012-07-01 01:13:17 UTC (rev 1990) +++ nagioscore/trunk/lib/iobroker.c 2012-07-02 01:01:07 UTC (rev 1991) @@ -78,14 +78,14 @@ { if (error == IOBROKER_ELIB) return strerror(errno); - error = ~error; + error = (~error) + 1; if (error < 0) { return iobroker_unknown_error; } if (error >= ARRAY_SIZE(iobroker_errors)) return strerror(error); - return iobroker_errors[~error].string; + return iobroker_errors[error].string; } void iobroker_print_set(int fd, iobroker_set *iobs) Modified: nagioscore/trunk/lib/test-iobroker.c =================================================================== --- nagioscore/trunk/lib/test-iobroker.c 2012-07-01 01:13:17 UTC (rev 1990) +++ nagioscore/trunk/lib/test-iobroker.c 2012-07-02 01:01:07 UTC (rev 1991) @@ -9,6 +9,7 @@ #include <stdlib.h> #include <fcntl.h> +#include "iocache.c" #include "iobroker.c" static int fail, pass; @@ -159,8 +160,27 @@ { int listen_fd, flags, sockopt = 1; struct sockaddr_in sain; + int error; + const char *err_msg; + error = iobroker_get_max_fds(NULL); + if (error == IOBROKER_ENOSET) + pass++; + else + fail++; + err_msg = iobroker_strerror(error); + if (err_msg && !strcmp(err_msg, iobroker_errors[(~error) + 1].string)) + pass++; + else + fail++; + iobs = iobroker_create(); + error = iobroker_get_max_fds(iobs); + if (iobs && error >= 0) + pass++; + else + fail++; + listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); flags = fcntl(listen_fd, F_GETFD); flags |= FD_CLOEXEC; @@ -198,7 +218,7 @@ } if (pass) { - if (pass == (ARRAY_SIZE(msg) - 1) * NUM_PROCS) { + if (pass == 3 + (ARRAY_SIZE(msg) - 1) * NUM_PROCS) { printf("PASS: All %d tests ran just fine\n", pass); } else { printf("PASS: %d tests passed, with connection problems\n", pass); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-02 01:04:54
|
Revision: 2002 http://nagios.svn.sourceforge.net/nagios/?rev=2002&view=rev Author: ageric Date: 2012-07-02 01:04:44 +0000 (Mon, 02 Jul 2012) Log Message: ----------- lib/iocache: Make iocache_use_delim() properly mark all data used It makes no sense to force callers to fiddle with adding or subtracting the delimiter, so we remove it from the size calculation and make sure we just pass the delimiter length along when marking data as used in the io cache. Tests are naturally fixed to reflect the new world order. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iocache.c nagioscore/trunk/lib/test-iocache.c Modified: nagioscore/trunk/lib/iocache.c =================================================================== --- nagioscore/trunk/lib/iocache.c 2012-07-02 01:04:29 UTC (rev 2001) +++ nagioscore/trunk/lib/iocache.c 2012-07-02 01:04:44 UTC (rev 2002) @@ -62,6 +62,11 @@ if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen) return 0; + if (ioc->ioc_buflen < ioc->ioc_offset) { + iocache_move_data(ioc); + return 0; + } + return ioc->ioc_buflen - ioc->ioc_offset; } @@ -88,6 +93,12 @@ if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen) return NULL; + *size = 0; + if (ioc->ioc_offset >= ioc->ioc_buflen) { + iocache_move_data(ioc); + return NULL; + } + buf = &ioc->ioc_buf[ioc->ioc_offset]; remains = iocache_available(ioc); while (remains >= delim_len) { @@ -99,12 +110,11 @@ if (delim_len == 1 || !memcmp(ptr, delim, delim_len)) { unsigned long ioc_start; - /* ptr must point *after* the delimiter */ - ptr += delim_len; ioc_start = (unsigned long)ioc->ioc_buf + ioc->ioc_offset; *size = (unsigned long)ptr - ioc_start; - return iocache_use_size(ioc, *size); + /* make sure we use up all of the delimiter as well */ + return iocache_use_size(ioc, delim_len + *size); } jump = 1 + (unsigned long)ptr - (unsigned long)buf; remains -= jump; Modified: nagioscore/trunk/lib/test-iocache.c =================================================================== --- nagioscore/trunk/lib/test-iocache.c 2012-07-02 01:04:29 UTC (rev 2001) +++ nagioscore/trunk/lib/test-iocache.c 2012-07-02 01:04:44 UTC (rev 2002) @@ -21,7 +21,7 @@ int i; iocache *ioc; - ioc = iocache_create(65536); + ioc = iocache_create(512 * 1024); for (i = 0; sc[i].str; i++) { memcpy(&ioc->ioc_buf[ioc->ioc_buflen], sc[i].str, sc[i].len); @@ -39,10 +39,10 @@ printf("Null pointer. What weird shit is this??\n"); exit(1); } - if (len != sc[i].len + delim_len) { + if (len != sc[i].len) { printf("########## len error\n"); error = 1; - } else if (memcmp(ptr, sc[i].str, len - delim_len)) { + } else if (memcmp(ptr, sc[i].str, len)) { printf("########## memcmp() error\n"); error = 2; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-05 11:38:42
|
Revision: 2005 http://nagios.svn.sourceforge.net/nagios/?rev=2005&view=rev Author: ageric Date: 2012-07-05 11:38:32 +0000 (Thu, 05 Jul 2012) Log Message: ----------- lib/iocache: Make struct iocache an opaque type With two simple helpers added (and some redundant code removed), this means we're free to modify the iocache structure any way we want and we'll never break its ABI. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iocache.c nagioscore/trunk/lib/iocache.h Modified: nagioscore/trunk/lib/iocache.c =================================================================== --- nagioscore/trunk/lib/iocache.c 2012-07-05 11:38:14 UTC (rev 2004) +++ nagioscore/trunk/lib/iocache.c 2012-07-05 11:38:32 UTC (rev 2005) @@ -3,6 +3,13 @@ #include <stdlib.h> #include <string.h> +struct iocache { + char *ioc_buf; /* the data */ + unsigned long ioc_offset; /* where we're reading in the buffer */ + unsigned long ioc_buflen; /* the amount of data read into the buffer */ + unsigned long ioc_bufsize; /* size of the buffer */ +}; + void iocache_destroy(iocache *ioc) { if (!ioc) @@ -49,6 +56,16 @@ return 0; } +int iocache_grow(iocache *ioc, unsigned long increment) +{ + return iocache_resize(ioc, iocache_size(ioc) + increment); +} + +unsigned long iocache_size(iocache *ioc) +{ + return ioc ? ioc->ioc_bufsize : 0; +} + unsigned long iocache_capacity(iocache *ioc) { if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize) Modified: nagioscore/trunk/lib/iocache.h =================================================================== --- nagioscore/trunk/lib/iocache.h 2012-07-05 11:38:14 UTC (rev 2004) +++ nagioscore/trunk/lib/iocache.h 2012-07-05 11:38:32 UTC (rev 2005) @@ -2,40 +2,11 @@ #define INCLUDE_iocache_h__ #include <stdlib.h> #include <limits.h> -struct iocache { - char *ioc_buf; /* the data */ - unsigned long ioc_offset; /* where we're reading in the buffer */ - unsigned long ioc_buflen; /* the amount of data read into the buffer */ - unsigned long ioc_bufsize; /* size of the buffer */ -}; + +/** opaque type for iocache operations */ +struct iocache; typedef struct iocache iocache; -static inline unsigned long iocache_used(iocache *ioc) -{ - if (!ioc) - return 0; - return ioc->ioc_buflen - ioc->ioc_offset; -} - -static inline unsigned long iocache_free(iocache *ioc) -{ - if (!ioc) - return 0; - return ioc->ioc_bufsize - ioc->ioc_buflen; -} - -static inline int iocache_grow(iocache *ioc, unsigned long add_size) -{ - if (!ioc) - return -1; - ioc->ioc_bufsize += add_size; - ioc->ioc_buf = realloc(ioc->ioc_buf, ioc->ioc_bufsize); - if (ioc->ioc_buf) - return 0; - - return -1; -} - /** * Destroys an iocache object, freeing all memory allocated to it. * @param ioc The iocache object to destroy @@ -51,6 +22,22 @@ extern int iocache_resize(iocache *ioc, unsigned long new_size); /** + * Grows an iocache object + * This uses iocache_resize() internally + * @param[in] ioc The iocache to grow + * @param[in] increment How much to increase it + * @return 0 on success, -1 on errors + */ +extern int iocache_grow(iocache *ioc, unsigned long increment); + +/** + * Returns the total size of the io cache + * @param[in] ioc The iocache to inspect + * @return The size of the io cache. If ioc is null, 0 is returned + */ +extern unsigned long iocache_size(iocache *ioc); + +/** * Returns remaining read capacity of the io cache * @param ioc The io cache to operate on * @return The number of bytes available to read This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-05 11:39:40
|
Revision: 2008 http://nagios.svn.sourceforge.net/nagios/?rev=2008&view=rev Author: ageric Date: 2012-07-05 11:39:30 +0000 (Thu, 05 Jul 2012) Log Message: ----------- libnagios headers: Fix doxygen commenting It's nifty and we'll want to use it, but for preference without the retarded amount of warnings the previous state of the comments caused doxygen to spit out. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iobroker.h nagioscore/trunk/lib/iocache.h nagioscore/trunk/lib/kvvec.h nagioscore/trunk/lib/libnagios.h nagioscore/trunk/lib/pqueue.h nagioscore/trunk/lib/squeue.h Modified: nagioscore/trunk/lib/iobroker.h =================================================================== --- nagioscore/trunk/lib/iobroker.h 2012-07-05 11:39:06 UTC (rev 2007) +++ nagioscore/trunk/lib/iobroker.h 2012-07-05 11:39:30 UTC (rev 2008) @@ -1,6 +1,13 @@ #ifndef INCLUDE_iobroker_h__ #define INCLUDE_iobroker_h__ +/** + * @file iobroker.h + * @brief I/O broker library function declarations + * + * @{ + */ + #if (_POSIX_C_SOURCE - 0) >= 200112L #include <poll.h> # define IOBROKER_POLLIN POLLIN @@ -69,7 +76,7 @@ * @param iobs The socket set to add the socket to. * @param sd The socket descriptor to add * @param arg Argument passed to input handler on available input - * @param input_handler The callback function to call when input is available + * @param handler The callback function to call when input is available * * @return 0 on succes. < 0 on errors. */ @@ -126,7 +133,7 @@ /** * Destroy a socket set as created by iobroker_create * @param iobs The socket set to destroy - * @param close close(2) all available sockets + * @param flags If set, close(2) all registered sockets */ extern void iobroker_destroy(iobroker_set *iobs, int flags); @@ -137,3 +144,4 @@ */ extern int iobroker_poll(iobroker_set *iobs, int timeout); #endif /* INCLUDE_iobroker_h__ */ +/** @} */ Modified: nagioscore/trunk/lib/iocache.h =================================================================== --- nagioscore/trunk/lib/iocache.h 2012-07-05 11:39:06 UTC (rev 2007) +++ nagioscore/trunk/lib/iocache.h 2012-07-05 11:39:30 UTC (rev 2008) @@ -3,6 +3,17 @@ #include <stdlib.h> #include <limits.h> +/** + * @file iocache.h + * @brief I/O cache function declarations + * + * The I/O cache library is useful for reading large chunks of data + * from sockets and utilizing parts of that data based on either + * size or a magic delimiter. + * + * @{ + */ + /** opaque type for iocache operations */ struct iocache; typedef struct iocache iocache; @@ -92,3 +103,4 @@ extern int iocache_read(iocache *ioc, int fd); #endif /* INCLUDE_iocache_h__ */ +/** @} */ Modified: nagioscore/trunk/lib/kvvec.h =================================================================== --- nagioscore/trunk/lib/kvvec.h 2012-07-05 11:39:06 UTC (rev 2007) +++ nagioscore/trunk/lib/kvvec.h 2012-07-05 11:39:30 UTC (rev 2008) @@ -1,27 +1,53 @@ #ifndef INCLUDE_kvvec_h__ #define INCLUDE_kvvec_h__ + +/** + * @file kvvec.h + * @brief Key/value vector library function and type declarations + * + * The kvvec library is nifty as either a configuration meta-format + * or for IPC purposes. Take a look at the buf2kvvec() and kvvec2buf() + * pair of functions for the latter. + */ + +/** + * key/value pair + * One of the two major components of the kvvec api + */ struct key_value { - char *key, *value; - int key_len, value_len; + char *key; /**< The key */ + char *value; /**< The value */ + int key_len; /**< Length of key */ + int value_len; /**< Length of value */ }; +/** + * key/value vector buffer. Actually just a buffer, but one that gets + * used as return value and internal tracker for kvvec2buf() + */ struct kvvec_buf { - char *buf; - unsigned long buflen; - unsigned long bufsize; + char *buf; /**< The buffer */ + unsigned long buflen; /**< Length of buffer */ + unsigned long bufsize; /**< Size of buffer (includes overalloc) */ }; +/** + * key/value vector struct + * This is the main component of the kvvec library + * @note This should be made opaque, with a kvvec_foreach() using a + * callback to iterate over key/value pairs. + */ struct kvvec { - struct key_value *kv; - int kv_alloc; - int kv_pairs; - int kvv_sorted; - int combined_len; /* used for kvvec_buf to avoid one loop */ + struct key_value *kv; /**< The key/value array */ + int kv_alloc; /**< Allocated size of key/value array */ + int kv_pairs; /**< Number of key/value pairs */ + int kvv_sorted; /**< Determines if this kvvec has been sorted */ }; /** Parameters for kvvec_destroy() */ -#define KVVEC_FREE_KEYS 1 -#define KVVEC_FREE_VALUES 2 +#define KVVEC_FREE_KEYS 1 /**< Free keys when destroying a kv vector */ +#define KVVEC_FREE_VALUES 2 /**< Free values when destroying a kv vector */ +/** Free both keys and values when destroying a kv vector */ #define KVVEC_FREE_ALL (KVVEC_FREE_KEYS | KVVEC_FREE_VALUES) /** @@ -112,7 +138,7 @@ * * @param str The buffer to convert to a key/value vector * @param len Length of buffer to convert - * @param kv_sep Character separating key and value + * @param kvsep Character separating key and value * @param pair_sep Character separating key/value pairs */ extern struct kvvec *buf2kvvec(const char *str, unsigned int len, const char kvsep, const char pair_sep); Modified: nagioscore/trunk/lib/libnagios.h =================================================================== --- nagioscore/trunk/lib/libnagios.h 2012-07-05 11:39:06 UTC (rev 2007) +++ nagioscore/trunk/lib/libnagios.h 2012-07-05 11:39:30 UTC (rev 2008) @@ -1,3 +1,9 @@ +/** + * @file libnagios.h + * + * @brief Include this for all public parts of libnagios to be accessible + */ + #ifndef LIB_libnagios_h__ #define LIB_libnagios_h__ #include "pqueue.h" Modified: nagioscore/trunk/lib/pqueue.h =================================================================== --- nagioscore/trunk/lib/pqueue.h 2012-07-05 11:39:06 UTC (rev 2007) +++ nagioscore/trunk/lib/pqueue.h 2012-07-05 11:39:30 UTC (rev 2008) @@ -57,15 +57,15 @@ /** the priority queue handle */ typedef struct pqueue_t { - unsigned int size; - unsigned int avail; - unsigned int step; - pqueue_cmp_pri_f cmppri; - pqueue_get_pri_f getpri; - pqueue_set_pri_f setpri; - pqueue_get_pos_f getpos; - pqueue_set_pos_f setpos; - void **d; + unsigned int size; /**< number of elements in this queue */ + unsigned int avail; /**< slots available in this queue */ + unsigned int step; /**< growth stepping setting */ + pqueue_cmp_pri_f cmppri; /**< callback to compare nodes */ + pqueue_get_pri_f getpri; /**< callback to get priority of a node */ + pqueue_set_pri_f setpri; /**< callback to set priority of a node */ + pqueue_get_pos_f getpos; /**< callback to get position of a node */ + pqueue_set_pos_f setpos; /**< callback to set position of a node */ + void **d; /**< The actualy queue in binary heap form */ } pqueue_t; @@ -75,12 +75,14 @@ * @param n the initial estimate of the number of queue items for which memory * should be preallocated * @param cmppri The callback function to run to compare two elements - * This callback should return 0 for matching and - * @param pri the callback function to run to assign a score to a element - * @param get the callback function to get the current element's position - * @param set the callback function to set the current element's position + * This callback should return 0 for 'lower' and non-zero + * for 'higher', or vice versa if reverse priority is desired + * @param setpri the callback function to run to assign a score to an element + * @param getpri the callback function to run to set a score to an element + * @param getpos the callback function to get the current element's position + * @param setpos the callback function to set the current element's position * - * @Return the handle or NULL for insufficent memory + * @return the handle or NULL for insufficent memory */ pqueue_t * pqueue_init(unsigned int n, @@ -117,7 +119,7 @@ /** * move an existing entry to a different priority * @param q the queue - * @param old the old priority + * @param new_pri the new priority * @param d the entry */ void @@ -128,7 +130,7 @@ /** * pop the highest-ranking item from the queue. - * @param p the queue + * @param q the queue * @return NULL on error, otherwise the entry */ void *pqueue_pop(pqueue_t *q); @@ -136,7 +138,7 @@ /** * remove an item from the queue. - * @param p the queue + * @param q the queue * @param d the entry * @return 0 on success */ @@ -146,7 +148,6 @@ /** * access highest-ranking item without removing it. * @param q the queue - * @param d the entry * @return NULL on error, otherwise the entry */ void *pqueue_peek(pqueue_t *q); Modified: nagioscore/trunk/lib/squeue.h =================================================================== --- nagioscore/trunk/lib/squeue.h 2012-07-05 11:39:06 UTC (rev 2007) +++ nagioscore/trunk/lib/squeue.h 2012-07-05 11:39:30 UTC (rev 2008) @@ -1,3 +1,14 @@ +/** + * @file squeue.h + * @brief Scheduling queue function declarations + * + * This library is based on the pqueue api, which implements a + * priority queue based on a binary heap, providing O(lg n) times + * for insert() and remove(), and O(1) time for peek(). + * @note There is no "find". Callers must maintain pointers to their + * scheduled events if they wish to be able to remove them. + * @{ + */ #ifndef INCLUDE_squeue_h__ #define INCLUDE_squeue_h__ #include <sys/time.h> @@ -42,14 +53,14 @@ * with few scheduled items in that timeframe you'd be better off * using a more narrow horizon. * - * @param horizon The desired event horizon + * @param size Hint about how large this queue will get * @return A pointer to a scheduling queue */ extern squeue_t *squeue_create(unsigned int size); /** * Destroys a scheduling queue completely - * @param[in] sq The doomed queue + * @param[in] q The doomed queue * @param[in] flags Flags determining the the level of destruction */ extern void squeue_destroy(squeue_t *q, int flags); @@ -59,7 +70,7 @@ * It's up to the caller to keep the event pointer in case he/she * wants to remove the event from the queue later. * - * @param sq The scheduling queue to add to + * @param q The scheduling queue to add to * @param tv When this event should occur * @param data Pointer to any kind of data * @return The complete scheduled event @@ -70,7 +81,7 @@ * Adds an event to the scheduling queue. * See notes for squeue_add_tv() for details * - * @param sq The scheduling queue to add to + * @param q The scheduling queue to add to * @param when The unix timestamp when this event is to occur * @param data Pointer to any kind of data * @return The complete scheduled event @@ -81,7 +92,7 @@ * Adds an event to the scheduling queue with millisecond precision * See notes on squeue_add_tv() for details * - * @param[in] sq The scheduling queue to add to + * @param[in] q The scheduling queue to add to * @param[in] when Unix timestamp when this event should occur * @param[in] usec Millisecond of above this event should occur * @param[in] data Pointer to any kind of data @@ -93,7 +104,7 @@ * Adds an event to the scheduling queue with millisecond precision * See notes on squeue_add_tv() for details * - * @param[in] sq The scheduling queue to add to + * @param[in] q The scheduling queue to add to * @param[in] when Unix timestamp when this event should occur * @param[in] msec Millisecond of above this event should occur * @param[in] data Pointer to any kind of data @@ -105,7 +116,7 @@ * Returns the data of the next scheduled event from the scheduling * queue without removing it from the queue. * - * @param sq The scheduling queue to peek into + * @param q The scheduling queue to peek into */ extern void *squeue_peek(squeue_t *q); @@ -115,7 +126,7 @@ * This is equivalent to squeue_peek() + squeue_pop() * @note This causes the squeue_event to be free()'d. * - * @param sq The scheduling queue to pop from + * @param q The scheduling queue to pop from */ extern void *squeue_pop(squeue_t *q); @@ -136,3 +147,4 @@ */ extern unsigned int squeue_size(squeue_t *q); #endif +/** @} */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-05 11:40:06
|
Revision: 2009 http://nagios.svn.sourceforge.net/nagios/?rev=2009&view=rev Author: ageric Date: 2012-07-05 11:39:55 +0000 (Thu, 05 Jul 2012) Log Message: ----------- libnagios: Add runcmd and worker libraries Although the worker library isn't, strictly speaking, a library as much as it is a toolkit and some application logic, it's nifty to have for creating remote workers or as an example on how to use the other parts of libnagios. The runcmd library is inherited from the plugins project and might need to be refactored for performance later. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/Makefile.in nagioscore/trunk/lib/libnagios.h Added Paths: ----------- nagioscore/trunk/lib/runcmd.c nagioscore/trunk/lib/runcmd.h nagioscore/trunk/lib/worker.c nagioscore/trunk/lib/worker.h Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-07-05 11:39:30 UTC (rev 2008) +++ nagioscore/trunk/lib/Makefile.in 2012-07-05 11:39:55 UTC (rev 2009) @@ -9,7 +9,7 @@ SNPRINTF_O=@SNPRINTF_O@ TESTED_SRC_C := squeue.c kvvec.c iocache.c iobroker.c -SRC_C := $(TESTED_SRC_C) pqueue.c +SRC_C := $(TESTED_SRC_C) pqueue.c runcmd.c worker.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) Modified: nagioscore/trunk/lib/libnagios.h =================================================================== --- nagioscore/trunk/lib/libnagios.h 2012-07-05 11:39:30 UTC (rev 2008) +++ nagioscore/trunk/lib/libnagios.h 2012-07-05 11:39:55 UTC (rev 2009) @@ -11,4 +11,6 @@ #include "kvvec.h" #include "iobroker.h" #include "iocache.h" +#include "runcmd.h" +#include "worker.h" #endif /* LIB_libnagios_h__ */ Added: nagioscore/trunk/lib/runcmd.c =================================================================== --- nagioscore/trunk/lib/runcmd.c (rev 0) +++ nagioscore/trunk/lib/runcmd.c 2012-07-05 11:39:55 UTC (rev 2009) @@ -0,0 +1,438 @@ +/* + * $Id: runcmd.c,v 1.3 2005/08/01 23:51:34 exon Exp $ + * + * A simple interface to executing programs from other programs, using an + * optimized and safe popen()-like implementation. It is considered safe + * in that no shell needs to be spawned and the environment passed to the + * execve()'d program is essentially empty. + * + * + * The code in this file is a derivative of popen.c which in turn was taken + * from "Advanced Programming for the Unix Environment" by W. Richard Stevens. + * + * Care has been taken to make sure the functions are async-safe. The one + * function which isn't is runcmd_init() which it doesn't make sense to + * call twice anyway, so the api as a whole should be considered async-safe. + * + */ + +#define NAGIOSPLUG_API_C 1 + +/* includes **/ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <errno.h> +#include "runcmd.h" + + +/** macros **/ +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif + +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +/* 4.3BSD Reno <signal.h> doesn't define SIG_ERR */ +#if defined(SIG_IGN) && !defined(SIG_ERR) +# define SIG_ERR ((Sigfunc *)-1) +#endif + +/* This variable must be global, since there's no way the caller + * can forcibly slay a dead or ungainly running program otherwise. + * Multithreading apps and plugins can initialize it (via runcmd_init()) + * in an async safe manner PRIOR to calling runcmd() for the first time. + * + * The check for initialized values is atomic and can + * occur in any number of threads simultaneously. */ +static pid_t *pids = NULL; + +/* If OPEN_MAX isn't defined, we try the sysconf syscall first. + * If that fails, we fall back to an educated guess which is accurate + * on Linux and some other systems. There's no guarantee that our guess is + * adequate and the program will die with SIGSEGV if it isn't and the + * upper boundary is breached. */ +#ifdef OPEN_MAX +# define maxfd OPEN_MAX +#else +# ifndef _SC_OPEN_MAX /* sysconf macro unavailable, so guess */ +# define maxfd 256 +# else +static int maxfd = 0; +# endif /* _SC_OPEN_MAX */ +#endif /* OPEN_MAX */ + + +/* yield the pid belonging to a particular file descriptor */ +pid_t runcmd_pid(int fd) +{ + if(!pids || fd >= maxfd || fd < 0) + return 0; + + return pids[fd]; +} + +/* + * Simple command parser which is still tolerably accurate for our + * simple needs. It might serve as a useful example on how to program + * a state-machine though. + * + * It's up to the caller to handle output redirection, job control, + * conditional statements, variable substitution, nested commands and + * function execution. We do mark such occasions with the return code + * though, which is to be interpreted as a bitfield with potentially + * multiple flags set. + */ +#define STATE_NONE 0 +#define STATE_WHITE (1 << 0) +#define STATE_INARG (1 << 1) +#define STATE_INSQ (1 << 2) +#define STATE_INDQ (1 << 3) +#define STATE_SPECIAL (1 << 4) +#define in_quotes (state & (STATE_INSQ | STATE_INDQ)) +#define is_state(s) (state == s) +#define set_state(s) (state = s) +#define have_state(s) ((state & s) == s) +#define add_state(s) (state |= s) +#define del_state(s) (state &= ~s) +#define add_ret(r) (ret |= r) +int runcmd_cmd2strv(const char *str, int *out_argc, char **out_argv) +{ + int arg = 0, i, a = 0; + int state, ret = 0; + size_t len; + char *argz; + + set_state(STATE_NONE); + len = strlen(str); + + argz = malloc(len + 10); + for (i = 0; i < len; i++) { + const char *p = &str[i]; + + switch (*p) { + case 0: + return ret; + + case ' ': case '\t': case '\r': case '\n': + if (is_state(STATE_INARG)) { + set_state(STATE_NONE); + argz[a++] = 0; + continue; + } + if (!in_quotes) + continue; + + break; + + case '\\': + i++; + break; + + case '\'': + if (have_state(STATE_INDQ)) + break; + if (have_state(STATE_INSQ)) { + del_state(STATE_INSQ); + continue; + } + + /* + * quotes can come inside arguments or + * at the start of them + */ + if (is_state(STATE_NONE) || is_state(STATE_INARG)) { + if (is_state(STATE_NONE)) { + /* starting a new argument */ + out_argv[arg++] = &argz[a]; + } + set_state(STATE_INSQ | STATE_INARG); + continue; + } + case '"': + if (have_state(STATE_INSQ)) + break; + if (have_state(STATE_INDQ)) { + del_state(STATE_INDQ); + continue; + } + if (is_state(STATE_NONE) || is_state(STATE_INARG)) { + if (is_state(STATE_NONE)) { + out_argv[arg++] = &argz[a]; + } + set_state(STATE_INDQ | STATE_INARG); + continue; + } + break; + + case '|': + if (!in_quotes) { + add_ret(CMD_HAS_REDIR); + } + break; + case '&': case ';': + if (!in_quotes) { + set_state(STATE_SPECIAL); + add_ret(CMD_HAS_JOBCONTROL); + if (i && str[i - 1] != *p) { + argz[a++] = 0; + out_argv[arg++] = &argz[a]; + } + } + break; + + case '`': + if (!in_quotes) { + add_ret(CMD_HAS_SUBCOMMAND); + } + break; + + case '(': + if (!in_quotes) { + add_ret(CMD_HAS_PAREN); + } + break; + + case '*': case '?': + if (!in_quotes) { + add_ret(CMD_HAS_WILDCARD); + } + + /* fallthrough */ + + default: + break; + } + + if (is_state(STATE_NONE)) { + set_state(STATE_INARG); + out_argv[arg++] = &argz[a]; + } + + /* by default we simply copy the byte */ + argz[a++] = str[i]; + } + + /* make sure we nul-terminate the last argument */ + argz[a++] = 0; + + if (have_state(STATE_INSQ)) + add_ret(CMD_HAS_UBSQ); + if (have_state(STATE_INDQ)) + add_ret(CMD_HAS_UBDQ); + + *out_argc = arg; + + return ret; +} + + +/* this function is NOT async-safe. It is exported so multithreaded + * plugins (or other apps) can call it prior to running any commands + * through this api and thus achieve async-safeness throughout the api */ +void runcmd_init(void) +{ +#if defined(RLIMIT_NOFILE) + if (!maxfd) { + struct rlimit rlim; + getrlimit(RLIMIT_NOFILE, &rlim); + maxfd = rlim.rlim_cur; + } +#elif !defined(OPEN_MAX) && !defined(IOV_MAX) && defined(_SC_OPEN_MAX) + if(!maxfd) { + if((maxfd = sysconf(_SC_OPEN_MAX)) < 0) { + /* possibly log or emit a warning here, since there's no + * guarantee that our guess at maxfd will be adequate */ + maxfd = 256; + } + } +#endif + + if (!pids) + pids = calloc(maxfd, sizeof(pid_t)); +} + + +/* Start running a command */ +int runcmd_open(const char *cmd, int *pfd, int *pfderr, char **env) +{ + char **argv = NULL; + int cmd2strv_errors, argc = 0; + size_t cmdlen; + pid_t pid; + + int i = 0; + + if(!pids) + runcmd_init(); + + /* if no command was passed, return with no error */ + if (!cmd || !*cmd) + return -1; + + cmdlen = strlen(cmd); + argv = calloc((cmdlen / 2) + 5, sizeof(char *)); + if (!argv) + return -1; + + cmd2strv_errors = runcmd_cmd2strv(cmd, &argc, argv); + if (cmd2strv_errors) { + /* + * if there are complications, we fall back to running + * the command via the shell + */ + free(argv[0]); + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = strdup(cmd); + if (!argv[2]) { + free(argv); + return -1; + } + argv[3] = NULL; + } + + if (pipe(pfd) < 0) { + if (!cmd2strv_errors) + free(argv[0]); + else + free(argv[2]); + free(argv); + return -1; + } + if (pipe(pfderr) < 0) { + if (!cmd2strv_errors) + free(argv[0]); + else + free(argv[2]); + free(argv); + close(pfd[0]); + close(pfd[1]); + return -1; + } + pid = fork(); + if (pid < 0) { + if (!cmd2strv_errors) + free(argv[0]); + else + free(argv[2]); + free(argv); + close(pfd[0]); + close(pfd[1]); + close(pfderr[0]); + close(pfderr[1]); + return -1; /* errno set by the failing function */ + } + + /* child runs excevp() and _exit. */ + if (pid == 0) { + close (pfd[0]); + if (pfd[1] != STDOUT_FILENO) { + dup2 (pfd[1], STDOUT_FILENO); + close (pfd[1]); + } + close (pfderr[0]); + if (pfderr[1] != STDERR_FILENO) { + dup2 (pfderr[1], STDERR_FILENO); + close (pfderr[1]); + } + + /* close all descriptors in pids[] + * This is executed in a separate address space (pure child), + * so we don't have to worry about async safety */ + for (i = 0; i < maxfd; i++) + if(pids[i] > 0) + close (i); + + i = execvp(argv[0], argv); + fprintf(stderr, "execvpe() failed. errno is %d: %s\n", errno, strerror(errno)); + if (!cmd2strv_errors) + free(argv[0]); + else + free(argv[2]); + exit (errno); + } + + /* parent picks up execution here */ + /* + * close childs file descriptors in our address space and + * release the memory we used that won't get passed to the + * caller. + */ + close(pfd[1]); + close(pfderr[1]); + if (!cmd2strv_errors) + free(argv[0]); + else + free(argv[2]); + free(argv); + + /* tag our file's entry in the pid-list and return it */ + pids[pfd[0]] = pid; + + return pfd[0]; +} + + +int runcmd_close(int fd) +{ + int status; + pid_t pid; + + /* make sure this fd was opened by runcmd_open() */ + if(fd < 0 || fd > maxfd || !pids || (pid = pids[fd]) == 0) + return -1; + + pids[fd] = 0; + if (close(fd) == -1) + return -1; + + /* EINTR is ok (sort of), everything else is bad */ + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + return -1; + + /* return child's termination status */ + return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; +} + + +int runcmd_try_close(int fd, int *status, int sig) +{ + pid_t pid; + int result; + + /* make sure this fd was opened by popen() */ + if(fd < 0 || fd > maxfd || !pids || !pids[fd]) + return -1; + + pid = pids[fd]; + while((result = waitpid(pid, status, WNOHANG)) != pid) { + if(!result) return 0; + if(result == -1) { + switch(errno) { + case EINTR: + continue; + case EINVAL: + return -1; + case ECHILD: + if(sig) { + result = kill(pid, sig); + sig = 0; + continue; + } + else return -1; + } /* switch */ + } + } + + pids[fd] = 0; + close(fd); + return result; +} Added: nagioscore/trunk/lib/runcmd.h =================================================================== --- nagioscore/trunk/lib/runcmd.h (rev 0) +++ nagioscore/trunk/lib/runcmd.h 2012-07-05 11:39:55 UTC (rev 2009) @@ -0,0 +1,76 @@ +#ifndef INCLUDE_runcmd_h__ +#define INCLUDE_runcmd_h__ + +/** + * @file runcmd.h + * @brief runcmd library function declarations + * + * @note This is inherited from the nagiosplugins project, although + * I (AE) wrote the original code, and it might need of refactoring + * for performance later. + * @{ + */ + +/** Return code bitflags for runcmd_cmd2strv() */ +#define CMD_HAS_REDIR (1 << 0) /**< I/O redirection */ +#define CMD_HAS_SUBCOMMAND (1 << 1) /**< subcommands present */ +#define CMD_HAS_PAREN (1 << 2) /**< parentheses present in command */ +#define CMD_HAS_JOBCONTROL (1 << 3) /**< job control stuff present */ +#define CMD_HAS_UBSQ (1 << 4) /**< unbalanced single quotes */ +#define CMD_HAS_UBDQ (1 << 5) /**< unbalanced double quotes */ +#define CMD_HAS_WILDCARD (1 << 6) /**< wildcards present */ + +/** + * Initialize the runcmd library. + * + * Only multi-threaded programs that might launch the first external + * program from multiple threads simultaneously need to bother with + * this. + */ +extern void runcmd_init(void); + +/** + * Return pid of a command with a specific file descriptor + * @param[in] fd stdout filedescriptor of the child to get pid from + * @return pid of the child, or 0 on errors + */ +extern pid_t runcmd_pid(int fd); + +/** + * Start a command from a command string + * @param[in] cmdstring The command to launch + * @param[out] pfd Child's stdout filedescriptor + * @param[out] pfderr Child's stderr filedescriptor + * @param[in] env Currently ignored for portability + */ +extern int runcmd_open(const char *cmdstring, int *pfd, int *pfderr, char **env) + __attribute__((__nonnull__(1, 2, 3))); + +/** + * Close a command and return its exit status + * @note Don't use this. It's a retarded way to reap children suitable + * only for launching a one-shot program. + * + * @param[in] fd The child's stdout filedescriptor + * @return exit-status of the child, or -1 in case of errors + */ +extern int runcmd_close(int fd); + +/** + * Convert a string to a vector of arguments like a shell would + * @note This might have bugs and is only tested to behave similar + * to how /bin/sh does things. For csh or other non bash-ish shells + * there are no guarantees. + * @note The out_argv array has to be large enough to hold all strings + * found in the command. + * @param[in] str The string to convert to an argument vector + * @param[out] out_argc The number of arguments found + * @param[out] out_argv The argument vector + * @return 0 on (great) success, or a bitmask of failure-codes + * representing f.e. unclosed quotes, job control or output redirection. + * See the CMD_HAS_* and their ilk to find out about the flag. + */ +extern int runcmd_cmd2strv(const char *str, int *out_argc, char **out_argv); + +#endif /* INCLUDE_runcmd_h__ */ +/** @} */ Added: nagioscore/trunk/lib/worker.c =================================================================== --- nagioscore/trunk/lib/worker.c (rev 0) +++ nagioscore/trunk/lib/worker.c 2012-07-05 11:39:55 UTC (rev 2009) @@ -0,0 +1,697 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include "libnagios.h" + +typedef struct iobuf { + int fd; + unsigned int len; + char *buf; +} iobuf; + +typedef struct child_process { + unsigned int id, timeout; + char *cmd; + pid_t pid; + int ret; + struct timeval start; + struct timeval stop; + float runtime; + struct rusage rusage; + iobuf outstd; + iobuf outerr; + struct kvvec *request; + squeue_event *sq_event; +} child_process; + +static iobroker_set *iobs; +static squeue_t *sq; +static unsigned int started, running_jobs; +static int master_sd; +static int parent_pid; + +static void worker_die(const char *msg) +{ + perror(msg); + exit(EXIT_FAILURE); +} + +/* + * contains all information sent in a particular request + */ +struct request { + char *cmd; + int when; + char **env; + struct kvvec *request, *response; +}; + +static void exit_worker(void) +{ + /* + * XXX: check to make sure we have no children running. If + * we do, kill 'em all before we go home. + */ + exit(EXIT_SUCCESS); +} + +/* + * write a log message to master. + * Note that this will break if we change delimiters someday, + * but avoids doing several extra malloc()+free() for this + * pretty simple case. + */ +static void wlog(const char *fmt, ...) +{ + va_list ap; + static char lmsg[8192] = "log="; + int len = 4, to_send; + + va_start(ap, fmt); + len = vsnprintf(&lmsg[len], sizeof(lmsg) - 7, fmt, ap); + va_end(ap); + if (len < 0 || len >= sizeof(lmsg)) + return; + + len += 4; /* log= */ + + /* add delimiter and send it. 1 extra as kv pair separator */ + to_send = len + MSG_DELIM_LEN_SEND + 1; + memset(&lmsg[len], 0, to_send); + if (write(master_sd, lmsg, to_send) < 0) { + if (errno == EPIPE) { + /* master has died or abandoned us, so exit */ + exit_worker(); + } + } +} + +static void job_error(child_process *cp, struct kvvec *kvv, const char *fmt, ...) +{ + char msg[4096]; + int len; + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(msg, sizeof(msg) - 1, fmt, ap); + va_end(ap); + if (cp) { + kvvec_addkv(kvv, "job_id", (char *)mkstr("%d", cp->id)); + } + kvvec_addkv_wlen(kvv, "error_msg", 5, msg, len); + send_kvvec(master_sd, kvv); + kvvec_destroy(kvv, 0); +} + +int tv_delta_msec(const struct timeval *start, const struct timeval *stop) +{ + int msecs; + unsigned long usecs = 0; + + msecs = (stop->tv_sec - start->tv_sec) * 1000; + if (stop->tv_usec < start->tv_usec) { + msecs -= 1000; + usecs += 1000000; + } + usecs += stop->tv_usec - start->tv_usec; + msecs += (usecs / 1000); + + return msecs; +} + +static float tv_delta_f(const struct timeval *start, const struct timeval *stop) +{ +#define DIVIDER 1000000 + float ret; + unsigned long usecs, stop_usec; + + ret = stop->tv_sec - start->tv_sec; + stop_usec = stop->tv_usec; + if (stop_usec < start->tv_usec) { + ret -= 1.0; + stop_usec += DIVIDER; + } + usecs = stop_usec - start->tv_usec; + + ret += (float)((float)usecs / DIVIDER); + return ret; +} + +#define MKSTR_BUFS 256 /* should be plenty */ +const char *mkstr(const char *fmt, ...) +{ + static char buf[MKSTR_BUFS][32]; /* 8k statically on the stack */ + static int slot = 0; + char *ret; + va_list ap; + + ret = buf[slot++ & (MKSTR_BUFS - 1)]; + va_start(ap, fmt); + vsnprintf(ret, sizeof(buf[0]), fmt, ap); + va_end(ap); + return ret; +} + +void send_kvvec(int sd, struct kvvec *kvv) +{ + int ret; + struct kvvec_buf *kvvb; + + /* + * key=value, separated by nul bytes and two nul's + * delimit one message from another. We need to cut + * one from MSG_DELIM_LEN here though, since nul is + * added unconditionally after each value as well + * and we'd otherwise run into an off-by-one when + * parsing the messages back into sensible order. + */ + kvvb = kvvec2buf(kvv, KV_SEP, PAIR_SEP, MSG_DELIM_LEN_SEND); + if (!kvvb) { + /* + * XXX: do *something* sensible here to let the + * master know we failed, although the most likely + * reason is OOM, in which case the OOM-slayer will + * probably kill us sooner or later. + */ + return; + } + + /* use bufsize here, as it gets us the nul string delimiter */ + ret = write(sd, kvvb->buf, kvvb->bufsize); + if (ret < 0) { + if (errno == EPIPE) { + /* + * master has crashed or abandoned us, so we die + * with what grace we can. + */ + exit_worker(); + } + } + free(kvvb->buf); + free(kvvb); +} + +#define kvvec_add_long(kvv, key, value) \ + do { \ + char *buf = (char *)mkstr("%ld", value); \ + kvvec_addkv_wlen(kvv, key, sizeof(key) - 1, buf, strlen(buf)); \ + } while (0) + +#define kvvec_add_tv(kvv, key, value) \ + do { \ + char *buf = (char *)mkstr("%ld.%06ld", value.tv_sec, value.tv_usec); \ + kvvec_addkv_wlen(kvv, key, sizeof(key) - 1, buf, strlen(buf)); \ + } while (0) + +static int finish_job(child_process *cp, int reason) +{ + struct kvvec *resp; + struct rusage *ru = &cp->rusage; + int i; + + resp = kvvec_init(12 + cp->request->kv_pairs); /* how many key/value pairs do we need? */ + + gettimeofday(&cp->stop, NULL); + + if (running_jobs != squeue_size(sq)) { + wlog("running_jobs(%d) != squeue_size(sq) (%d)\n", + running_jobs, squeue_size(sq)); + wlog("started: %d; running: %d; finished: %d\n", + started, running_jobs, started - running_jobs); + } + + /* + * we must remove the job's timeout ticker, + * or we'll end up accessing an already free()'d + * pointer, or the pointer to a different child. + */ + squeue_remove(sq, cp->sq_event); + + /* get rid of still open filedescriptors */ + if (cp->outstd.fd != -1) + iobroker_close(iobs, cp->outstd.fd); + if (cp->outerr.fd != -1) + iobroker_close(iobs, cp->outerr.fd); + + cp->runtime = tv_delta_f(&cp->start, &cp->stop); + + /* + * Now build the return message. + * First comes the request, minus environment variables + */ + for (i = 0; i < cp->request->kv_pairs; i++) { + struct key_value *kv = &cp->request->kv[i]; + /* skip environment macros */ + if (kv->key_len == 3 && !strcmp(kv->key, "env")) { + continue; + } + kvvec_addkv_wlen(resp, kv->key, kv->key_len, kv->value, kv->value_len); + } + kvvec_addkv(resp, "wait_status", (char *)mkstr("%d", cp->ret)); + kvvec_addkv_wlen(resp, "outstd", 6, cp->outstd.buf, cp->outstd.len); + kvvec_addkv_wlen(resp, "outerr", 6, cp->outerr.buf, cp->outerr.len); + kvvec_add_tv(resp, "start", cp->start); + kvvec_add_tv(resp, "stop", cp->stop); + kvvec_addkv(resp, "runtime", (char *)mkstr("%f", cp->runtime)); + if (!reason) { + /* child exited nicely */ + kvvec_addkv(resp, "exited_ok", "1"); + kvvec_add_tv(resp, "ru_utime", ru->ru_utime); + kvvec_add_tv(resp, "ru_stime", ru->ru_stime); + kvvec_add_long(resp, "ru_minflt", ru->ru_minflt); + kvvec_add_long(resp, "ru_majflt", ru->ru_majflt); + kvvec_add_long(resp, "ru_nswap", ru->ru_nswap); + kvvec_add_long(resp, "ru_inblock", ru->ru_inblock); + kvvec_add_long(resp, "ru_oublock", ru->ru_oublock); + kvvec_add_long(resp, "ru_nsignals", ru->ru_nsignals); + } else { + /* some error happened */ + kvvec_addkv(resp, "exited_ok", "0"); + kvvec_addkv(resp, "error_code", (char *)mkstr("%d", reason)); + } + send_kvvec(master_sd, resp); + + /* + * we mustn't free() the key/value pairs here, as they're all + * stack-allocated or located in the request thing + */ + kvvec_destroy(resp, 0); + + running_jobs--; + if (cp->outstd.buf) { + free(cp->outstd.buf); + cp->outstd.buf = NULL; + } + if (cp->outerr.buf) { + free(cp->outerr.buf); + cp->outerr.buf = NULL; + } + + kvvec_destroy(cp->request, KVVEC_FREE_ALL); + free(cp->cmd); + free(cp); + + return 0; +} + +static int check_completion(child_process *cp, int flags) +{ + int result, status; + time_t max_time; + + if (!cp || !cp->pid) { + return 0; + } + + max_time = time(NULL) + 1; + + /* + * we mustn't let EINTR interrupt us, since it could well + * be a SIGCHLD from the properly exiting process doing it + */ + do { + errno = 0; + result = wait4(cp->pid, &status, flags, &cp->rusage); + } while (result == -1 && errno == EINTR && time(NULL) < max_time); + + if (result == cp->pid) { + cp->ret = status; + finish_job(cp, 0); + return 0; + } + + if (errno == ECHILD) { + cp->ret = status; + finish_job(cp, errno); + } + + return -errno; +} + +static void kill_job(child_process *cp, int reason) +{ + int ret; + struct rusage ru; + + /* brutal but efficient */ + ret = kill(cp->pid, SIGKILL); + if (ret < 0) { + if (errno == ESRCH) { + finish_job(cp, reason); + return; + } + wlog("kill(%d, SIGKILL) failed: %s\n", cp->pid, strerror(errno)); + } + + ret = wait4(cp->pid, &cp->ret, 0, &ru); + finish_job(cp, reason); + +#ifdef PLAY_NICE_IN_kill_job + int i, sig = SIGTERM; + + pid = cp->pid; + + for (i = 0; i < 2; i++) { + /* check one last time if the job is done */ + ret = check_completion(cp, WNOHANG); + if (!ret || ret == -ECHILD) { + /* check_completion ran finish_job() */ + return; + } + + /* not done, so signal it. SIGTERM first and check again */ + errno = 0; + ret = kill(pid, sig); + if (ret < 0) { + finish_job(cp, -errno); + } + sig = SIGKILL; + check_completion(cp, WNOHANG); + if (ret < 0) { + finish_job(cp, errno); + } + usleep(50000); + } +#endif /* PLAY_NICE_IN_kill_job */ +} + +static void gather_output(child_process *cp, iobuf *io) +{ + iobuf *other_io; + + other_io = io == &cp->outstd ? &cp->outerr : &cp->outstd; + + for (;;) { + char buf[4096]; + int rd; + + rd = read(io->fd, buf, sizeof(buf)); + if (rd < 0) { + if (errno == EINTR) + continue; + /* XXX: handle the error somehow */ + check_completion(cp, WNOHANG); + } + + if (rd) { + /* we read some data */ + io->buf = realloc(io->buf, rd + io->len + 1); + memcpy(&io->buf[io->len], buf, rd); + io->len += rd; + io->buf[io->len] = '\0'; + } else { + iobroker_close(iobs, io->fd); + io->fd = -1; + if (other_io->fd < 0) { + check_completion(cp, 0); + } else { + check_completion(cp, WNOHANG); + } + } + break; + } +} + + +static int stderr_handler(int fd, int events, void *cp_) +{ + child_process *cp = (child_process *)cp_; + gather_output(cp, &cp->outerr); + return 0; +} + +static int stdout_handler(int fd, int events, void *cp_) +{ + child_process *cp = (child_process *)cp_; + gather_output(cp, &cp->outstd); + return 0; +} + +static int fd_start_cmd(child_process *cp) +{ + int pfd[2], pfderr[2]; + + cp->outstd.fd = runcmd_open(cp->cmd, pfd, pfderr, NULL); + if (cp->outstd.fd == -1) { + return -1; + } + gettimeofday(&cp->start, NULL); + + cp->outerr.fd = pfderr[0]; + cp->pid = runcmd_pid(cp->outstd.fd); + iobroker_register(iobs, cp->outstd.fd, cp, stdout_handler); + iobroker_register(iobs, cp->outerr.fd, cp, stderr_handler); + + return 0; +} + +static iocache *ioc; + +child_process *parse_command_kvvec(struct kvvec *kvv) +{ + int i; + child_process *cp; + + /* get this command's struct and insert it at the top of the list */ + cp = calloc(1, sizeof(*cp)); + if (!cp) { + wlog("Failed to calloc() a child_process struct"); + return NULL; + } + + /* + * we must copy from the vector, since it points to data + * found in the iocache where we read the command, which will + * be overwritten when we receive one next + */ + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv = &kvv->kv[i]; + char *key = kv->key; + char *value = kv->value; + char *endptr; + + if (!strcmp(key, "command")) { + cp->cmd = strdup(value); + continue; + } + if (!strcmp(key, "job_id")) { + cp->id = (unsigned int)strtoul(value, &endptr, 0); + continue; + } + if (!strcmp(key, "timeout")) { + cp->timeout = (unsigned int)strtoul(value, &endptr, 0); + continue; + } + } + + /* jobs without a timeout get a default of 60 seconds. */ + if (!cp->timeout) { + cp->timeout = 60; + } + + return cp; +} + +static void spawn_job(struct kvvec *kvv) +{ + int result, i; + child_process *cp; + + if (!kvv) { + wlog("Received NULL command key/value vector. Bug in iocache.c or kvvec.c?"); + return; + } + + wlog("Spawning command from kvvec with %d entries", kvv->kv_pairs); + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv = &kvv->kv[i]; + wlog("%d: %s=%s", i, kv->key, kv->value); + } + + cp = parse_command_kvvec(kvv); + if (!cp) { + job_error(NULL, kvv, "Failed to parse worker-command"); + return; + } + if (!cp->cmd) { + job_error(cp, kvv, "Failed to parse commandline. Ignoring job %u", cp->id); + return; + } + + result = fd_start_cmd(cp); + if (result < 0) { + job_error(cp, kvv, "Failed to start child"); + return; + } + + started++; + running_jobs++; + cp->request = kvv; + cp->sq_event = squeue_add(sq, cp->timeout + time(NULL), cp); +} + +static int receive_command(int sd, int events, void *discard) +{ + int ioc_ret; + char *buf; + unsigned long size; + + if (!ioc) { + ioc = iocache_create(512 * 1024); + } + ioc_ret = iocache_read(ioc, sd); + + /* master closed the connection, so we exit */ + if (ioc_ret == 0) { + iobroker_close(iobs, sd); + exit_worker(); + } + if (ioc_ret < 0) { + /* XXX: handle this somehow */ + } + +#if 0 + /* debug-volley */ + buf = iocache_use_size(ioc, ioc_ret); + write(master_sd, buf, ioc_ret); + return 0; +#endif + /* + * now loop over all inbound messages in the iocache. + * Since KV_TERMINATOR is a nul-byte, they're separated by 3 nuls + */ + while ((buf = iocache_use_delim(ioc, MSG_DELIM, MSG_DELIM_LEN_RECV, &size))) { + struct kvvec *kvv; + kvv = buf2kvvec(buf, (unsigned int)size, KV_SEP, PAIR_SEP); + if (kvv) + spawn_job(kvv); + } + + return 0; +} + + +static void enter_worker(int sd) +{ + /* created with socketpair(), usually */ + master_sd = sd; + parent_pid = getppid(); + (void)chdir("/tmp"); + (void)chdir("nagios-workers"); + + if (setpgid(0, 0)) { + /* XXX: handle error somehow, or maybe just ignore it */ + } + + /* we need to catch child signals the default way */ + signal(SIGCHLD, SIG_DFL); + + fcntl(fileno(stdout), F_SETFD, FD_CLOEXEC); + fcntl(fileno(stderr), F_SETFD, FD_CLOEXEC); + fcntl(master_sd, F_SETFD, FD_CLOEXEC); + iobs = iobroker_create(); + if (!iobs) { + /* XXX: handle this a bit better */ + worker_die("Worker failed to create io broker socket set"); + } + + /* + * Create a modest scheduling queue that will be + * more than enough for our needs + */ + sq = squeue_create(1024); + + iobroker_register(iobs, master_sd, NULL, receive_command); + while (iobroker_get_num_fds(iobs) > 0) { + int poll_time = -1; + + /* check for timed out jobs */ + for (;;) { + child_process *cp; + struct timeval now, tmo; + + /* stop when scheduling queue is empty */ + cp = (child_process *)squeue_peek(sq); + if (!cp) + break; + + tmo.tv_usec = cp->start.tv_usec; + tmo.tv_sec = cp->start.tv_sec + cp->timeout; + gettimeofday(&now, NULL); + poll_time = tv_delta_msec(&now, &tmo); + /* + * A little extra takes care of rounding errors and + * ensures we never kill a job before it times out. + * 5 milliseconds is enough to take care of that. + */ + poll_time += 5; + if (poll_time > 0) + break; + + /* this job timed out, so kill it */ + wlog("job with pid %d timed out. Killing it", cp->pid); + kill_job(cp, ETIME); + } + + iobroker_poll(iobs, poll_time); + + /* + * if our parent goes away we can't really do anything + * sensible at all, so let's just break out and exit + */ + if (kill(parent_pid, 0) < 0 && errno == ESRCH) { + break; + } + } + + printf("Fell out of the event loop. what the lols?\n"); + /* we exit when the master shuts us down */ + exit(EXIT_SUCCESS); +} + +struct worker_process *spawn_worker(void (*init_func)(void *), void *init_arg) +{ + int sv[2]; + int pid; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) + return NULL; + + pid = fork(); + if (pid < 0) + return NULL; + + /* parent leaves the child */ + if (pid) { + worker_process *worker = calloc(1, sizeof(worker_process)); + if (!worker) { + kill(SIGKILL, pid); + return NULL; + } + close(sv[1]); + worker->sd = sv[0]; + worker->pid = pid; + worker->ioc = iocache_create(1 * 1024 * 1024); + + /* 1 socket for master, 2 fd's for each child */ + worker->max_jobs = (iobroker_max_usable_fds() - 1) / 2; + worker->jobs = calloc(worker->max_jobs, sizeof(worker_job *)); + + return worker; + } + + if (init_func) { + init_func(init_arg); + } + /* child closes parent's end of socket and gets busy */ + close(sv[0]); + enter_worker(sv[1]); + + /* not reached, ever */ + exit(EXIT_FAILURE); +} Added: nagioscore/trunk/lib/worker.h =================================================================== --- nagioscore/trunk/lib/worker.h (rev 0) +++ nagioscore/trunk/lib/worker.h 2012-07-05 11:39:55 UTC (rev 2009) @@ -0,0 +1,89 @@ +#ifndef INCLUDE_worker_h__ +#define INCLUDE_worker_h__ +#include <errno.h> +#include <sys/socket.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#include "libnagios.h" + +/** + * @file worker.h + * @brief Worker implementation along with various helpers + * + * This code isn't really in the "library" category, but it's tucked + * in here to provide a good resource for writing remote workers and + * as an example on how to use the API's found here. + */ + +/* + * Since PAIR_SEP is a nul byte, and MSG_DELIM is all nuls, we must + * take care to use different MSG_DELIM_LEN depending on if we're + * sending or receiving. Change this if PAIR_SEP alters. + */ +#define MSG_DELIM "\0\0\0" /**< message limiter */ +#define MSG_DELIM_LEN (sizeof(MSG_DELIM) - 1) /**< message delimiter length */ +#define MSG_DELIM_LEN_SEND (MSG_DELIM_LEN - 1) /**< msg delim len when sendin */ +#define MSG_DELIM_LEN_RECV (MSG_DELIM_LEN) /**< msg delimm len when receivin */ +#define PAIR_SEP 0 /**< pair separator for buf2kvvec() and kvvec2buf() */ +#define KV_SEP '=' /**< key/value separator for buf2kvvec() and kvvec2buf() */ + +/** Worker job data */ +typedef struct worker_job { + int id; /**< job id */ + int type; /**< internal only */ + time_t timeout; /**< timeout, in absolute time */ + char *command; /**< command string for this job */ + void *arg; /**< any random argument */ +} worker_job; + +/** A worker process as seen from its controller */ +typedef struct worker_process { + int sd; /**< communication socket */ + pid_t pid; /**< pid */ + int max_jobs; /**< Max number of jobs we can handle */ + int jobs_running; /**< jobs running */ + int jobs_started; /**< jobs started */ + struct timeval start; /**< worker start time */ + iocache *ioc; /**< iocache for reading from worker */ + worker_job **jobs; /**< array of jobs */ + int job_index; /**< round-robin slot allocator (this wraps) */ + struct worker_process *prev_wp; /**< previous worker in list */ + struct worker_process *next_wp; /**< next worker in list */ +} worker_process; + +/** + * Spawn a worker process + * @param[in] init_func The initialization function for the worker + * @param[in] init_arg Initialization argument for the worker + * @return A worker process struct on success (for the parent). Null on errors + */ +extern worker_process *spawn_worker(void (init_func)(void *), void *init_arg); + +/** + * Send a key/value vector as a bytestream through a socket + * @param[in] sd The socket descriptor to send to + * @param kvv The key/value vector to send + */ +extern void send_kvvec(int sd, struct kvvec *kvv); + +/** + * Create a short-lived string in stack-allocated memory + * The number and size of strings is limited (currently to 256 strings of + * 32 bytes each), so beware and use this sensibly. Intended for + * number-to-string conversion. + * @param[in] fmt The format string + * @return A pointer to the formatted string on success. Undefined on errors + */ +extern const char *mkstr(const char *fmt, ...); + +/** + * Calculate the millisecond delta between two timeval structs + * @param[in] start The start time + * @param[in] stop The stop time + * @return The millisecond delta between the two structs + */ +extern int tv_delta_msec(const struct timeval *start, const struct timeval *stop); +#endif /* INCLUDE_worker_h__ */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-07-05 11:40:20
|
Revision: 2010 http://nagios.svn.sourceforge.net/nagios/?rev=2010&view=rev Author: ageric Date: 2012-07-05 11:40:14 +0000 (Thu, 05 Jul 2012) Log Message: ----------- Add the wproc program This small program demonstrates the basics of how workers work and is quite useful when testing the worker process parts of the nagios library. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/.gitignore Added Paths: ----------- nagioscore/trunk/lib/wproc.c Modified: nagioscore/trunk/lib/.gitignore =================================================================== --- nagioscore/trunk/lib/.gitignore 2012-07-05 11:39:55 UTC (rev 2009) +++ nagioscore/trunk/lib/.gitignore 2012-07-05 11:40:14 UTC (rev 2010) @@ -2,3 +2,4 @@ test-kvvec test-iocache test-iobroker +wproc Added: nagioscore/trunk/lib/wproc.c =================================================================== --- nagioscore/trunk/lib/wproc.c (rev 0) +++ nagioscore/trunk/lib/wproc.c 2012-07-05 11:40:14 UTC (rev 2010) @@ -0,0 +1,192 @@ +/* + * Simple test-program to try multiplexing running other programs + * through the worker process layer. + */ + +#include <time.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include "worker.h" + +/* we can't handle packets larger than 64MiB */ +#define MAX_IOCACHE_SIZE (64 * 1024 * 1024) +static int sigreceived; +static iobroker_set *iobs; + +static void die(const char *msg) +{ + perror(msg); + exit(EXIT_FAILURE); +} + +static void sighandler(int sig) +{ + sigreceived = sig; + printf("%d: caught sig %d (%s)\n", getpid(), sig, strsignal(sig)); +} + +static void child_exited(int sig) +{ + struct rusage ru; + int status, result; + + result = wait3(&status, 0, &ru); + printf("wait3() status: %d; return %d: %s\n", + status, result, strerror(errno)); + if (WIFEXITED(status)) { + printf("Child with pid %d exited normally\n", result); + } + if (WIFSIGNALED(status)) { + printf("Child caught signal %d\n", WTERMSIG(status)); + printf("Child did%s produce a core dump\n", WCOREDUMP(status) ? "" : " not"); + } + exit(1); +} + +static int print_input(int sd, int events, void *wp_) +{ + int ret, pkt = 0; + worker_process *wp = (worker_process *)wp_; + struct kvvec *kvv; + char *buf; + unsigned long tot_bytes = 0, size; + + /* + * if some command filled the buffer, we grow it and read some + * more until we hit the limit + * @todo Define a limit :p + */ + size = iocache_size(wp->ioc); + if (!iocache_capacity(wp->ioc)) { + if (iocache_size(wp->ioc) < MAX_IOCACHE_SIZE) { + /* double the size */ + iocache_grow(wp->ioc, iocache_size(wp->ioc)); + printf("Growing iocache for worker %d. sizes old/new %lu/%lu\n", + wp->pid, size, iocache_size(wp->ioc)); + } else { + printf("iocache_size() for worker %d is already at max\n", wp->pid); + } + } + + ret = iocache_read(wp->ioc, sd); + if (!ret) { + printf("Worker with pid %d seems to have crashed. Exiting\n", wp->pid); + exit(1); + } + if (ret < 0) { + printf("iocache_read() from worker %d returned %d: %m\n", wp->pid, ret); + return; + } + printf("read %d bytes from worker with pid %d::\n", ret, wp->pid); + while ((buf = iocache_use_delim(wp->ioc, MSG_DELIM, MSG_DELIM_LEN_RECV, &size))) { + int i; + tot_bytes += size + MSG_DELIM_LEN_RECV; + kvv = buf2kvvec(buf, (unsigned int)size, KV_SEP, PAIR_SEP); + if (!kvv) { + printf("main: Failed to parse buffer of size %d to key/value vector\n", size); + continue; + } + for (i = 0; i < kvv->kv_pairs; i++) { + struct key_value *kv = &kvv->kv[i]; + if (!i && memcmp(kv->key, buf, kv->key_len)) { + printf("### kv[0]->key doesn't match buf. error in kvvec?\n"); + } + printf("main: %2d.%02d: %s=%s\n", pkt, i, kv->key, kv->value); + } + pkt++; + kvvec_destroy(kvv, KVVEC_FREE_ALL); + } + + printf("iocache: available: %d; size: %lu; capacity: %ld\n", + iocache_available(wp->ioc), iocache_size(wp->ioc), iocache_capacity(wp->ioc)); + printf("Got %d packets in %ld bytes (ret: %d)\n", pkt, tot_bytes, ret); + + return 0; +} + +#define NWPS 1 +static worker_process *wps[NWPS]; +static int wp_index; + +static int send_command(int sd, int events, void *discard) +{ + char buf[8192]; + int ret; + worker_process *wp; + struct kvvec *kvv; + + ret = read(sd, buf, sizeof(buf)); + if (ret == 0) { + iobroker_close(iobs, sd); + return 0; + } + if (ret < 0) { + printf("main: Failed to read() from fd %d: %s", + sd, strerror(errno)); + } + + /* this happens when we're reading from stdin */ + buf[--ret] = 0; + + kvv = kvvec_init(5); + wp = wps[wp_index++ % NWPS]; + kvvec_addkv(kvv, "job_id", (char *)mkstr("%d", wp->job_index++)); + kvvec_addkv_wlen(kvv, "command", sizeof("command") - 1, buf, ret); + kvvec_addkv(kvv, "timeout", (char *)mkstr("%d", 10)); + printf("Sending kvvec with %d pairs to worker %d\n", kvv->kv_pairs, wp->pid); + send_kvvec(wp->sd, kvv); + kvvec_destroy(kvv, 0); + return 0; +} + +void print_some_crap(void *arg) +{ + char *str = (char *)arg; + + printf("%d: Argument passed: %s\n", getpid(), str); +} + +int main(int argc, char **argv) +{ + struct worker_process *wp; + int i; + + signal(SIGINT, sighandler); + signal(SIGPIPE, sighandler); + signal(SIGCHLD, child_exited); + + iobs = iobroker_create(); + if (!iobs) + die("Failed to create io broker set"); + + for (i = 0; i < NWPS; i++) { + wp = spawn_worker(print_some_crap, "lalala"); + if (!wp) { + die("Failed to spawn worker(s)\n"); + } + wps[i] = wp; + printf("Registering worker sd %d with io broker\n", wp->sd); + iobroker_register(iobs, wp->sd, wp, print_input); + } + + iobroker_register(iobs, fileno(stdin), NULL, send_command); + + /* get to work */ + while (!sigreceived && iobroker_get_num_fds(iobs)) { + iobroker_poll(iobs, -1); + } + + for (i = 0; i < NWPS; i++) { + kill(wps[i]->pid, SIGKILL); + } + + return 0; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-02 00:42:23
|
Revision: 2027 http://nagios.svn.sourceforge.net/nagios/?rev=2027&view=rev Author: ageric Date: 2012-08-02 00:42:17 +0000 (Thu, 02 Aug 2012) Log Message: ----------- lib/iocache: Add iocache_reset() and use it where sensible It's nifty to be able to reset an I/O cache structure without having to release and re-create all its memory. This patch allows us to do just that, and also implements it where sensible. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iocache.c nagioscore/trunk/lib/iocache.h Modified: nagioscore/trunk/lib/iocache.c =================================================================== --- nagioscore/trunk/lib/iocache.c 2012-08-02 00:42:00 UTC (rev 2026) +++ nagioscore/trunk/lib/iocache.c 2012-08-02 00:42:17 UTC (rev 2027) @@ -33,12 +33,23 @@ if (!ioc->ioc_offset) return; /* nothing to do */ - available = iocache_available(ioc); + /* if we're fully read, we only have to reset the counters */ + if (ioc->ioc_buflen <= ioc->ioc_offset) { + iocache_reset(ioc); + return; + } + available = ioc->ioc_buflen - ioc->ioc_offset; memmove(ioc->ioc_buf, ioc->ioc_buf + ioc->ioc_offset, available); ioc->ioc_offset = 0; ioc->ioc_buflen = available; } +void iocache_reset(iocache *ioc) +{ + if (ioc) + ioc->ioc_offset = ioc->ioc_buflen = 0; +} + int iocache_resize(iocache *ioc, unsigned long new_size) { char *buf; @@ -81,8 +92,8 @@ if (!ioc || !ioc->ioc_buf || !ioc->ioc_bufsize || !ioc->ioc_buflen) return 0; - if (ioc->ioc_buflen < ioc->ioc_offset) { - iocache_move_data(ioc); + if (ioc->ioc_buflen <= ioc->ioc_offset) { + iocache_reset(ioc); return 0; } @@ -100,6 +111,7 @@ ret = ioc->ioc_buf + ioc->ioc_offset; ioc->ioc_offset += size; + return ret; } @@ -166,15 +178,6 @@ if (!ioc || !ioc->ioc_buf || fd < 0) return -1; - /* - * Check if we've managed to read our fill and the caller - * has parsed all data. Otherwise we might end up in a state - * where we can't read anything but there's still new data - * queued on the socket - */ - if (ioc->ioc_offset >= ioc->ioc_buflen) - ioc->ioc_offset = ioc->ioc_buflen = 0; - /* we make sure we've got as much room as possible */ iocache_move_data(ioc); Modified: nagioscore/trunk/lib/iocache.h =================================================================== --- nagioscore/trunk/lib/iocache.h 2012-08-02 00:42:00 UTC (rev 2026) +++ nagioscore/trunk/lib/iocache.h 2012-08-02 00:42:17 UTC (rev 2027) @@ -25,6 +25,14 @@ extern void iocache_destroy(iocache *ioc); /** + * Resets an iocache struct, discarding all data in it without free()'ing + * any memory. + * + * @param[in] ioc The iocache struct to reset + */ +extern void iocache_reset(iocache *ioc); + +/** * Resizes the buffer in an io cache * @param ioc The io cache to resize * @param new_size The new size of the io cache This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-02 00:42:39
|
Revision: 2028 http://nagios.svn.sourceforge.net/nagios/?rev=2028&view=rev Author: ageric Date: 2012-08-02 00:42:33 +0000 (Thu, 02 Aug 2012) Log Message: ----------- lib/iocache: Add functions iocache_{add,write,send,sendto}() It's nifty to be able to cache output in memory before flushing it, in order to avoid tons of small writes. This patch does just that, although testing is currently lacking. We're not really using these functions anywhere right now, but they've been sitting in my working tree far too long not to be committed now. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iocache.c nagioscore/trunk/lib/iocache.h Modified: nagioscore/trunk/lib/iocache.c =================================================================== --- nagioscore/trunk/lib/iocache.c 2012-08-02 00:42:17 UTC (rev 2027) +++ nagioscore/trunk/lib/iocache.c 2012-08-02 00:42:33 UTC (rev 2028) @@ -2,6 +2,7 @@ #include <unistd.h> #include <stdlib.h> #include <string.h> +#include <errno.h> struct iocache { char *ioc_buf; /* the data */ @@ -191,3 +192,55 @@ return bytes_read; } + + +int iocache_add(iocache *ioc, char *buf, unsigned int len) +{ + if (!ioc || iocache_capacity(ioc) < len) + return -1; + + memcpy(ioc->ioc_buf + ioc->ioc_offset, buf, len); + ioc->ioc_buflen += len; + return ioc->ioc_buflen - ioc->ioc_offset; +} + +/* + * Three cases to handle: + * - buf has data, iocache doesn't. + * - iocache has data, buf doesn't. + * - both buf and iocache has data. + */ +int iocache_sendto(iocache *ioc, int fd, char *buf, unsigned int len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) +{ + int sent; + + errno = 0; + if (!ioc) + return -1; + + if (!ioc->ioc_buflen && !len) + return 0; + + if (ioc->ioc_buf && iocache_available(ioc)) { + if (buf && len) { + /* copy buf and len to iocache buffer to use just one write */ + if (iocache_capacity(ioc) < len) { + if (iocache_grow(ioc, iocache_size(ioc)) < 0) + return -1; + } + if (iocache_add(ioc, buf, len) < 0) + return -1; + } + buf = ioc->ioc_buf; + len = iocache_available(ioc); + } + + sent = sendto(fd, buf, len, flags, dest_addr, addrlen); + if (sent < 1) + return -errno; + + if (iocache_available(ioc)) + iocache_use_size(ioc, sent); + + return sent; +} Modified: nagioscore/trunk/lib/iocache.h =================================================================== --- nagioscore/trunk/lib/iocache.h 2012-08-02 00:42:17 UTC (rev 2027) +++ nagioscore/trunk/lib/iocache.h 2012-08-02 00:42:33 UTC (rev 2028) @@ -1,7 +1,8 @@ #ifndef INCLUDE_iocache_h__ #define INCLUDE_iocache_h__ #include <stdlib.h> -#include <limits.h> +#include <sys/types.h> +#include <sys/socket.h> /** * @file iocache.h @@ -110,5 +111,63 @@ */ extern int iocache_read(iocache *ioc, int fd); +/** + * Add data to the iocache buffer + * The data is copied, so it can safely be taken from the stack in a + * function that returns before the data is used. + * If the io cache is too small to hold the data, -1 will be returned. + * + * @param[in] ioc The io cache to add to + * @param[in] buf Pointer to the data we should add + * @param[in] len Length (in bytes) of data pointed to by buf + * @return iocache_available(ioc) on success, -1 on errors + */ +extern int iocache_add(iocache *ioc, char *buf, unsigned int len); + +/** + * Like sendto(), but sends all cached data prior to the requested + * + * @param[in] ioc The iocache to send, or cache data in + * @param[in] fd The file descriptor to send to + * @param[in] buf Pointer to the data to send + * @param[in] len Length (in bytes) of data to send + * @param[in] flags Flags passed to sendto(2) + * @param[in] dest_addr Destination address + * @param[in] addrlen size (in bytes) of dest_addr + * @return bytes sent on success, -ERRNO on errors + */ +extern int iocache_sendto(iocache *ioc, int fd, char *buf, unsigned int len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); + +/** + * Like send(2), but sends all cached data prior to the requested + * This function uses iocache_sendto() internally, but can only be + * used on connected sockets or open()'ed files. + * + * @param[in] ioc The iocache to send, or cache data in + * @param[in] fd The file descriptor to send to + * @param[in] buf Pointer to the data to send + * @param[in] len Length (in bytes) of data to send + * @param[in] flags Flags passed to sendto(2) + * @return bytes sent on success, -ERRNO on errors + */ +static inline int iocache_send(iocache *ioc, int fd, char *buf, unsigned int len, int flags) +{ + return iocache_sendto(ioc, fd, buf, len, flags, NULL, 0); +} + +/** + * Like write(2), but sends all cached data prior to the requested + * This function uses iocache_send() internally. + * + * @param[in] ioc The iocache to send, or cache data in + * @param[in] fd The file descriptor to send to + * @param[in] buf Pointer to the data to send + * @param[in] len Length (in bytes) of data to send + * @return bytes sent on success, -ERRNO on errors + */ +static inline int iocache_write(iocache *ioc, int fd, char *buf, unsigned int len) +{ + return iocache_send(ioc, fd, buf, len, 0); +} #endif /* INCLUDE_iocache_h__ */ /** @} */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-02 00:45:37
|
Revision: 2037 http://nagios.svn.sourceforge.net/nagios/?rev=2037&view=rev Author: ageric Date: 2012-08-02 00:45:31 +0000 (Thu, 02 Aug 2012) Log Message: ----------- lib/iobroker: Add iobroker_is_registered(fd) As per the questioning name, it returns 1 iff fd is registered and 0 in all other cases. This is sort of a debug function, since callers should really keep track of which descriptors they've registered with the broker, but since I keep tip-toeing around it when committing changes it might as well be a permanent fixture of the broker. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iobroker.c nagioscore/trunk/lib/iobroker.h Modified: nagioscore/trunk/lib/iobroker.c =================================================================== --- nagioscore/trunk/lib/iobroker.c 2012-08-02 00:45:14 UTC (rev 2036) +++ nagioscore/trunk/lib/iobroker.c 2012-08-02 00:45:31 UTC (rev 2037) @@ -225,7 +225,14 @@ return 0; } +int iobroker_is_registered(iobroker_set *iobs, int fd) +{ + if (!iobs || fd < 0 || fd > iobs->max_fds || !iobs->iobroker_fds[fd]) + return 0; + return iobs->iobroker_fds[fd]->fd == fd; +} + int iobroker_unregister(iobroker_set *iobs, int fd) { if (!iobs) Modified: nagioscore/trunk/lib/iobroker.h =================================================================== --- nagioscore/trunk/lib/iobroker.h 2012-08-02 00:45:14 UTC (rev 2036) +++ nagioscore/trunk/lib/iobroker.h 2012-08-02 00:45:31 UTC (rev 2037) @@ -84,6 +84,14 @@ /** + * Check if a particular filedescriptor is registered with the iobroker set + * @param[in] The iobroker set the filedescriptor should be member of + * @param[in] fd The filedescriptor to check for + * @return 1 if the filedescriptor is registered and 0 otherwise + */ +extern int iobroker_is_registered(iobroker_set *iobs, int fd); + +/** * Getter function for number of file descriptors registered in * the set specified. * @param iobs The io broker set to query This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-02 00:45:53
|
Revision: 2038 http://nagios.svn.sourceforge.net/nagios/?rev=2038&view=rev Author: ageric Date: 2012-08-02 00:45:47 +0000 (Thu, 02 Aug 2012) Log Message: ----------- lib/iobroker: Make iobroker_poll() return number of triggered fd's This makes it possible to do some simple verification that we're getting some sort of input from something registered with the broker. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iobroker.c nagioscore/trunk/lib/iobroker.h Modified: nagioscore/trunk/lib/iobroker.c =================================================================== --- nagioscore/trunk/lib/iobroker.c 2012-08-02 00:45:31 UTC (rev 2037) +++ nagioscore/trunk/lib/iobroker.c 2012-08-02 00:45:47 UTC (rev 2038) @@ -316,7 +316,7 @@ int iobroker_poll(iobroker_set *iobs, int timeout) { - int i, nfds; + int i, nfds, ret = 0; if (!iobs) return IOBROKER_ENOSET; @@ -342,6 +342,7 @@ if (s) { s->handler(fd, iobs->ep_events[i].events, s->arg); + ret++; } } #elif defined(IOBROKER_USES_SELECT) @@ -386,6 +387,7 @@ continue; } s->handler(s->fd, POLLIN, s->arg); + ret++; } } } @@ -420,9 +422,10 @@ continue; } s->handler(s->fd, (int)iobs->pfd[i].revents, s->arg); + ret++; } } #endif - return 0; + return ret; } Modified: nagioscore/trunk/lib/iobroker.h =================================================================== --- nagioscore/trunk/lib/iobroker.h 2012-08-02 00:45:31 UTC (rev 2037) +++ nagioscore/trunk/lib/iobroker.h 2012-08-02 00:45:47 UTC (rev 2038) @@ -149,6 +149,7 @@ * Wait for input on any of the registered sockets. * @param iobs The socket set to wait for. * @param timeout Timeout in milliseconds. -1 is "wait indefinitely" + * @return -1 on errors, or number of filedescriptors with input */ extern int iobroker_poll(iobroker_set *iobs, int timeout); #endif /* INCLUDE_iobroker_h__ */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-02 00:46:12
|
Revision: 2039 http://nagios.svn.sourceforge.net/nagios/?rev=2039&view=rev Author: ageric Date: 2012-08-02 00:46:06 +0000 (Thu, 02 Aug 2012) Log Message: ----------- lib/worker: Make send_kvvec() return what send() returned Doing so makes it possible to keep track of and report write errors, which is naturally crucial, but also helps us keep track of how much data we've sent, which is really nifty to know when debugging. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/worker.c nagioscore/trunk/lib/worker.h Modified: nagioscore/trunk/lib/worker.c =================================================================== --- nagioscore/trunk/lib/worker.c 2012-08-02 00:45:47 UTC (rev 2038) +++ nagioscore/trunk/lib/worker.c 2012-08-02 00:46:06 UTC (rev 2039) @@ -96,6 +96,7 @@ char msg[4096]; int len; va_list ap; + int ret; va_start(ap, fmt); len = vsnprintf(msg, sizeof(msg) - 1, fmt, ap); @@ -104,7 +105,9 @@ kvvec_addkv(kvv, "job_id", (char *)mkstr("%d", cp->id)); } kvvec_addkv_wlen(kvv, "error_msg", 5, msg, len); - send_kvvec(master_sd, kvv); + ret = send_kvvec(master_sd, kvv); + if (ret < 0 && errno == EPIPE) + exit_worker(); kvvec_destroy(kvv, 0); } @@ -157,7 +160,7 @@ return ret; } -void send_kvvec(int sd, struct kvvec *kvv) +int send_kvvec(int sd, struct kvvec *kvv) { int ret; struct kvvec_buf *kvvb; @@ -178,22 +181,14 @@ * reason is OOM, in which case the OOM-slayer will * probably kill us sooner or later. */ - return; + return 0; } /* use bufsize here, as it gets us the nul string delimiter */ ret = write(sd, kvvb->buf, kvvb->bufsize); - if (ret < 0) { - if (errno == EPIPE) { - /* - * master has crashed or abandoned us, so we die - * with what grace we can. - */ - exit_worker(); - } - } free(kvvb->buf); free(kvvb); + return ret; } #define kvvec_add_long(kvv, key, value) \ @@ -212,7 +207,7 @@ { struct kvvec *resp; struct rusage *ru = &cp->rusage; - int i; + int i, ret; resp = kvvec_init(12 + cp->request->kv_pairs); /* how many key/value pairs do we need? */ @@ -274,7 +269,9 @@ kvvec_addkv(resp, "exited_ok", "0"); kvvec_addkv(resp, "error_code", (char *)mkstr("%d", reason)); } - send_kvvec(master_sd, resp); + ret = send_kvvec(master_sd, resp); + if (ret < 0 && errno == EPIPE) + exit_worker(); /* * we mustn't free() the key/value pairs here, as they're all Modified: nagioscore/trunk/lib/worker.h =================================================================== --- nagioscore/trunk/lib/worker.h 2012-08-02 00:45:47 UTC (rev 2038) +++ nagioscore/trunk/lib/worker.h 2012-08-02 00:46:06 UTC (rev 2039) @@ -66,8 +66,9 @@ * Send a key/value vector as a bytestream through a socket * @param[in] sd The socket descriptor to send to * @param kvv The key/value vector to send + * @return The number of bytes sent, or -1 on errors */ -extern void send_kvvec(int sd, struct kvvec *kvv); +extern int send_kvvec(int sd, struct kvvec *kvv); /** * Create a short-lived string in stack-allocated memory This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-02 00:47:28
|
Revision: 2043 http://nagios.svn.sourceforge.net/nagios/?rev=2043&view=rev Author: ageric Date: 2012-08-02 00:47:22 +0000 (Thu, 02 Aug 2012) Log Message: ----------- lib/worker: Add set_socket_options() It's handy to disallow sockets to spill over into plugins, and it's also nifty to beef up the send and receive buffers somewhat, so we do just that. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/worker.c nagioscore/trunk/lib/worker.h Modified: nagioscore/trunk/lib/worker.c =================================================================== --- nagioscore/trunk/lib/worker.c 2012-08-02 00:47:05 UTC (rev 2042) +++ nagioscore/trunk/lib/worker.c 2012-08-02 00:47:22 UTC (rev 2043) @@ -565,7 +565,22 @@ return 0; } +int set_socket_options(int sd, int bufsize) +{ + int ret; + ret = fcntl(sd, F_SETFD, FD_CLOEXEC); + ret |= fcntl(sd, F_SETFL, O_NONBLOCK); + + if (!bufsize) + return ret; + ret |= setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int)); + ret |= setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int)); + + return ret; +} + + static void enter_worker(int sd) { /* created with socketpair(), usually */ @@ -595,6 +610,7 @@ * more than enough for our needs */ sq = squeue_create(1024); + set_socket_options(master_sd, 256 * 1024); iobroker_register(iobs, master_sd, NULL, receive_command); while (iobroker_get_num_fds(iobs) > 0) { Modified: nagioscore/trunk/lib/worker.h =================================================================== --- nagioscore/trunk/lib/worker.h 2012-08-02 00:47:05 UTC (rev 2042) +++ nagioscore/trunk/lib/worker.h 2012-08-02 00:47:22 UTC (rev 2043) @@ -87,4 +87,12 @@ * @return The millisecond delta between the two structs */ extern int tv_delta_msec(const struct timeval *start, const struct timeval *stop); + +/** + * Set some common socket options + * @param[in] sd The socket to set options for + * @param[in] bufsize Size to set send and receive buffers to + * @return 0 on success. < 0 on errors + */ +extern int set_socket_options(int sd, int bufsize); #endif /* INCLUDE_worker_h__ */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-17 14:15:29
|
Revision: 2074 http://nagios.svn.sourceforge.net/nagios/?rev=2074&view=rev Author: ageric Date: 2012-08-17 14:15:22 +0000 (Fri, 17 Aug 2012) Log Message: ----------- Fix doxygen errors in lib/kvvec.h and lib/iobroker.h Since we're shipping a doxygen file we might as well make sure it works as intended and without warnings. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/iobroker.h nagioscore/trunk/lib/kvvec.h Modified: nagioscore/trunk/lib/iobroker.h =================================================================== --- nagioscore/trunk/lib/iobroker.h 2012-08-17 12:08:26 UTC (rev 2073) +++ nagioscore/trunk/lib/iobroker.h 2012-08-17 14:15:22 UTC (rev 2074) @@ -85,7 +85,7 @@ /** * Check if a particular filedescriptor is registered with the iobroker set - * @param[in] The iobroker set the filedescriptor should be member of + * @param[in] iobs The iobroker set the filedescriptor should be member of * @param[in] fd The filedescriptor to check for * @return 1 if the filedescriptor is registered and 0 otherwise */ Modified: nagioscore/trunk/lib/kvvec.h =================================================================== --- nagioscore/trunk/lib/kvvec.h 2012-08-17 12:08:26 UTC (rev 2073) +++ nagioscore/trunk/lib/kvvec.h 2012-08-17 14:15:22 UTC (rev 2074) @@ -60,6 +60,7 @@ /** * Initialize a previously allocated key/value vector * + * @param kvv The key/value vector to initialize * @param hint Number of key/value pairs we expect to store * @return Pointer to a struct kvvec, properly initialized */ @@ -169,6 +170,7 @@ * @param len Length of buffer to convert * @param kvsep Character separating key and value * @param pair_sep Character separating key/value pairs + * @param flags bitmask. See KVVEC_{ASSIGN,COPY,APPEND} for values * @return The created key/value vector */ extern struct kvvec *buf2kvvec(char *str, unsigned int len, const char kvsep, const char pair_sep, int flags); @@ -177,10 +179,12 @@ * Parse a buffer into the pre-allocated key/value vector. Immensely * useful for ipc in combination with kvvec2buf(). * + * @param kvv A pre-allocated key/value vector to populate * @param str The buffer to convert to a key/value vector * @param len Length of buffer to convert * @param kvsep Character separating key and value * @param pair_sep Character separating key/value pairs + * @param flags bitmask. See KVVEC_{ASSIGN,COPY,APPEND} for values * @return The number of pairs in the created key/value vector */ extern int buf2kvvec_prealloc(struct kvvec *kvv, char *str, unsigned int len, const char kvsep, const char pair_sep, int flags); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-20 16:24:45
|
Revision: 2077 http://nagios.svn.sourceforge.net/nagios/?rev=2077&view=rev Author: ageric Date: 2012-08-20 16:24:38 +0000 (Mon, 20 Aug 2012) Log Message: ----------- Add t-utils This is like a short version of libtap, but niftier since we don't have to run configure and can get prettier output. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/Makefile.in nagioscore/trunk/lib/test-iobroker.c nagioscore/trunk/lib/test-iocache.c nagioscore/trunk/lib/test-kvvec.c nagioscore/trunk/lib/test-squeue.c Added Paths: ----------- nagioscore/trunk/lib/t-utils.c nagioscore/trunk/lib/t-utils.h Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-08-20 11:28:28 UTC (rev 2076) +++ nagioscore/trunk/lib/Makefile.in 2012-08-20 16:24:38 UTC (rev 2077) @@ -16,15 +16,18 @@ test: $(TESTS) @for t in $(TESTS); do echo $$t:; ./$$t || exit 1; echo; done -test-squeue: pqueue.o test-squeue.o +test-squeue: pqueue.o test-squeue.o t-utils.o $(CC) $(ALL_CFLAGS) $(LDFLAGS) $^ -o $@ %.o: %.c %.h Makefile $(CC) $(ALL_CFLAGS) -c $< -o $@ -test-%: test-%.c %.c %.h Makefile - $(CC) $(ALL_CFLAGS) $< -o $@ +test-%.o: test-%.c %.c %.h Makefile + $(CC) $(ALL_CFLAGS) -c $< -o $@ +test-%: t-utils.o test-%.o + $(CC) $(ALL_CFLAGS) $^ -o $@ + $(LIBNAME): $(SRC_O) $(AR) cr $@ $^ @@ -64,3 +67,7 @@ rm -f untested *.gcov *.gcda *.gcno gmon.out .PHONY: clean clean-test clean-coverage coverage + +# stop make from removing intermediary files, as ours aren't really +# intermediary +.SECONDARY: Added: nagioscore/trunk/lib/t-utils.c =================================================================== --- nagioscore/trunk/lib/t-utils.c (rev 0) +++ nagioscore/trunk/lib/t-utils.c 2012-08-20 16:24:38 UTC (rev 2077) @@ -0,0 +1,170 @@ +#include "t-utils.h" + +const char *cyan = "", *red = "", *green = "", *yellow = "", *reset = ""; +uint passed, failed, t_verbose = 0; +static uint t_depth; +static const char *indent_str = " "; + +/* can't be used when a or b has side-effects, but we don't care here */ +#define max(a, b) (a > b ? a : b) +#define min(a, b) (a < b ? a : b) +#define delta(a, b) ((max(a, b) - (min(a, b)))) + + +void t_set_colors(int force) +{ + if (force == 1 || isatty(fileno(stdout))) { + cyan = CLR_CYAN; + red = CLR_RED; + yellow = CLR_YELLOW; + green = CLR_GREEN; + reset = CLR_RESET; + } +} + +static void t_indent(uint depth) +{ + uint i; + for (i = 0; i < depth; i++) { + printf("%s", indent_str); + } +} + +void t_start(const char *fmt, ...) +{ + va_list ap; + + t_indent(t_depth++); + va_start(ap, fmt); + printf("%s### ", cyan); + vfprintf(stdout, fmt, ap); + printf("%s\n", reset); + va_end(ap); +} + +int t_end(void) +{ + if (t_depth) + t_depth--; + if (!t_depth || failed) { + t_indent(t_depth); + printf("Test results: %s%u passed%s, %s%u failed%s\n", + green, passed, reset, failed ? red : "", failed, failed ? reset : ""); + } + + return failed ? EXIT_FAILURE : EXIT_SUCCESS; +} + +static int t_okv(int success, const char *fmt, va_list ap) +{ + passed += !!success; + failed += !success; + if (!fmt || !t_verbose) + return success; + + if (fmt && t_verbose) { + t_indent(t_depth); + if (success) { + printf("%sPASS%s ", green, reset); + } else { + printf("%sFAIL%s ", red, reset); + } + vfprintf(stdout, fmt, ap); + putchar('\n'); + } + + return success; +} + +int t_ok(int success, const char *fmt, ...) +{ + va_list ap; + + if (fmt && t_verbose) { + va_start(ap, fmt); + t_okv(success, fmt, ap); + va_end(ap); + } + else + t_okv(success, NULL, NULL); + + return success; +} + +void t_pass(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + t_okv(TEST_PASS, fmt, ap); + va_end(ap); +} + +void t_fail(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + t_okv(TEST_FAIL, fmt, ap); + va_end(ap); +} + +void t_diag(const char *fmt, ...) +{ + if (fmt) { + va_list ap; + t_indent(t_depth + 1); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + putchar('\n'); + } +} + +int ok_int(int a, int b, const char *name) +{ + if (a == b) { + t_pass(name); + return TEST_PASS; + } + + t_fail(name); + t_diag("%d != %d. delta: %d", a, b, delta(a, b)); + return TEST_FAIL; +} + +int ok_uint(uint a, uint b, const char *name) +{ + if (a == b) { + t_pass(name); + return TEST_PASS; + } + + t_fail(name); + t_diag("%u != %u. delta: %u", a, b, delta(a, b)); + return TEST_FAIL; +} + +int ok_str(const char *a, const char *b, const char *name) +{ + if ((!a && !b) || (a && b && !strcmp(a, b))) { + t_pass(name); + return TEST_PASS; + } + + t_fail(name); + t_diag("'%s' != '%s'", a, b); + return TEST_FAIL; +} + +void __attribute__((__noreturn__)) crash(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + + exit(1); +} Added: nagioscore/trunk/lib/t-utils.h =================================================================== --- nagioscore/trunk/lib/t-utils.h (rev 0) +++ nagioscore/trunk/lib/t-utils.h 2012-08-20 16:24:38 UTC (rev 2077) @@ -0,0 +1,63 @@ +#ifndef INCLUDE_test_utils_h__ +#define INCLUDE_test_utils_h__ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +#define TEST_PASS 1 +#define TEST_FAIL 0 + +#define CLR_RESET "\033[m" +#define CLR_BOLD "\033[1m" +#define CLR_RED "\033[31m" +#define CLR_GREEN "\033[32m" +#define CLR_BROWN "\033[33m" +#define CLR_YELLOW "\033[33m\033[1m" +#define CLR_BLUE "\033[34m" +#define CLR_MAGENTA "\033[35m" +#define CLR_CYAN "\033[36m" +#define CLR_BG_RED "\033[41m" +#define CLR_BRIGHT_RED "\033[31m\033[1m" +#define CLR_BRIGHT_GREEN "\033[32m\033[1m" +#define CLR_BRIGHT_BLUE "\033[34m\033[1m" +#define CLR_BRIGHT_MAGENTA "\033[35m\033[1m" +#define CLR_BRIGHT_CYAN "\033[36m\033[1m" + +extern const char *red, *green, *yellow, *cyan, *reset; +extern unsigned int passed, failed, t_verbose; + +#define CHECKPOINT() \ + do { \ + fprintf(stderr, "ALIVE @ %s:%s:%d\n", __FILE__, __func__, __LINE__); \ + } while(0) + +#define t_assert(expr) \ + +extern void t_set_colors(int force); +extern void t_start(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2))); +extern void t_pass(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2))); +extern void t_fail(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2))); +extern void t_diag(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2))); +extern int t_ok(int success, const char *fmt, ...) + __attribute__((__format__(__printf__, 2, 3))); +#define test t_ok +#define t_req(expr) \ + if (!(expr)) \ + crash("No further testing is possible: " #expr " @%s:%d", __FILE__, __LINE__) +extern int ok_int(int a, int b, const char *name); +extern int ok_uint(unsigned int a, unsigned int b, const char *name); +extern int ok_str(const char *a, const char *b, const char *name); +extern int t_end(void); +extern void crash(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2), __noreturn__)); +#endif Modified: nagioscore/trunk/lib/test-iobroker.c =================================================================== --- nagioscore/trunk/lib/test-iobroker.c 2012-08-20 11:28:28 UTC (rev 2076) +++ nagioscore/trunk/lib/test-iobroker.c 2012-08-20 16:24:38 UTC (rev 2077) @@ -10,21 +10,10 @@ #include <fcntl.h> #include "iobroker.c" +#include "t-utils.h" -static int fail, pass; static iobroker_set *iobs; -#define CHECKPOINT(x__) \ - do { \ - fprintf(stderr, "ALIVE @ %s:%s:%d\n", __FILE__, __func__, __LINE__); \ - } while(0) -#define TRY(x__) \ - do { \ - fprintf(stderr, "Trying '%s;'\n", #x__); \ - x__; \ - fprintf(stderr, "Seems to have worked out ok\n"); \ - } while (0) - static char *msg[] = { "Welcome to the echo service!\n", "test msg nr 2", @@ -69,13 +58,8 @@ buf[len] = 0; - if (len != strlen(msg[i]) || memcmp(buf, msg[i], len)) { - printf("fd: %d, i: %d; len: %d; buf: %s\n", fd, i, len, buf); - fprintf(stderr, "Upping fail at #1\n"); - fail++; - } else { - pass++; - } + test(len == strlen(msg[i]), "len match for message %d", i); + test(!memcmp(buf, msg[i], len), "data match for message %d", i); } i++; @@ -86,7 +70,6 @@ } if (!msg[i]) { - //printf("OK: %d messages sent on socket %d\n", i, fd); iobroker_close(iobs, fd); return 0; } @@ -122,9 +105,8 @@ int sighandler(int sig) { /* test failed */ - fprintf(stderr, "Caught signal %d (%sSIGALRM). Fail = %d\n", - sig, sig == SIGALRM ? "" : "not ", fail); - exit(1); + t_fail("Caught signal %d", sig); + exit(t_end()); } @@ -151,7 +133,6 @@ } iobroker_poll(iobs, -1); } - printf("%d connections spammed\n", i); return 0; } @@ -162,23 +143,18 @@ int error; const char *err_msg; + t_set_colors(0); + t_start("iobroker ipc test"); + error = iobroker_get_max_fds(NULL); - if (error == IOBROKER_ENOSET) - pass++; - else - fail++; + ok_int(error, IOBROKER_ENOSET, "test errors when passing null"); + err_msg = iobroker_strerror(error); - if (err_msg && !strcmp(err_msg, iobroker_errors[(~error) + 1].string)) - pass++; - else - fail++; + test(err_msg && !strcmp(err_msg, iobroker_errors[(~error) + 1].string), "iobroker_strerror() returns the right string"); iobs = iobroker_create(); error = iobroker_get_max_fds(iobs); - if (iobs && error >= 0) - pass++; - else - fail++; + test(iobs && error >= 0, "max fd's for real iobroker set must be > 0"); listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); flags = fcntl(listen_fd, F_GETFD); @@ -191,10 +167,7 @@ sain.sin_port = ntohs(9123); sain.sin_family = AF_INET; bind(listen_fd, (struct sockaddr *)&sain, sizeof(sain)); - printf("Listening on %s:%d with socket %d\n", - inet_ntoa(sain.sin_addr), ntohs(sain.sin_port), listen_fd); listen(listen_fd, 128); - printf("Registering listener socket %d with I/O broker\n", listen_fd); iobroker_register(iobs, listen_fd, iobs, listen_handler); if (argc == 1) @@ -208,20 +181,8 @@ } iobroker_close(iobs, listen_fd); - printf("Destroying iobs\n"); iobroker_destroy(iobs, 0); - if (fail) { - printf("FAIL: %d tests failed\n", fail); - return 1; - } - - if (pass) { - if (pass == 3 + (ARRAY_SIZE(msg) - 1) * NUM_PROCS) { - printf("PASS: All %d tests ran just fine\n", pass); - } else { - printf("PASS: %d tests passed, with connection problems\n", pass); - } - } + t_end(); return 0; } Modified: nagioscore/trunk/lib/test-iocache.c =================================================================== --- nagioscore/trunk/lib/test-iocache.c 2012-08-20 11:28:28 UTC (rev 2076) +++ nagioscore/trunk/lib/test-iocache.c 2012-08-20 16:24:38 UTC (rev 2077) @@ -1,6 +1,7 @@ #include <stdio.h> #include <stdarg.h> #include "iocache.c" +#include "t-utils.h" struct strcode { char *str; @@ -22,6 +23,8 @@ iocache *ioc; ioc = iocache_create(512 * 1024); + if (!test(ioc != NULL, "iocache_create must work")) + crash("can't test with no available memory"); for (i = 0; sc[i].str; i++) { memcpy(&ioc->ioc_buf[ioc->ioc_buflen], sc[i].str, sc[i].len); @@ -35,18 +38,13 @@ unsigned long len; int error = 0; ptr = iocache_use_delim(ioc, delim, delim_len, &len); + t_req(ptr != NULL); if (!ptr) { printf("Null pointer. What weird shit is this??\n"); exit(1); } - if (len != sc[i].len) { - printf("########## len error\n"); - error = 1; - } else if (memcmp(ptr, sc[i].str, len)) { - printf("########## memcmp() error\n"); - error = 2; - } - + test(len == sc[i].len, "len check, string %d, delim_len %d", i, delim_len); + test(!memcmp(ptr, sc[i].str, len), "memcmp() check, string %d, delim_len %d", i, delim_len); if (error) { printf("delim_len: %d. i: %d; len: %lu; sc[i].len: %d\n", delim_len, i, len, sc[i].len); @@ -69,14 +67,15 @@ ADDSTR("\0\0"), ADDSTR("XXXxXXX"), ADDSTR("LALALALALALALAKALASBALLE\n"), - { NULL, 0 }, }; - for (i = 0; sc[i].str; i++) { - printf(" Testing delimiter of len %d\n", sc[i].len); + t_set_colors(0); + t_start("iocache_use_delim() test"); + for (i = 0; i < ARRAY_SIZE(sc); i++) { + t_start("Testing delimiter of len %d", sc[i].len); test_delimiter(sc[i].str, sc[i].len); + t_end(); } - printf("All tests passed\n"); - return 0; + return t_end(); } Modified: nagioscore/trunk/lib/test-kvvec.c =================================================================== --- nagioscore/trunk/lib/test-kvvec.c 2012-08-20 11:28:28 UTC (rev 2076) +++ nagioscore/trunk/lib/test-kvvec.c 2012-08-20 16:24:38 UTC (rev 2077) @@ -3,6 +3,7 @@ #include <string.h> #include <stdlib.h> #include "kvvec.c" +#include "t-utils.h" static int walking_steps, walks; @@ -20,8 +21,8 @@ } if (discard && vec) { - if (!kv_compare(&vec->kv[step], kv)) - printf(" PASS: step %d on walk %d matches\n", step, walks); + t_ok(!kv_compare(&vec->kv[step], kv), "step %d on walk %d", + step, walks); } step++; @@ -65,6 +66,9 @@ struct kvvec *kvv, *kvv2, *kvv3; struct kvvec_buf *kvvb, *kvvb2; + t_set_colors(0); + + t_start("key/value vector tests"); kvv = kvvec_create(1); kvv2 = kvvec_create(1); kvv3 = kvvec_create(1); @@ -84,39 +88,34 @@ kvvb = kvvec2buf(kvv, KVSEP, PAIRSEP, OVERALLOC); - if (kvv->kv_pairs != kvv2->kv_pairs) { - printf("Failure: kvvec2buf -> buf2kvvec fails to get pairs teamed up (delta: %d)\n", - kvv->kv_pairs - kvv2->kv_pairs); - } + test(kvv->kv_pairs == kvv2->kv_pairs, "pairs should be identical"); for (i = 0; i < kvv->kv_pairs; i++) { struct key_value *kv1, *kv2; kv1 = &kvv->kv[i]; if (i >= kvv2->kv_pairs) { - printf("%d failed: Not present in kvv2\n", i); + t_fail("missing var %d in kvv2", i); printf("[%s=%s] (%d+%d)\n", kv1->key, kv1->value, kv1->key_len, kv1->value_len); continue; } kv2 = &kvv2->kv[i]; - if (kv_compare(kv1, kv2)) { + if (!test(!kv_compare(kv1, kv2), "kv pair %d must match", i)) { printf("%d failed: [%s=%s] (%d+%d) != [%s=%s (%d+%d)]\n", i, kv1->key, kv1->value, kv1->key_len, kv1->value_len, kv2->key, kv2->value, kv2->key_len, kv2->value_len); } } - if (kvvb2->buflen == kvvb->buflen) - printf("PASS: kvvb->buflen == kvvb->buflen\n"); - if (kvvb2->bufsize == kvvb->bufsize) - printf("PASS: kvvb2->bufsize = kvvb->bufsize\n"); + test(kvvb2->buflen == kvvb->buflen, "buflens must match"); + test(kvvb2->bufsize == kvvb->bufsize, "bufsizes must match"); + if (kvvb2->buflen == kvvb->buflen && kvvb2->bufsize == kvvb->bufsize && !memcmp(kvvb2->buf, kvvb->buf, kvvb->bufsize)) { - printf("PASS: kvvec -> buf -> kvvec conversion works flawlessly\n"); + t_pass("kvvec -> buf -> kvvec conversion works flawlessly"); } else { - printf("FAIL: kvvec -> buf -> kvvec conversion failed :'(\n"); - return EXIT_FAILURE; + t_fail("kvvec -> buf -> kvvec conversion failed :'("); } free(kvvb->buf); @@ -125,5 +124,7 @@ free(kvvb2); kvvec_destroy(kvv, 1); kvvec_destroy(kvv3, KVVEC_FREE_ALL); + + t_end(); return 0; } Modified: nagioscore/trunk/lib/test-squeue.c =================================================================== --- nagioscore/trunk/lib/test-squeue.c 2012-08-20 11:28:28 UTC (rev 2076) +++ nagioscore/trunk/lib/test-squeue.c 2012-08-20 16:24:38 UTC (rev 2077) @@ -10,13 +10,8 @@ #include <unistd.h> #include <sys/time.h> #include "squeue.c" +#include "t-utils.h" -typedef struct test_suite { - int pass; - int fail; - int latest; -} test_suite; - static void squeue_foreach(squeue_t *q, int (*walker)(squeue_event *, void *), void *arg) { squeue_t *dup; @@ -34,30 +29,14 @@ squeue_destroy(dup, 0); } -int t_print(test_suite *ts) -{ - printf("total: %d; passed: %d; failed: %d; status: %s\n", - ts->pass + ts->fail, ts->pass, ts->fail, ts->fail ? "FAILED" : "PASSED"); - return ts->fail ? -1 : 0; -} - -#define TEST_PASS 0 -#define TEST_FAIL 1 - #define t(expr, args...) \ - if ((expr)) { \ - ts->pass++; \ - ts->latest = TEST_PASS; \ - if (getenv("TEST_VERBOSE")) \ - printf("pass: " #expr "\n"); \ - } else { \ - ts->fail++; \ - ts->latest = TEST_FAIL; \ - printf("fail (@%s:%d):\n ##\t" #expr "\n", __FILE__, __LINE__); \ - printf(" " args); \ - kill(getpid(), SIGSEGV); \ - return -1; /* this will leak memory in some tests */ \ - } + do { \ + if ((expr)) { \ + t_pass(#expr); \ + } else { \ + t_fail(#expr " @%s:%d", __FILE__, __LINE__); \ + } \ + } while(0) typedef struct sq_test_event { unsigned long id; @@ -70,7 +49,6 @@ static int walks = 0; walks++; - test_suite *ts = (test_suite *)arg; t(sq_high <= evt->when.tv_sec, "sq_high: %lu; evt->when: %lu\n", sq_high, evt->when.tv_sec); sq_high = (unsigned long)evt->when.tv_sec; @@ -79,7 +57,7 @@ } #define EVT_ARY 65101 -static int sq_test_random(squeue_t *sq, test_suite *ts) +static int sq_test_random(squeue_t *sq) { unsigned long size, i; unsigned long long numbers[EVT_ARY], *d, max = 0; @@ -114,22 +92,15 @@ return 0; } -#if 0 -static void print_squeue_event(FILE *f, void *e) +int main(int argc, char **argv) { - sq_test_event *te = ((squeue_event *)e)->data; - te = (sq_test_event *)squeue_event_data((squeue_event *)e); - - fprintf(f, "%lu\n", te->id); -} -#endif - -static int test_squeue(test_suite *ts) -{ squeue_t *sq; struct timeval tv; sq_test_event a, b, c, d, *x; + t_set_colors(0); + t_start("squeue tests"); + a.id = 1; b.id = 2; c.id = 3; @@ -142,7 +113,7 @@ t(squeue_size(sq) == 0); /* we fill and empty the squeue completely once before testing */ - sq_test_random(sq, ts); + sq_test_random(sq); t(squeue_size(sq) == 0, "Size should be 0 after first sq_test_random"); t((a.evt = squeue_add(sq, time(NULL) + 9, &a)) != NULL); @@ -155,7 +126,7 @@ t(squeue_size(sq) == 4); /* add and remove lots. remainder should be what we have above */ - sq_test_random(sq, ts); + sq_test_random(sq); /* testing squeue_peek() */ t((x = (sq_test_event *)squeue_peek(sq)) != NULL); @@ -199,18 +170,10 @@ t(squeue_remove(NULL, NULL) == -1); t(squeue_remove(NULL, a.evt) == -1); - squeue_foreach(sq, sq_walker, ts); + squeue_foreach(sq, sq_walker, NULL); /* clean up to prevent false valgrind positives */ squeue_destroy(sq, 0); - return t_print(ts); + return t_end(); } - -int main(int argc, char **argv) -{ - test_suite ts = { 0 }; - test_squeue(&ts); -// test_kvvec(); - return 0; -} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-20 16:27:19
|
Revision: 2081 http://nagios.svn.sourceforge.net/nagios/?rev=2081&view=rev Author: ageric Date: 2012-08-20 16:27:12 +0000 (Mon, 20 Aug 2012) Log Message: ----------- Add bitmap library to libnagios This is insanely useful for calculating dependency graphs now that each object has a unique id, and will briefly be used to do exactly that. Actually, it's insanely useful to flag just about anything with an id in constant time and very little space, but we'll initially be using it to check for circular paths in service dependencies. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/.gitignore nagioscore/trunk/lib/Makefile.in nagioscore/trunk/lib/libnagios.h Added Paths: ----------- nagioscore/trunk/lib/bitmap.c nagioscore/trunk/lib/bitmap.h nagioscore/trunk/lib/test-bitmap.c Modified: nagioscore/trunk/lib/.gitignore =================================================================== --- nagioscore/trunk/lib/.gitignore 2012-08-20 16:26:47 UTC (rev 2080) +++ nagioscore/trunk/lib/.gitignore 2012-08-20 16:27:12 UTC (rev 2081) @@ -2,4 +2,5 @@ test-kvvec test-iocache test-iobroker +test-bitmap wproc Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-08-20 16:26:47 UTC (rev 2080) +++ nagioscore/trunk/lib/Makefile.in 2012-08-20 16:27:12 UTC (rev 2081) @@ -8,7 +8,7 @@ all: $(LIBNAME) SNPRINTF_O=@SNPRINTF_O@ -TESTED_SRC_C := squeue.c kvvec.c iocache.c iobroker.c +TESTED_SRC_C := squeue.c kvvec.c iocache.c iobroker.c bitmap.c SRC_C := $(TESTED_SRC_C) pqueue.c runcmd.c worker.c skiplist.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) Added: nagioscore/trunk/lib/bitmap.c =================================================================== --- nagioscore/trunk/lib/bitmap.c (rev 0) +++ nagioscore/trunk/lib/bitmap.c 2012-08-20 16:27:12 UTC (rev 2081) @@ -0,0 +1,261 @@ +#include <assert.h> +#include "bitmap.h" +#include <stdlib.h> +#include <unistd.h> + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +typedef unsigned int bmap; + +#define MAPSIZE (sizeof(bmap) * CHAR_BIT) +#define MAPMASK (MAPSIZE - 1) /* bits - 1, so 63 for 64-bit machines */ +#define SHIFTOUT (MAPSIZE == 64 ? 6 : 5) /* log2(bits) */ + +struct bitmap { + bmap *vector; + unsigned int alloc; +}; + +static bitmap *bitmap_init(bitmap *bm, unsigned long size) +{ + if (!bm) + return NULL; + + /* be tight on space */ + bm->alloc = (size >> SHIFTOUT) + !!(size & MAPMASK); + bm->vector = calloc(1, bm->alloc * sizeof(bmap)); +#if 0 + printf("bitmap created with %lu bytes of storage and capacity %lu\n", + bm->alloc * sizeof(bmap), bm->alloc * MAPSIZE); +#endif + return bm; +} + +bitmap *bitmap_create(unsigned long size) +{ + return bitmap_init(calloc(1, sizeof(bitmap)), size); +} + +void bitmap_destroy(bitmap *bm) +{ + if (!bm) + return; + if (bm->vector) + free(bm->vector); + free(bm); +} + +bitmap *bitmap_copy(const bitmap *bm) +{ + bitmap *ret; + + if (!bm) + return NULL; + ret = bitmap_create(bitmap_cardinality(bm)); + if (!ret) + return NULL; + + memcpy(ret->vector, bm->vector, bitmap_size(bm)); + return ret; +} + +static inline unsigned int l_bits(bmap map) +{ + unsigned int i, tot_bits = 0; + + /* + * bits per byte. A 16k entry table would be slightly faster + * on most archs but a damn sight uglier on all of them + */ + const unsigned char bpb[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, + }; + + if (0 && map) + printf("0x%08x\n", map); + for (i = 0; i < sizeof(bmap); i++) { + const unsigned char ch = (map >> (i * CHAR_BIT)) & 0xff; + const unsigned char cbits = bpb[ch]; + tot_bits += cbits; + if (0 && cbits) + printf("%d: top char: 0x%02x; bits: %u; tot_bits: %u\n", + i, ch, cbits, tot_bits); + } + + return tot_bits; +} + + +int bitmap_set(bitmap *bm, unsigned long pos) +{ + const bmap l = pos >> SHIFTOUT; + const unsigned int bit = pos & MAPMASK; + + if (!bm) + return 0; + if (l > bm->alloc) + return -1; + + bm->vector[l] |= (1 << bit); + return 0; +} + +int bitmap_isset(const bitmap *bm, unsigned long pos) +{ + const bmap l = pos >> SHIFTOUT; + const int bit = pos & MAPMASK; + int set; + + if (!bm || l > bm->alloc) + return 0; + + set = !!(bm->vector[l] & (1 << bit)); + return set; +} + +int bitmap_unset(bitmap *bm, unsigned long pos) +{ + const bmap l = pos >> SHIFTOUT; + const int bit = pos & MAPMASK; + const int val = bitmap_isset(bm, pos); + + bm->vector[l] &= ~(1 << bit); + return val; +} + +unsigned long bitmap_cardinality(const bitmap *bm) +{ + if (!bm) + return 0; + + return bm->alloc * MAPSIZE; +} + +/* + * count set bits in alloc * (mapsize / 8) ops + */ +unsigned long bitmap_count_set_bits(const bitmap *bm) +{ + unsigned long i, set_bits = 0; + + if (!bm) + return 0; + + for (i = 0; i < bm->alloc; i++) { + set_bits += l_bits(bm->vector[i]); + } + + return set_bits; +} + +unsigned long bitmap_count_unset_bits(const bitmap *bm) +{ + return bitmap_cardinality(bm) - bitmap_count_set_bits(bm); +} + +#define BITMAP_MATH(a, b) \ + int i; \ + bitmap *bm; \ + /* a->alloc has to be smallest */ \ + if (a->alloc > b->alloc) { \ + const bitmap *temp = b; \ + b = a; \ + a = temp; \ + } \ + bm = bitmap_create(bitmap_cardinality(b)); \ + if (!bm) \ + return NULL; \ + for (i = 0; i < a->alloc; i++) + +bitmap *bitmap_intersect(const bitmap *a, const bitmap *b) +{ + BITMAP_MATH(a, b) { + bm->vector[i] = a->vector[i] & b->vector[i]; + } + + return bm; +} + + +bitmap *bitmap_union(const bitmap *a, const bitmap *b) +{ + BITMAP_MATH(a, b) { + bm->vector[i] = a->vector[i] | b->vector[i]; + } + + return bm; +} + +/* + * Remove all elements from a that are also in b. + */ +bitmap *bitmap_minus(const bitmap *a, const bitmap *b) +{ + BITMAP_MATH(a, b) { + bm->vector[i] = a->vector[i] & ~(b->vector[i]); + } + return bm; +} + +/* + * set difference gets everything in A that isn't also in B. A is the + * numerator, so if it's larger we must include any overflow in the + * resulting set. + */ +bitmap *bitmap_diff(const bitmap *a, const bitmap *b) +{ + const bitmap *a_ = a, *b_ = b; + + BITMAP_MATH(a, b) { + bm->vector[i] = a->vector[i] & ~(b->vector[i]); + } + if (a_->alloc > b_->alloc) { + memcpy(&bm->vector[i], &b->vector[i], (b->alloc - a->alloc) * MAPSIZE); + } + return bm; +} + +/* + * symmetric set difference lists all items only present in one set + */ +bitmap *bitmap_symdiff(const bitmap *a, const bitmap *b) +{ + BITMAP_MATH(a, b) { + bm->vector[i] = (a->vector[i] | b->vector[i]) ^ (a->vector[i] & b->vector[i]); + } + if (b->alloc > a->alloc) { + memcpy(&bm->vector[i], &b->vector[i], (b->alloc - a->alloc) * MAPSIZE); + } + return bm; +} + +#define min(a, b) (a > b ? b : a) +int bitmap_cmp(const bitmap *a, const bitmap *b) +{ + int ret; + + ret = memcmp(a->vector, b->vector, min(a->alloc, b->alloc) * MAPSIZE); + if (ret || a->alloc == b->alloc) { + return ret; + } + if (a->alloc > b->alloc) + return 1; + return -1; +} Added: nagioscore/trunk/lib/bitmap.h =================================================================== --- nagioscore/trunk/lib/bitmap.h (rev 0) +++ nagioscore/trunk/lib/bitmap.h 2012-08-20 16:27:12 UTC (rev 2081) @@ -0,0 +1,134 @@ +#ifndef LIBNAGIOS_bitmap_h__ +#define LIBNAGIOS_bitmap_h__ +/** + * @file bitmap.h + * @brief Bit vector API + * @{ + */ +struct bitmap; +typedef struct bitmap bitmap; + +/** + * Create a bitmaptor of size 'size' + * @param size Desired storage capacity + * @return A bitmap pointer on success, NULL on errors + */ +extern bitmap *bitmap_create(unsigned long size); + +/** + * Destroy a bitmaptor by freeing all the memory it uses + * @param bm The bitmaptor to destroy + */ +extern void bitmap_destroy(bitmap *bm); + +/** + * Copy a bitmaptor + * @param bm The bitmaptor to copy + * @return Pointer to an identical bitmap on success, NULL on errors + */ +extern bitmap *bitmap_copy(const bitmap *bm); + +/** + * Set a bit in the vector + * @param bm The bitmaptor to operate on + * @param pos Position of the bit to set + * @return 0 on success, -1 on errors + */ +extern int bitmap_set(bitmap *bm, unsigned long pos); + +/** + * Check if a particular bit is set in the vector + * @param bm The bitmaptor to check + * @param pos Position of the bit to check + * @return 1 if set, otherwise 0 + */ +extern int bitmap_isset(const bitmap *bm, unsigned long pos); + +/** + * Unset a particular bit in the vector + * @param bm The bitmaptor to operate on + * @param pos Position of the bit to unset + */ +extern int bitmap_unset(bitmap *bm, unsigned long pos); + +/** + * Obtain cardinality (max number of elements) of the bitmaptor + * @param bm The bitmaptor to check + * @return The cardinality of the bitmaptor + */ +extern unsigned long bitmap_cardinality(const bitmap *bm); +#define bitmap_size bitmap_cardinality + +/** + * Count set bits in vector. Completed in O(n/8) time. + * @param bm The bitmaptor to count bits in + * @return The number of set bits + */ +extern unsigned long bitmap_count_set_bits(const bitmap *bm); + +/** + * Count unset bits in vector. Completed in O(n/8) time. + * @param bm The bitmaptor to count bits in + * @return The number of set bits + */ +extern unsigned long bitmap_count_unset_bits(const bitmap *bm); + +/** + * Unset all bits in a bitmaptor + * @param bm The bitmaptor to clear + */ +extern bitmap *bitmap_clear(bitmap *bm); + +/** + * Calculate intersection of two bitmaptors + * The intersection is defined as all bits that are members of + * both A and B. It's equivalent to bitwise AND. + * This function completes in O(n/sizeof(long)) operations. + * @param a The first bitmaptor + * @param b The second bitmaptor + * @return NULL on errors; A newly created bitmaptor on success. + */ +extern bitmap *bitmap_intersect(const bitmap *a, const bitmap *b); + +/** + * Calculate union of two bitmaptors + * The union is defined as all bits that are members of + * A or B or both A and B. It's equivalent to bitwise OR. + * This function completes in O(n/sizeof(long)) operations. + * @param a The first bitmaptor + * @param b The second bitmaptor + * @return NULL on errors; A newly created bitmaptor on success. + */ +extern bitmap *bitmap_union(const bitmap *a, const bitmap *b); + +/** + * Calculate set difference between two bitmaptors + * The set difference of A / B is defined as all members of A + * that isn't members of B. Note that parameter ordering matters + * for this function. + * This function completes in O(n/sizeof(long)) operations. + * @param a The first bitmaptor (numerator) + * @param b The first bitmaptor (denominator) + * @return NULL on errors; A newly created bitmaptor on success. + */ +extern bitmap *bitmap_diff(const bitmap *a, const bitmap *b); + +/** + * Calculate symmetric difference between two bitmaptors + * The symmetric difference between A and B is the set that + * contains all elements in either set but not in both. + * This function completes in O(n/sizeof(long)) operations. + * @param a The first bitmaptor + * @param b The second bitmaptor + */ +extern bitmap *bitmap_symdiff(const bitmap *a, const bitmap *b); + +/** + * Compare two bitmaptors for equality + * @param a The first bitmaptor + * @param b The other bitmaptor + * @return Similar to memcmp(), with tiebreaks determined by cardinality + */ +extern int bitmap_cmp(const bitmap *a, const bitmap *b); +/** @} */ +#endif /* LIBNAGIOS_bitmap_h__ */ Modified: nagioscore/trunk/lib/libnagios.h =================================================================== --- nagioscore/trunk/lib/libnagios.h 2012-08-20 16:26:47 UTC (rev 2080) +++ nagioscore/trunk/lib/libnagios.h 2012-08-20 16:27:12 UTC (rev 2081) @@ -13,6 +13,7 @@ #include "iobroker.h" #include "iocache.h" #include "runcmd.h" +#include "bitmap.h" #include "worker.h" #include "skiplist.h" #endif /* LIB_libnagios_h__ */ Added: nagioscore/trunk/lib/test-bitmap.c =================================================================== --- nagioscore/trunk/lib/test-bitmap.c (rev 0) +++ nagioscore/trunk/lib/test-bitmap.c 2012-08-20 16:27:12 UTC (rev 2081) @@ -0,0 +1,66 @@ +#include "t-utils.h" +#include "lnag-utils.h" +#include "bitmap.c" + +#define PRIME 2089 +int main(int argc, char **argv) +{ + bitmap *a = NULL, *b, *r_union, *r_diff, *r_symdiff, *r_intersect; + int i; + int sa[] = { 2, 3, 4, 1783, 1784, 1785 }; + int sb[] = { 1, 2, 3, 1784, 1785, 1786, 1790, 1791, 1792 }; + /* + * intersect: 2, 3, 1784, 1785 + * union: 1, 2, 3, 4, 1783, 1784, 1785, 1786, 1790, 1791, 1792 + * diff A/B: 4, 1783 + * diff B/A: 1, 1786, 1790, 1791, 1792 + * symdiff: 1, 1783, 1786, 1790, 1791, 1792 + */ + + ok_int(bitmap_count_set_bits(a), 0, "counting bits in null vector a"); + a = bitmap_create(PRIME); + b = bitmap_create(PRIME); + ok_int(bitmap_count_set_bits(b), 0, "counting bits in empty vector b"); + + t_set_colors(0); + ok_int(bitmap_cardinality(a) > PRIME, 1, "bitmap cardinality test"); + for (i = 0; i < veclen(sa); i++) { + bitmap_set(a, sa[i]); + } + ok_int(bitmap_count_set_bits(a), veclen(sa), "counting set bits for a"); + + for (i = 0; i < veclen(sb); i++) { + ok_int(0, bitmap_isset(b, sb[i]), "checking unset bit"); + bitmap_set(b, sb[i]); + if (!ok_int(1, bitmap_isset(b, sb[i]), "set and isset should work")) + printf("sb[i]: %d\n", sb[i]); + } + if (!ok_int(bitmap_count_set_bits(b), veclen(sb), "counting set bits for b")) { + for (i = 0; i < PRIME; i++) { + if (bitmap_isset(b, i)) { + ; + } + } + } + + r_union = bitmap_union(a, b); + ok_int(bitmap_count_set_bits(r_union), 11, "bitmap union sets the right amount of bits"); + for (i = 0; i < veclen(sa); i++) { + ok_int(1, bitmap_isset(r_union, sa[i]), "union should have bits from a"); + } + for (i = 0; i < veclen(sb); i++) { + ok_int(1, bitmap_isset(r_union, sb[i]), "union should have bits from b"); + } + + r_diff = bitmap_diff(a, b); + ok_int(bitmap_count_set_bits(r_diff), 2, "diff must set right amount of bits"); + + r_symdiff = bitmap_symdiff(a, b); + ok_int(bitmap_count_set_bits(r_symdiff), 7, "symdiff must set right amount of bits"); + + r_intersect = bitmap_intersect(a, b); + ok_int(bitmap_count_set_bits(r_intersect), 4, "intersect must set right amount of bits"); + + t_end(); + return 0; +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-20 16:27:46
|
Revision: 2082 http://nagios.svn.sourceforge.net/nagios/?rev=2082&view=rev Author: ageric Date: 2012-08-20 16:27:39 +0000 (Mon, 20 Aug 2012) Log Message: ----------- libnagios: Add dkhash, the dual-keyed hash api Since most objects in Nagios are uniquely identified by two keys (as opposed to just one), it's prudent to make it easy to give quick access to such objects with simple API calls, both to Nagios and to all its modules. This library will be used as the basis for object lookups in the near future, as it provides O(1) lookup time, to be compared with the O(n lg n) time provided by skiplists. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/.gitignore nagioscore/trunk/lib/Makefile.in nagioscore/trunk/lib/libnagios.h Added Paths: ----------- nagioscore/trunk/lib/dkhash.c nagioscore/trunk/lib/dkhash.h nagioscore/trunk/lib/test-dkhash.c Modified: nagioscore/trunk/lib/.gitignore =================================================================== --- nagioscore/trunk/lib/.gitignore 2012-08-20 16:27:12 UTC (rev 2081) +++ nagioscore/trunk/lib/.gitignore 2012-08-20 16:27:39 UTC (rev 2082) @@ -3,4 +3,5 @@ test-iocache test-iobroker test-bitmap +test-dkhash wproc Modified: nagioscore/trunk/lib/Makefile.in =================================================================== --- nagioscore/trunk/lib/Makefile.in 2012-08-20 16:27:12 UTC (rev 2081) +++ nagioscore/trunk/lib/Makefile.in 2012-08-20 16:27:39 UTC (rev 2082) @@ -8,7 +8,7 @@ all: $(LIBNAME) SNPRINTF_O=@SNPRINTF_O@ -TESTED_SRC_C := squeue.c kvvec.c iocache.c iobroker.c bitmap.c +TESTED_SRC_C := squeue.c kvvec.c iocache.c iobroker.c bitmap.c dkhash.c SRC_C := $(TESTED_SRC_C) pqueue.c runcmd.c worker.c skiplist.c SRC_O := $(patsubst %.c,%.o,$(SRC_C)) $(SNPRINTF_O) TESTS := $(patsubst %.c,test-%,$(TESTED_SRC_C)) Added: nagioscore/trunk/lib/dkhash.c =================================================================== --- nagioscore/trunk/lib/dkhash.c (rev 0) +++ nagioscore/trunk/lib/dkhash.c 2012-08-20 16:27:39 UTC (rev 2082) @@ -0,0 +1,250 @@ +#include <stdlib.h> +#include <string.h> +#include "dkhash.h" +#include "lnag-utils.h" + +#define dkhash_func(k) sdbm((unsigned char *)k) +#define dkhash_func2(k1, k2) (dkhash_func(k1) ^ dkhash_func(k2)) + +typedef struct dkhash_bucket { + const char *key; + const char *key2; + void *data; + struct dkhash_bucket *next; +} dkhash_bucket; + +struct dkhash_table { + dkhash_bucket **buckets; + unsigned int num_buckets; + unsigned int added, removed; + unsigned int entries; + unsigned int max_entries; +}; + +/* struct data access functions */ +unsigned int dkhash_num_entries(dkhash_table *t) +{ + return t ? t->entries : 0; +} + +unsigned int dkhash_num_entries_max(dkhash_table *t) +{ + return t ? t->max_entries : 0; +} + +unsigned int dkhash_num_entries_added(dkhash_table *t) +{ + return t ? t->added : 0; +} + +unsigned int dkhash_num_entries_removed(dkhash_table *t) +{ + return t ? t->removed : 0; +} + +unsigned int dkhash_table_size(dkhash_table *t) +{ + return t ? t->num_buckets : 0; +} + +/* + * polynomial conversion ignoring overflows + * + * Ozan Yigit's original sdbm algorithm, but without Duff's device + * so we can use it without knowing the length of the string + */ +static inline unsigned int sdbm(register const unsigned char *k) +{ + register unsigned int h = 0; + + while (*k) + h = *k++ + 65599 * h; + + return h; +} + +static int dkhash_insert_bucket(dkhash_table *t, const char *k1, const char *k2, void *data, unsigned int h) +{ + dkhash_bucket *bkt; + + if (!(bkt = malloc(sizeof(*bkt)))) + return -1; + + h &= t->num_buckets - 1; + + t->added++; + bkt->data = data; + bkt->key = k1; + bkt->key2 = k2; + bkt->next = t->buckets[h]; + t->buckets[h] = bkt; + + if (++t->entries > t->max_entries) + t->max_entries = t->entries; + + return 0; +} + +int dkhash_insert(dkhash_table *t, const char *k1, const char *k2, void *data) +{ + unsigned int h = k2 ? dkhash_func2(k1, k2) : dkhash_func(k1); + + return dkhash_insert_bucket(t, k1, k2, data, h); +} + +static dkhash_bucket *dkhash_get_bucket(dkhash_table *t, const char *key) +{ + dkhash_bucket *bkt; + + if (!t) + return NULL; + + bkt = t->buckets[dkhash_func(key) % t->num_buckets]; + for (; bkt; bkt = bkt->next) { + if (!strcmp(key, bkt->key)) + return bkt; + } + + return NULL; +} + +static dkhash_bucket *dkhash_get_bucket2(dkhash_table *t, const char *k1, const char *k2) +{ + dkhash_bucket *bkt; + + if (!t) + return NULL; + + bkt = t->buckets[dkhash_func2(k1, k2) % t->num_buckets]; + for (; bkt; bkt = bkt->next) { + if (!strcmp(k1, bkt->key) && !strcmp(k2, bkt->key2)) + return bkt; + } + + return NULL; +} + +void *dkhash_get(dkhash_table *t, const char *k1, const char *k2) +{ + dkhash_bucket *bkt; + + if (k2) + bkt = dkhash_get_bucket2(t, k1, k2); + else + bkt = dkhash_get_bucket(t, k1); + + return bkt ? bkt->data : NULL; +} + +dkhash_table *dkhash_create(unsigned int size) +{ + dkhash_table *t = calloc(sizeof(dkhash_table), 1); + unsigned int po2; + + if (!size) + return NULL; + + /* we'd like size to be a power of 2, so we round up */ + po2 = rup2pof2(size); + + if (t) { + t->buckets = calloc(po2, sizeof(dkhash_bucket *)); + if (t->buckets) { + t->num_buckets = po2; + return t; + } + + free(t); + } + + return NULL; +} + +void *dkhash_update(dkhash_table *t, const char *k1, const char *k2, void *data) +{ + dkhash_bucket *bkt; + + bkt = dkhash_get_bucket2(t, k1, k2); + if (!bkt) { + dkhash_insert(t, k1, k2, data); + return NULL; + } + + bkt->data = data; + return NULL; +} + +static inline void *dkhash_destroy_bucket(dkhash_bucket *bkt) +{ + void *data; + + data = bkt->data; + free(bkt); + return data; +} + +void *dkhash_remove(dkhash_table *t, const char *k1, const char *k2) +{ + unsigned int h; + dkhash_bucket *bkt, *prev; + + h = k2 ? dkhash_func2(k1, k2) : dkhash_func(k1); + h &= t->num_buckets - 1; + + if (!(bkt = t->buckets[h])) + return NULL; + + if (!strcmp(k1, bkt->key) && !strcmp(k2, bkt->key2)) { + t->buckets[h] = bkt->next; + t->entries--; + t->removed++; + return dkhash_destroy_bucket(bkt); + } + + prev = bkt; + for (bkt = bkt->next; bkt; bkt = bkt->next) { + if (!strcmp(k1, bkt->key) && !strcmp(k2, bkt->key2)) { + prev->next = bkt->next; + t->entries--; + t->removed++; + return dkhash_destroy_bucket(bkt); + } + } + + return NULL; +} + +void dkhash_walk_data(dkhash_table *t, int (*walker)(void *)) +{ + dkhash_bucket *bkt, *prev; + unsigned int i; + + if (!t->entries) + return; + + for (i = 0; i < t->num_buckets; i++) { + int depth = 0; + + prev = t->buckets[i]; + dkhash_bucket *next; + for (bkt = t->buckets[i]; bkt; bkt = next) { + next = bkt->next; + + if (walker(bkt->data) != DKHASH_WALK_REMOVE) { + /* only update prev if we don't remove current */ + prev = bkt; + depth++; + continue; + } + t->removed++; + t->entries--; + dkhash_destroy_bucket(bkt); + if (depth) { + prev->next = next; + } + else { + t->buckets[i] = next; + } + } + } +} Added: nagioscore/trunk/lib/dkhash.h =================================================================== --- nagioscore/trunk/lib/dkhash.h (rev 0) +++ nagioscore/trunk/lib/dkhash.h 2012-08-20 16:27:39 UTC (rev 2082) @@ -0,0 +1,122 @@ +#ifndef LIBNAGIOS_dkhash_h__ +#define LIBNAGIOS_dkhash_h__ + +/** + * @file dkhash.h + * @brief Dual-key hash functions for Nagios + * + * Having a dual-key hash function is pretty unusual, but since so + * much data in Nagios pertains to services (which are uniquely + * identified based on both host_name and service_description), it + * makes sense here. + */ + +/** return flags usable from the callback function of dkhash_walk_data() */ +#define DKHASH_WALK_REMOVE 1 /**< Remove the most recently visited object */ +#define DKHASH_WALK_STOP 2 /**< Cause walking to stop */ + +struct dkhash_table; +/** opaque type */ +typedef struct dkhash_table dkhash_table; + +/** + * Create a dual-keyed hash-table of the given size + * Note that it's generally useful to make the table 25-30% larger + * than the number of items you intend to store, and also note that + * the 'size' arguments gets rounded up to the nearest power of 2. + * @param size The desired size of the hash-table. + */ +extern dkhash_table *dkhash_create(unsigned int size); + +/** + * Destroy a dual-keyed hash table + * @param t The table to destroy + * @return 0 on success, -1 on errors + */ +extern int dkhash_destroy(dkhash_table *t); + +/** + * Fetch the data associated with a particular key + * @param t The table to get the data from + * @param k1 The first key + * @param k2 The second key + * @return The data on success, NULL on errors + */ +extern void *dkhash_get(dkhash_table *t, const char *k1, const char *k2); + +/** + * Insert a new entry into the hash table + * @param t The hash table + * @param k1 The first key + * @param k2 The second key (may be null) + * @param data The data to insert + * @return 0 on success, < 0 on errors + */ +extern int dkhash_insert(dkhash_table *t, const char *k1, const char *k2, void *data); + +/** + * Remove data from the hash table + * Note that this does not free() the pointer to the data stored in the + * table. It just destroys containers for that data in the hash table. + * @param t The hash table + * @param k1 The first key + * @param k2 The second key + * @return The removed data on success, or NULL on errors + */ +extern void *dkhash_remove(dkhash_table *t, const char *k1, const char *k2); + +/** + * Update the data associated with a particular set of keys + * @param t The hash table + * @param k1 The first key + * @param k2 The second key + * @param data The new data for the keys + */ +extern void *dkhash_update(dkhash_table *t, const char *k1, const char *k2, void *data); + +/** + * Call a funciton once for each item in the hash-table + * The callback function can return DKHASH_WALK_{REMOVE,STOP} or any + * OR'ed combination thereof to control the walking procedure, and + * should return 0 on the normal case. + * @param t The hash table + * @param walker The callback function to send the data to + */ +extern void dkhash_walk_data(dkhash_table *t, int (*walker)(void *data)); + +/** + * Get number of items in the hash table + * @param t The hash table + * @return Number of items currently in the hash-table + */ +extern unsigned int dkhash_num_items(dkhash_table *t); + +/** + * Get max number of items stored in the hash table + * @param t The hash table + * @return Max number of items stored in hash-table + */ +extern unsigned int dkhash_num_entries_max(dkhash_table *t); + +/** + * Get number of entries added to hash table + * Note that some of them may have been removed. + * @param t The hash table + * @return The number of items added to the table + */ +extern unsigned int dkhash_num_entries_added(dkhash_table *t); + +/** + * Get number of removed items from hash table + * @param t The hash table + * @return Number of items removed from hash table + */ +extern unsigned int dkhash_num_entries_removed(dkhash_table *t); + +/** + * Get actual table size (in number of buckets) + * @param t The hash table + * @return Number of bucket-slots in hash table + */ +extern unsigned int dkhash_table_size(dkhash_table *t); +#endif /* LIBNAGIOS_dkhash_h__ */ Modified: nagioscore/trunk/lib/libnagios.h =================================================================== --- nagioscore/trunk/lib/libnagios.h 2012-08-20 16:27:12 UTC (rev 2081) +++ nagioscore/trunk/lib/libnagios.h 2012-08-20 16:27:39 UTC (rev 2082) @@ -14,6 +14,7 @@ #include "iocache.h" #include "runcmd.h" #include "bitmap.h" +#include "dkhash.h" #include "worker.h" #include "skiplist.h" #endif /* LIB_libnagios_h__ */ Added: nagioscore/trunk/lib/test-dkhash.c =================================================================== --- nagioscore/trunk/lib/test-dkhash.c (rev 0) +++ nagioscore/trunk/lib/test-dkhash.c 2012-08-20 16:27:39 UTC (rev 2082) @@ -0,0 +1,190 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "dkhash.c" +#include "t-utils.h" + +static struct { + char *k1, *k2; +} keys[] = { + { "nisse", "banan" }, + { "foo", "bar" }, + { "kalle", "penslar" }, + { "hello", "world" }, + { "test", "fnurg" }, + { "bar", "nitfol" }, + { "andreas", "regerar" }, +}; + +static int removed; +static struct test_data { + int x, i, j; +} del; + +unsigned int dkhash_count_entries(dkhash_table *table) +{ + unsigned int i, count = 0; + + for (i = 0; i < table->num_buckets; i++) { + dkhash_bucket *bkt; + for (bkt = table->buckets[i]; bkt; bkt = bkt->next) + count++; + } + + return count; +} + +int dkhash_check_table(dkhash_table *t) +{ + return t ? t->entries - dkhash_count_entries(t) : 0; +} + +void dkhash_debug_print_table(dkhash_table *t, const char *name, int force) +{ + int delta = dkhash_check_table(t); + unsigned int count; + if (!delta && !force) + return; + + count = dkhash_count_entries(t); + printf("debug data for dkhash table '%s'\n", name); + printf(" entries: %u; counted: %u; delta: %d\n", + t->entries, count, delta); + printf(" added: %u; removed: %u; delta: %d\n", + t->added, t->removed, t->added - t->removed); +} +#define dkhash_debug_table(t, force) dkhash_debug_print_table(t, #t, force) + +static struct test_data *ddup(int x, int i, int j) +{ + struct test_data *d; + + d = malloc(sizeof(*d)); + d->x = x; + d->i = i; + d->j = j; + return d; +} + +struct dkhash_check { + uint entries, count, max, added, removed; + int ent_delta, addrm_delta; +}; + +static int del_matching(void *data) +{ + struct test_data *d = (struct test_data *)data; + + if (!memcmp(d, &del, sizeof(del))) { + removed++; + return DKHASH_WALK_REMOVE; + } + + return 0; +} + +int main(int argc, char **argv) +{ + dkhash_table *ti, *tj, *tx, *t; + uint i, j, x; + struct test_data s; + char *p1, *p2; + + t_set_colors(0); + t_start("dkhash basic test"); + t_verbose = 1; + t = dkhash_create(512); + + p1 = strdup("a not-so secret value"); + dkhash_insert(t, "nisse", NULL, p1); + p2 = dkhash_get(t, "nisse", NULL); + test(p1 == p2, "get should get what insert set"); + dkhash_insert(t, "kalle", "bananas", p1); + p2 = dkhash_get(t, "kalle", "bezinga"); + test(p1 != p2, "we should never get the wrong key"); + ok_int(2, dkhash_num_entries(t), "should be 2 entries after 2 inserts"); + p2 = dkhash_remove(t, "kalle", "bezinga"); + ok_int(2, dkhash_num_entries(t), "should be 2 entries after 2 inserts and 1 failed remove"); + ok_int(0, dkhash_num_entries_removed(t), "should be 0 removed entries after failed remove"); + p2 = dkhash_remove(t, "kalle", "bananas"); + test(p1 == p2, "dkhash_remove() should return removed data"); + ok_int(dkhash_num_entries(t), 1, "should be 1 entries after 2 inserts and 1 successful remove"); + t_end(); + + /* lots of tests below, so we shut up while they're running */ + t_verbose = 0; + + t_start("dkhash_walk_data() test"); + memset(&s, 0, sizeof(s)); + /* first we set up the dkhash-tables */ + ti = dkhash_create(16); + tj = dkhash_create(16); + tx = dkhash_create(16); + x = i = j = 0; + for (x = 0; x < ARRAY_SIZE(keys); x++) { + dkhash_insert(tx, keys[x].k1, NULL, ddup(x, 0, 0)); + dkhash_insert(tx, keys[x].k2, NULL, ddup(x, 0, 0)); + dkhash_insert(tx, keys[x].k1, keys[x].k2, ddup(x, 0, 0)); + s.x += 3; + ok_int(s.x, dkhash_num_entries(tx), "x table adding"); + + for (i = 0; i < ARRAY_SIZE(keys); i++) { + dkhash_insert(ti, keys[i].k1, NULL, ddup(x, i, 0)); + dkhash_insert(ti, keys[i].k1, NULL, ddup(x, i, 0)); + dkhash_insert(ti, keys[i].k1, keys[i].k2, ddup(x, i, 0)); + s.i += 3; + ok_int(s.i, dkhash_num_entries(ti), "i table adding"); + + for (j = 0; j < ARRAY_SIZE(keys); j++) { + dkhash_insert(tj, keys[j].k1, NULL, ddup(x, i, j)); + dkhash_insert(tj, keys[j].k2, NULL, ddup(x, i, j)); + dkhash_insert(tj, keys[j].k1, keys[j].k2, ddup(x, i, j)); + s.j += 3; + ok_int(s.j, dkhash_num_entries(tj), "j table adding"); + } + } + } + + ok_int(s.x, dkhash_num_entries(tx), "x table done adding"); + ok_int(s.i, dkhash_num_entries(ti), "i table done adding"); + ok_int(s.j, dkhash_num_entries(tj), "j table done adding"); + + for (x = 0; x < ARRAY_SIZE(keys); x++) { + del.x = x; + del.i = del.j = 0; + + ok_int(s.x, dkhash_num_entries(tx), "x table pre-delete"); + s.x -= 3; + dkhash_walk_data(tx, del_matching); + ok_int(s.x, dkhash_num_entries(tx), "x table post-delete"); + + for (i = 0; i < ARRAY_SIZE(keys); i++) { + del.i = i; + del.j = 0; + ok_int(s.i, dkhash_num_entries(ti), "i table pre-delete"); + dkhash_walk_data(ti, del_matching); + s.i -= 3; + ok_int(s.i, dkhash_num_entries(ti), "i table post-delete"); + + for (j = 0; j < ARRAY_SIZE(keys); j++) { + del.j = j; + ok_int(s.j, dkhash_num_entries(tj), "j table pre-delete"); + dkhash_walk_data(tj, del_matching); + s.j -= 3; + ok_int(s.j, dkhash_num_entries(tj), "j table post-delete"); + } + } + } + + test(0 == dkhash_num_entries(tx), "x table post all ops"); + test(0 == dkhash_check_table(tx), "x table consistency post all ops"); + test(0 == dkhash_num_entries(ti), "i table post all ops"); + test(0 == dkhash_check_table(ti), "i table consistency post all ops"); + test(0 == dkhash_num_entries(tj), "j table post all ops"); + test(0 == dkhash_check_table(tj), "j table consistency post all ops"); + dkhash_debug_table(tx, 0); + dkhash_debug_table(ti, 0); + dkhash_debug_table(tj, 0); + + return t_end(); +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-26 19:58:34
|
Revision: 2086 http://nagios.svn.sourceforge.net/nagios/?rev=2086&view=rev Author: ageric Date: 2012-08-26 19:58:26 +0000 (Sun, 26 Aug 2012) Log Message: ----------- lib/bitmap: Janitor fixes Remove debug printf()'s and s/bitmaptors/bitmaps/ Blind search and replace from 'bitvector' to 'bitmap' caused the latter, while sloppiness caused the former. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/bitmap.c nagioscore/trunk/lib/bitmap.h Modified: nagioscore/trunk/lib/bitmap.c =================================================================== --- nagioscore/trunk/lib/bitmap.c 2012-08-26 19:58:04 UTC (rev 2085) +++ nagioscore/trunk/lib/bitmap.c 2012-08-26 19:58:26 UTC (rev 2086) @@ -27,10 +27,6 @@ /* be tight on space */ bm->alloc = (size >> SHIFTOUT) + !!(size & MAPMASK); bm->vector = calloc(1, bm->alloc * sizeof(bmap)); -#if 0 - printf("bitmap created with %lu bytes of storage and capacity %lu\n", - bm->alloc * sizeof(bmap), bm->alloc * MAPSIZE); -#endif return bm; } Modified: nagioscore/trunk/lib/bitmap.h =================================================================== --- nagioscore/trunk/lib/bitmap.h 2012-08-26 19:58:04 UTC (rev 2085) +++ nagioscore/trunk/lib/bitmap.h 2012-08-26 19:58:26 UTC (rev 2086) @@ -2,7 +2,7 @@ #define LIBNAGIOS_bitmap_h__ /** * @file bitmap.h - * @brief Bit vector API + * @brief Bit map API * @{ */ struct bitmap; @@ -29,7 +29,7 @@ extern bitmap *bitmap_copy(const bitmap *bm); /** - * Set a bit in the vector + * Set a bit in the map * @param bm The bitmaptor to operate on * @param pos Position of the bit to set * @return 0 on success, -1 on errors @@ -37,7 +37,7 @@ extern int bitmap_set(bitmap *bm, unsigned long pos); /** - * Check if a particular bit is set in the vector + * Check if a particular bit is set in the map * @param bm The bitmaptor to check * @param pos Position of the bit to check * @return 1 if set, otherwise 0 @@ -45,7 +45,7 @@ extern int bitmap_isset(const bitmap *bm, unsigned long pos); /** - * Unset a particular bit in the vector + * Unset a particular bit in the map * @param bm The bitmaptor to operate on * @param pos Position of the bit to unset */ @@ -60,14 +60,14 @@ #define bitmap_size bitmap_cardinality /** - * Count set bits in vector. Completed in O(n/8) time. + * Count set bits in map. Completed in O(n/8) time. * @param bm The bitmaptor to count bits in * @return The number of set bits */ extern unsigned long bitmap_count_set_bits(const bitmap *bm); /** - * Count unset bits in vector. Completed in O(n/8) time. + * Count unset bits in map. Completed in O(n/8) time. * @param bm The bitmaptor to count bits in * @return The number of set bits */ @@ -80,7 +80,7 @@ extern bitmap *bitmap_clear(bitmap *bm); /** - * Calculate intersection of two bitmaptors + * Calculate intersection of two bitmaps * The intersection is defined as all bits that are members of * both A and B. It's equivalent to bitwise AND. * This function completes in O(n/sizeof(long)) operations. @@ -91,7 +91,7 @@ extern bitmap *bitmap_intersect(const bitmap *a, const bitmap *b); /** - * Calculate union of two bitmaptors + * Calculate union of two bitmaps * The union is defined as all bits that are members of * A or B or both A and B. It's equivalent to bitwise OR. * This function completes in O(n/sizeof(long)) operations. @@ -102,7 +102,7 @@ extern bitmap *bitmap_union(const bitmap *a, const bitmap *b); /** - * Calculate set difference between two bitmaptors + * Calculate set difference between two bitmaps * The set difference of A / B is defined as all members of A * that isn't members of B. Note that parameter ordering matters * for this function. @@ -114,7 +114,7 @@ extern bitmap *bitmap_diff(const bitmap *a, const bitmap *b); /** - * Calculate symmetric difference between two bitmaptors + * Calculate symmetric difference between two bitmaps * The symmetric difference between A and B is the set that * contains all elements in either set but not in both. * This function completes in O(n/sizeof(long)) operations. @@ -124,7 +124,7 @@ extern bitmap *bitmap_symdiff(const bitmap *a, const bitmap *b); /** - * Compare two bitmaptors for equality + * Compare two bitmaps for equality * @param a The first bitmaptor * @param b The other bitmaptor * @return Similar to memcmp(), with tiebreaks determined by cardinality This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-26 19:59:36
|
Revision: 2089 http://nagios.svn.sourceforge.net/nagios/?rev=2089&view=rev Author: ageric Date: 2012-08-26 19:59:30 +0000 (Sun, 26 Aug 2012) Log Message: ----------- lib/bitmap: Add bitmap_resize() and use it in initialization It's nifty to be able to reuse a single bitmap instead of having to allocate a new one for a lot of tasks. This makes it possible to grow and shrink the bitmap dynamically to fit the needs of the programmer. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/bitmap.c nagioscore/trunk/lib/bitmap.h Modified: nagioscore/trunk/lib/bitmap.c =================================================================== --- nagioscore/trunk/lib/bitmap.c 2012-08-26 19:59:07 UTC (rev 2088) +++ nagioscore/trunk/lib/bitmap.c 2012-08-26 19:59:30 UTC (rev 2089) @@ -16,23 +16,64 @@ struct bitmap { bmap *vector; - unsigned int alloc; + unsigned long alloc; }; +int bitmap_resize(bitmap *bm, unsigned long size) +{ + unsigned long ralloc; + bmap *nvec; + + if (!bm) + return -1; + + /* be tight on space */ + ralloc = (size >> SHIFTOUT) + !!(size & MAPMASK); + + if (!bm->vector) { + bm->vector = calloc(1, ralloc * sizeof(bmap)); + if (!bm->vector) + return -1; + bm->alloc = ralloc; + return 0; + } + + nvec = realloc(bm->vector, ralloc * sizeof(bmap)); + if (!nvec) { + return -1; + } + bm->vector = nvec; + bm->alloc = ralloc; + return 0; +} + static bitmap *bitmap_init(bitmap *bm, unsigned long size) { + int ret; + if (!bm) return NULL; - /* be tight on space */ - bm->alloc = (size >> SHIFTOUT) + !!(size & MAPMASK); - bm->vector = calloc(1, bm->alloc * sizeof(bmap)); + ret = bitmap_resize(bm, size); + if (ret < 0) + return NULL; + return bm; } bitmap *bitmap_create(unsigned long size) { - return bitmap_init(calloc(1, sizeof(bitmap)), size); + bitmap *bm; + + if (!(bm = calloc(1, sizeof(bitmap)))) + return NULL; + + if (bitmap_init(bm, size) == bm) + return bm; + + free(bm); + + return NULL; } void bitmap_destroy(bitmap *bm) Modified: nagioscore/trunk/lib/bitmap.h =================================================================== --- nagioscore/trunk/lib/bitmap.h 2012-08-26 19:59:07 UTC (rev 2088) +++ nagioscore/trunk/lib/bitmap.h 2012-08-26 19:59:30 UTC (rev 2089) @@ -9,6 +9,16 @@ typedef struct bitmap bitmap; /** + * Resize a bitmap + * If the bitmap is made smaller, data will silently be lost. + * + * @param bm The bitmap to resize + * @param size The new desired size of the bitmap + * @return 0 on success, -1 on errors. + */ +extern int bitmap_resize(bitmap *bm, unsigned long size); + +/** * Create a bitmaptor of size 'size' * @param size Desired storage capacity * @return A bitmap pointer on success, NULL on errors This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-26 19:59:56
|
Revision: 2090 http://nagios.svn.sourceforge.net/nagios/?rev=2090&view=rev Author: ageric Date: 2012-08-26 19:59:49 +0000 (Sun, 26 Aug 2012) Log Message: ----------- lib/bitmap: Add bitmap_unite() This function takes the union of two bitmaps but returns the result in the first bitmap passed. It's incredibly useful when doing dynamic programming and adding whole sets at a time to the same existing set. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/bitmap.c nagioscore/trunk/lib/bitmap.h Modified: nagioscore/trunk/lib/bitmap.c =================================================================== --- nagioscore/trunk/lib/bitmap.c 2012-08-26 19:59:30 UTC (rev 2089) +++ nagioscore/trunk/lib/bitmap.c 2012-08-26 19:59:49 UTC (rev 2090) @@ -236,6 +236,20 @@ return bm; } +bitmap *bitmap_unite(bitmap *res, const bitmap *addme) +{ + unsigned int i; + + if (bitmap_size(addme) > bitmap_size(res)) { + bitmap_resize(res, bitmap_size(addme)); + } + + for (i = 0; i < addme->alloc; i++) { + res->vector[i] |= addme->vector[i]; + } + return res; +} + /* * Remove all elements from a that are also in b. */ Modified: nagioscore/trunk/lib/bitmap.h =================================================================== --- nagioscore/trunk/lib/bitmap.h 2012-08-26 19:59:30 UTC (rev 2089) +++ nagioscore/trunk/lib/bitmap.h 2012-08-26 19:59:49 UTC (rev 2090) @@ -112,6 +112,14 @@ extern bitmap *bitmap_union(const bitmap *a, const bitmap *b); /** + * Calculate union of two bitmaps and store result in one of them + * @param res The first bitmap + * @param addme The bitmap to unite to the first bitmap + * @return NULL on errors, res on success + */ +extern bitmap *bitmap_unite(bitmap *res, const bitmap *addme); + +/** * Calculate set difference between two bitmaps * The set difference of A / B is defined as all members of A * that isn't members of B. Note that parameter ordering matters This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-26 20:00:32
|
Revision: 2091 http://nagios.svn.sourceforge.net/nagios/?rev=2091&view=rev Author: ageric Date: 2012-08-26 20:00:26 +0000 (Sun, 26 Aug 2012) Log Message: ----------- lib/bitmap: Add the missing bitmap_clear() function And fix up some comments while we're at it. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/bitmap.c nagioscore/trunk/lib/bitmap.h Modified: nagioscore/trunk/lib/bitmap.c =================================================================== --- nagioscore/trunk/lib/bitmap.c 2012-08-26 19:59:49 UTC (rev 2090) +++ nagioscore/trunk/lib/bitmap.c 2012-08-26 20:00:26 UTC (rev 2091) @@ -19,6 +19,12 @@ unsigned long alloc; }; +void bitmap_clear(bitmap *bm) +{ + if (bm) + memset(bm->vector, 0, bm->alloc * sizeof(bmap)); +} + int bitmap_resize(bitmap *bm, unsigned long size) { unsigned long ralloc; Modified: nagioscore/trunk/lib/bitmap.h =================================================================== --- nagioscore/trunk/lib/bitmap.h 2012-08-26 19:59:49 UTC (rev 2090) +++ nagioscore/trunk/lib/bitmap.h 2012-08-26 20:00:26 UTC (rev 2091) @@ -84,10 +84,10 @@ extern unsigned long bitmap_count_unset_bits(const bitmap *bm); /** - * Unset all bits in a bitmaptor - * @param bm The bitmaptor to clear + * Unset all bits in a bitmap + * @param bm The bitmap to clear */ -extern bitmap *bitmap_clear(bitmap *bm); +extern void bitmap_clear(bitmap *bm); /** * Calculate intersection of two bitmaps This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-08-26 20:02:22
|
Revision: 2095 http://nagios.svn.sourceforge.net/nagios/?rev=2095&view=rev Author: ageric Date: 2012-08-26 20:02:16 +0000 (Sun, 26 Aug 2012) Log Message: ----------- lib/skiplist: Add accessor function to get number of items in skiplist Since the types were made opaque, we need this in order to be able to get the size of the skiplist at all. Signed-off-by: Andreas Ericsson <ae...@op...> Modified Paths: -------------- nagioscore/trunk/lib/skiplist.c nagioscore/trunk/lib/skiplist.h Modified: nagioscore/trunk/lib/skiplist.c =================================================================== --- nagioscore/trunk/lib/skiplist.c 2012-08-26 20:01:55 UTC (rev 2094) +++ nagioscore/trunk/lib/skiplist.c 2012-08-26 20:02:16 UTC (rev 2095) @@ -52,6 +52,9 @@ skiplistnode *head; } skiplist; +unsigned long skiplist_num_items(skiplist *list) { + return list ? list->items : 0; + } static skiplistnode *skiplist_new_node(skiplist *list, int node_levels) { skiplistnode *newnode = NULL; Modified: nagioscore/trunk/lib/skiplist.h =================================================================== --- nagioscore/trunk/lib/skiplist.h 2012-08-26 20:01:55 UTC (rev 2094) +++ nagioscore/trunk/lib/skiplist.h 2012-08-26 20:02:16 UTC (rev 2095) @@ -33,6 +33,7 @@ struct skiplist_struct; typedef struct skiplist_struct skiplist; +unsigned long skiplist_num_items(skiplist *list); skiplist *skiplist_new(int max_levels, float level_probability, int allow_duplicates, int append_duplicates, int (*compare_function)(void *, void *)); int skiplist_insert(skiplist *list, void *data); int skiplist_empty(skiplist *list); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |