]> err.no Git - systemd/commitdiff
initial commit
authorLennart Poettering <lennart@poettering.net>
Tue, 17 Nov 2009 23:42:52 +0000 (00:42 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 17 Nov 2009 23:42:52 +0000 (00:42 +0100)
20 files changed:
.gitignore [new file with mode: 0644]
CODING_STYLE [new file with mode: 0644]
Makefile [new file with mode: 0644]
hashmap.c [new file with mode: 0644]
hashmap.h [new file with mode: 0644]
job.c [new file with mode: 0644]
job.h [new file with mode: 0644]
list.h [new file with mode: 0644]
macro.h [new file with mode: 0644]
main.c [new file with mode: 0644]
manager.c [new file with mode: 0644]
manager.h [new file with mode: 0644]
name.c [new file with mode: 0644]
name.h [new file with mode: 0644]
set.c [new file with mode: 0644]
set.h [new file with mode: 0644]
strv.c [new file with mode: 0644]
strv.h [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..6169dd0
--- /dev/null
@@ -0,0 +1,2 @@
+systemd
+*.o
diff --git a/CODING_STYLE b/CODING_STYLE
new file mode 100644 (file)
index 0000000..1b8c8cf
--- /dev/null
@@ -0,0 +1,16 @@
+
+- 8ch indent, no tabs
+
+- structs in MixedCase, variables, functions in lower_case
+
+- the destructors always unregister the object from the next bigger
+  object, not the other way around
+
+- to minimize strict aliasing violations we prefer unions over casting
+
+- for robustness reasons destructors should be able to destruct
+  half-initialized objects, too
+
+- error codes are returned as negative Exxx. i.e. return EINVAL. There
+  are some exceptions: for constructors its is OK to return NULL on
+  OOM. For lookup functions NULL is fine too for "not found".
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..ee144a3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+CFLAGS=-Wall -Wextra -O0 -g -pipe
+LIBS=-lrt
+
+systemd: main.o name.o util.o set.o hashmap.o strv.o job.o manager.o
+       $(CC) $(CFLAGS) -o $@ $^  $(LIBS)
+
+clean:
+       rm -f *.o systemd
diff --git a/hashmap.c b/hashmap.c
new file mode 100644 (file)
index 0000000..1b2e059
--- /dev/null
+++ b/hashmap.c
@@ -0,0 +1,325 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "macro.h"
+
+#define NBUCKETS 127
+
+struct hashmap_entry {
+        const void *key;
+        void *value;
+
+        struct hashmap_entry *bucket_next, *bucket_previous;
+        struct hashmap_entry *iterate_next, *iterate_previous;
+};
+
+struct Hashmap {
+        hash_func_t hash_func;
+        compare_func_t compare_func;
+
+        struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+        unsigned n_entries;
+};
+
+#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
+
+unsigned string_hash_func(const void *p) {
+        unsigned hash = 0;
+        const char *c;
+
+        for (c = p; *c; c++)
+                hash = 31 * hash + (unsigned) *c;
+
+        return hash;
+}
+
+int string_compare_func(const void *a, const void *b) {
+        return strcmp(a, b);
+}
+
+unsigned trivial_hash_func(const void *p) {
+        return PTR_TO_UINT(p);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
+        Hashmap *h;
+
+        if (!(h = malloc0(ALIGN(sizeof(Hashmap)) + NBUCKETS * ALIGN(sizeof(struct hashmap_entry*)))))
+                return NULL;
+
+        h->hash_func = hash_func ? hash_func : trivial_hash_func;
+        h->compare_func = compare_func ? compare_func : trivial_compare_func;
+
+        h->n_entries = 0;
+        h->iterate_list_head = h->iterate_list_tail = NULL;
+
+        return h;
+}
+
+static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
+        assert(h);
+        assert(e);
+
+        /* Remove from iteration list */
+        if (e->iterate_next)
+                e->iterate_next->iterate_previous = e->iterate_previous;
+        else
+                h->iterate_list_tail = e->iterate_previous;
+
+        if (e->iterate_previous)
+                e->iterate_previous->iterate_next = e->iterate_next;
+        else
+                h->iterate_list_head = e->iterate_next;
+
+        /* Remove from hash table bucket list */
+        if (e->bucket_next)
+                e->bucket_next->bucket_previous = e->bucket_previous;
+
+        if (e->bucket_previous)
+                e->bucket_previous->bucket_next = e->bucket_next;
+        else {
+                unsigned hash = h->hash_func(e->key) % NBUCKETS;
+                BY_HASH(h)[hash] = e->bucket_next;
+        }
+
+        free(e);
+
+        assert(h->n_entries >= 1);
+        h->n_entries--;
+}
+
+void hashmap_free(Hashmap*h) {
+
+        if (!h)
+                return;
+
+        while (h->iterate_list_head)
+                remove_entry(h, h->iterate_list_head);
+
+        free(h);
+}
+
+static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
+        struct hashmap_entry *e;
+        assert(h);
+        assert(hash < NBUCKETS);
+
+        for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
+                if (h->compare_func(e->key, key) == 0)
+                        return e;
+
+        return NULL;
+}
+
+int hashmap_put(Hashmap *h, const void *key, void *value) {
+        struct hashmap_entry *e;
+        unsigned hash;
+
+        assert(h);
+
+        hash = h->hash_func(key) % NBUCKETS;
+
+        if (hash_scan(h, hash, key))
+                return -EEXIST;
+
+        if (!(e = new(struct hashmap_entry, 1)))
+                return -ENOMEM;
+
+        e->key = key;
+        e->value = value;
+
+        /* Insert into hash table */
+        e->bucket_next = BY_HASH(h)[hash];
+        e->bucket_previous = NULL;
+        if (BY_HASH(h)[hash])
+                BY_HASH(h)[hash]->bucket_previous = e;
+        BY_HASH(h)[hash] = e;
+
+        /* Insert into iteration list */
+        e->iterate_previous = h->iterate_list_tail;
+        e->iterate_next = NULL;
+        if (h->iterate_list_tail) {
+                assert(h->iterate_list_head);
+                h->iterate_list_tail->iterate_next = e;
+        } else {
+                assert(!h->iterate_list_head);
+                h->iterate_list_head = e;
+        }
+        h->iterate_list_tail = e;
+
+        h->n_entries++;
+        assert(h->n_entries >= 1);
+
+        return 0;
+}
+
+void* hashmap_get(Hashmap *h, const void *key) {
+        unsigned hash;
+        struct hashmap_entry *e;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % NBUCKETS;
+
+        if (!(e = hash_scan(h, hash, key)))
+                return NULL;
+
+        return e->value;
+}
+
+void* hashmap_remove(Hashmap *h, const void *key) {
+        struct hashmap_entry *e;
+        unsigned hash;
+        void *data;
+
+        if (!h)
+                return NULL;
+
+        hash = h->hash_func(key) % NBUCKETS;
+
+        if (!(e = hash_scan(h, hash, key)))
+                return NULL;
+
+        data = e->value;
+        remove_entry(h, e);
+
+        return data;
+}
+
+void *hashmap_iterate(Hashmap *h, void **state, const void **key) {
+        struct hashmap_entry *e;
+
+        assert(state);
+
+        if (!h)
+                goto at_end;
+
+        if (*state == (void*) -1)
+                goto at_end;
+
+        if (!*state && !h->iterate_list_head)
+                goto at_end;
+
+        e = *state ? *state : h->iterate_list_head;
+
+        if (e->iterate_next)
+                *state = e->iterate_next;
+        else
+                *state = (void*) -1;
+
+        if (key)
+                *key = e->key;
+
+        return e->value;
+
+at_end:
+        *state = (void *) -1;
+
+        if (key)
+                *key = NULL;
+
+        return NULL;
+}
+
+void *hashmap_iterate_backwards(Hashmap *h, void **state, const void **key) {
+        struct hashmap_entry *e;
+
+        assert(state);
+
+        if (!h)
+                goto at_beginning;
+
+        if (*state == (void*) -1)
+                goto at_beginning;
+
+        if (!*state && !h->iterate_list_tail)
+                goto at_beginning;
+
+        e = *state ? *state : h->iterate_list_tail;
+
+        if (e->iterate_previous)
+                *state = e->iterate_previous;
+        else
+                *state = (void*) -1;
+
+        if (key)
+                *key = e->key;
+
+        return e->value;
+
+at_beginning:
+        *state = (void *) -1;
+
+        if (key)
+                *key = NULL;
+
+        return NULL;
+}
+
+void* hashmap_first(Hashmap *h) {
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        return h->iterate_list_head->value;
+}
+
+void* hashmap_last(Hashmap *h) {
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_tail)
+                return NULL;
+
+        return h->iterate_list_tail->value;
+}
+
+void* hashmap_steal_first(Hashmap *h) {
+        void *data;
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        data = h->iterate_list_head->value;
+        remove_entry(h, h->iterate_list_head);
+
+        return data;
+}
+
+unsigned hashmap_size(Hashmap *h) {
+
+        if (!h)
+                return 0;
+
+        return h->n_entries;
+}
+
+bool hashmap_isempty(Hashmap *h) {
+
+        if (!h)
+                return true;
+
+        return h->n_entries == 0;
+}
diff --git a/hashmap.h b/hashmap.h
new file mode 100644 (file)
index 0000000..4c946e3
--- /dev/null
+++ b/hashmap.h
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foohashmaphfoo
+#define foohashmaphfoo
+
+#include <stdbool.h>
+
+/* Pretty straightforward hash table implementation. As a minor
+ * optimization a NULL hashmap object will be treated as empty hashmap
+ * for all read operations. That way it is not necessary to
+ * instantiate an object for each Hashmap use. */
+
+typedef struct Hashmap Hashmap;
+
+typedef unsigned (*hash_func_t)(const void *p);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+unsigned string_hash_func(const void *p);
+int string_compare_func(const void *a, const void *b);
+
+unsigned trivial_hash_func(const void *p);
+int trivial_compare_func(const void *a, const void *b);
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
+void hashmap_free(Hashmap*);
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+void* hashmap_get(Hashmap *h, const void *key);
+void* hashmap_remove(Hashmap *h, const void *key);
+
+unsigned hashmap_size(Hashmap *h);
+bool hashmap_isempty(Hashmap *h);
+
+void *hashmap_iterate(Hashmap *h, void **state, const void **key);
+void *hashmap_iterate_backwards(Hashmap *h, void **state, const void **key);
+
+void *hashmap_steal_first(Hashmap *h);
+void* hashmap_first(Hashmap *h);
+void* hashmap_last(Hashmap *h);
+
+#define HASHMAP_FOREACH(e, h, state) \
+        for ((state) = NULL, (e) = hashmap_iterate((h), &(state), NULL); (e); (e) = hashmap_iterate((h), &(state), NULL))
+
+#define HASHMAP_FOREACH_BACKWARDS(e, h, state) \
+        for ((state) = NULL, (e) = hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = hashmap_iterate_backwards((h), &(state), NULL))
+
+#endif
diff --git a/job.c b/job.c
new file mode 100644 (file)
index 0000000..5cd8f73
--- /dev/null
+++ b/job.c
@@ -0,0 +1,59 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+
+#include "macro.h"
+#include "job.h"
+
+Job* job_new(Manager *m, JobType type, Name *name) {
+        Job *j;
+
+        assert(m);
+        assert(type < _JOB_TYPE_MAX);
+        assert(name);
+
+        if (!(j = new0(Job, 1)))
+                return NULL;
+
+        j->manager = m;
+        j->id = m->current_job_id++;
+        j->type = type;
+        j->name = name;
+
+        /* We don't link it here, that's what job_link() is for */
+
+        return j;
+}
+
+int job_link(Job *j) {
+        int r;
+
+        assert(j);
+        assert(!j->linked);
+
+        if ((r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j)) < 0)
+                return r;
+
+        j->name->meta.job = j;
+
+        j->linked = true;
+
+        return 0;
+}
+
+void job_free(Job *j) {
+        assert(j);
+
+        /* Detach from next 'bigger' objects */
+
+        if (j->linked) {
+                if (j->name && j->name->meta.job == j)
+                        j->name->meta.job = NULL;
+
+                hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
+        }
+
+        /* Free data and next 'smaller' objects */
+
+        free(j);
+}
diff --git a/job.h b/job.h
new file mode 100644 (file)
index 0000000..d19f747
--- /dev/null
+++ b/job.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foojobhfoo
+#define foojobhfoo
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+typedef struct Job Job;
+typedef enum JobType JobType;
+typedef enum JobMode JobMode;
+
+#include "manager.h"
+#include "name.h"
+#include "hashmap.h"
+#include "list.h"
+
+enum JobType {
+        JOB_START,
+        JOB_STOP,
+        JOB_VERIFY_STARTED,
+        JOB_RELOAD,
+        JOB_RESTART,
+        JOB_TRY_RESTART, /* restart if running */
+        JOB_RESTART_FINISH, /* 2nd part of a restart, i.e. the actual starting */
+        _JOB_TYPE_MAX
+};
+
+typedef enum JobState {
+        JOB_WAITING,
+        JOB_RUNNING,
+        JOB_DONE,
+        _JOB_STATE_MAX
+} JobState;
+
+enum JobMode {
+        JOB_FAIL,
+        JOB_REPLACE,
+        _JOB_MODE_MAX
+};
+
+struct Job {
+        Manager *manager;
+        uint32_t id;
+
+        JobType type;
+        JobState state;
+        Name *name;
+
+        bool linked:1;
+};
+
+Job* job_new(Manager *m, JobType type, Name *name);
+int job_link(Job *job);
+void job_free(Job *job);
+
+#endif
diff --git a/list.h b/list.h
new file mode 100644 (file)
index 0000000..c255e08
--- /dev/null
+++ b/list.h
@@ -0,0 +1,90 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foolisthfoo
+#define foolisthfoo
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define LIST_HEAD(t,name)                                               \
+    t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define LIST_FIELDS(t)                                                  \
+    t *next, *prev
+
+/* Initialize the list's head */
+#define LIST_HEAD_INIT(t,item)                                          \
+        do {                                                            \
+                (item) = (t*) NULL; }                                   \
+        while(false)
+
+/* Initialize a list item */
+#define LIST_INIT(t,item)                                               \
+        do {                                                            \
+                t *_item = (item);                                      \
+                assert(_item);                                          \
+                _item->prev = _item->next = NULL;                       \
+        } while(false)
+
+/* Prepend an item to the list */
+#define LIST_PREPEND(t,head,item)                                       \
+        do {                                                            \
+                t **_head = &(head), *_item = (item);                   \
+                assert(_item);                                          \
+                if ((_item->next = *_head))                             \
+                        _item->next->prev = _item;                      \
+                _item->prev = NULL;                                     \
+                *_head = _item;                                         \
+        } while(false)
+
+/* Remove an item from the list */
+#define LIST_REMOVE(t,head,item)                                        \
+        do {                                                            \
+                t **_head = &(head), *_item = (item);                   \
+                assert(_item);                                          \
+                if (_item->next)                                        \
+                        _item->next->prev = _item->prev;                \
+                if (_item->prev)                                        \
+                        _item->prev->next = _item->next;                \
+                else {                                                  \
+                        assert(*_head == _item);                        \
+                        *_head = _item->next;                           \
+                }                                                       \
+                _item->next = _item->prev = NULL;                       \
+        } while(false)
+
+/* Find the head of the list */
+#define LIST_FIND_HEAD(t,item,head)                                     \
+        do {                                                            \
+                t **_head = (head), *_item = (item);                    \
+                *_head = _item;                                         \
+                assert(_head);                                          \
+                while ((*_head)->prev)                                  \
+                        *_head = (*_head)->prev;                        \
+        } while (false)
+
+/* Insert an item after another one (a = where, b = what) */
+#define LIST_INSERT_AFTER(t,head,a,b)                                   \
+        do {                                                            \
+                t **_head = &(head), *_a = (a), *_b = (b);              \
+                assert(_b);                                             \
+                if (!_a) {                                              \
+                        if ((_b->next = *_head))                        \
+                                _b->next->prev = _b;                    \
+                        _b->prev = NULL;                                \
+                        *_head = _b;                                    \
+                } else {                                                \
+                        if ((_b->next = _a->next))                      \
+                                _b->next->prev = _b;                    \
+                        _b->prev = _a;                                  \
+                        _a->next = _b;                                  \
+                }                                                       \
+        } while(false)
+
+#define LIST_FOREACH(i,head)                                            \
+        for (i = (head); i; i = i->next)
+
+#define LIST_FOREACH_SAFE(i,n,head)                                     \
+        for (i = (head); i && ((n = i->next), 1); i = n)
+
+#endif
diff --git a/macro.h b/macro.h
new file mode 100644 (file)
index 0000000..267da83
--- /dev/null
+++ b/macro.h
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foomacrohfoo
+#define foomacrohfoo
+
+#include <assert.h>
+#include <sys/types.h>
+
+#define __printf_attr(a,b) __attribute__ ((format (printf, a, b)))
+#define __sentinel __attribute__ ((sentinel))
+#define __noreturn __attribute__((noreturn))
+#define __unused __attribute__ ((unused))
+#define __destructor __attribute__ ((destructor))
+#define __pure __attribute__ ((pure))
+#define __const __attribute__ ((const))
+#define __deprecated __attribute__ ((deprecated))
+#define __packed __attribute__ ((packed))
+#define __malloc __attribute__ ((malloc))
+
+/* Rounds up */
+static inline size_t ALIGN(size_t l) {
+        return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
+}
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+#define MAX(a,b)                                \
+        __extension__ ({                        \
+                        typeof(a) _a = (a);     \
+                        typeof(b) _b = (b);     \
+                        _a > _b ? _a : _b;      \
+                })
+
+#define MIN(a,b)                                \
+        __extension__ ({                        \
+                        typeof(a) _a = (a);     \
+                        typeof(b) _b = (b);     \
+                        _a < _b ? _a : _b;      \
+                })
+
+#define CLAMP(x, low, high)                                             \
+        __extension__ ({                                                \
+                        typeof(x) _x = (x);                             \
+                        typeof(low) _low = (low);                       \
+                        typeof(high) _high = (high);                    \
+                        ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
+                })
+
+
+
+#define assert_not_reached(t) assert(!(t))
+
+#define assert_se(x) assert(x)
+
+#define assert_cc(expr)                            \
+        do {                                       \
+                switch (0) {                       \
+                        case 0:                    \
+                        case !!(expr):             \
+                                ;                  \
+                }                                  \
+        } while (false)
+
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..08ccd4f
--- /dev/null
+++ b/main.c
@@ -0,0 +1,38 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "manager.h"
+
+int main(int argc, char *argv[]) {
+        Manager *m = NULL;
+        Name *milestone = NULL;
+        Job *job = NULL;
+        int r, retval = 1;
+
+        if (!(m = manager_new()) < 0) {
+                fprintf(stderr, "Failed to allocate manager object: %s\n", strerror(ENOMEM));
+                goto finish;
+        }
+
+
+        if ((r = manager_load_name(m, "default.milestone", &milestone) < 0)) {
+                fprintf(stderr, "Failed to load default milestone: %s\n", strerror(-r));
+                goto finish;
+        }
+
+        if ((r = manager_add_job(m, JOB_START, milestone, JOB_REPLACE, &job)) < 0) {
+                fprintf(stderr, "Failed to start default milestone: %s\n", strerror(-r));
+                goto finish;
+        }
+
+        retval = 0;
+
+finish:
+        if (m)
+                manager_free(m);
+
+        return retval;
+}
diff --git a/manager.c b/manager.c
new file mode 100644 (file)
index 0000000..d6bc35a
--- /dev/null
+++ b/manager.c
@@ -0,0 +1,309 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+#include <errno.h>
+
+#include "manager.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "strv.h"
+
+Manager* manager_new(void) {
+        Manager *m;
+
+        if (!(m = new0(Manager, 1)))
+                return NULL;
+
+        if (!(m->names = hashmap_new(string_hash_func, string_compare_func)))
+                goto fail;
+
+        if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
+                goto fail;
+
+        if (!(m->jobs_to_add = hashmap_new(trivial_hash_func, trivial_compare_func)))
+                goto fail;
+
+        if (!(m->jobs_to_remove = set_new(trivial_hash_func, trivial_compare_func)))
+                goto fail;
+
+        return m;
+
+fail:
+        manager_free(m);
+        return NULL;
+}
+
+void manager_free(Manager *m) {
+        Name *n;
+
+        assert(m);
+
+        while ((n = hashmap_first(m->names)))
+                name_free(n);
+
+        hashmap_free(m->names);
+        hashmap_free(m->jobs);
+
+        /* FIXME: This is incomplete */
+
+        hashmap_free(m->jobs_to_add);
+        set_free(m->jobs_to_remove);
+
+        free(m);
+}
+
+int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, Job **_ret) {
+        Job *ret, *other;
+        void *state;
+        Name *dep;
+        int r;
+
+        assert(m);
+        assert(type < _JOB_TYPE_MAX);
+        assert(name);
+        assert(mode < _JOB_MODE_MAX);
+        assert(_ret);
+
+        /* Check for conflicts, first against the jobs we shall
+         * create */
+        if ((other = hashmap_get(m->jobs_to_add, name))) {
+
+                if (other->type != type)
+                        return -EEXIST;
+
+        } else if (name->meta.job) {
+
+                if (name->meta.job->type != type) {
+
+                        if (mode == JOB_FAIL)
+                                return -EEXIST;
+
+                        if ((r = set_put(m->jobs_to_remove, name->meta.job)) < 0)
+                                return r;
+                }
+        }
+
+        if (!(ret = job_new(m, type, name)))
+                return -ENOMEM;
+
+        if ((r = hashmap_put(m->jobs_to_add, name, ret)) < 0)
+                goto fail;
+
+        if (type == JOB_START || type == JOB_VERIFY_STARTED || type == JOB_RESTART_FINISH) {
+                SET_FOREACH(dep, ret->name->meta.requires, state)
+                        if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
+                                goto fail;
+                SET_FOREACH(dep, ret->name->meta.soft_requires, state)
+                        if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
+                                goto fail;
+                SET_FOREACH(dep, ret->name->meta.wants, state)
+                        if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
+                                goto fail;
+                SET_FOREACH(dep, ret->name->meta.requisite, state)
+                        if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, mode, NULL)) < 0)
+                                goto fail;
+                SET_FOREACH(dep, ret->name->meta.soft_requisite, state)
+                        if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, JOB_FAIL, NULL)) < 0)
+                                goto fail;
+                SET_FOREACH(dep, ret->name->meta.conflicts, state)
+                        if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
+                                goto fail;
+
+        } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
+
+                SET_FOREACH(dep, ret->name->meta.required_by, state)
+                        if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
+                                goto fail;
+        }
+
+        if (_ret)
+                *_ret = ret;
+
+        return 0;
+
+fail:
+        job_free(ret);
+
+        return r;
+}
+
+
+Job *manager_get_job(Manager *m, uint32_t id) {
+        assert(m);
+
+        return hashmap_get(m->jobs, UINT32_TO_PTR(id));
+}
+
+Name *manager_get_name(Manager *m, const char *name) {
+        assert(m);
+        assert(name);
+
+        return hashmap_get(m->names, name);
+}
+
+static int detect_type(Name *name) {
+        char **n;
+
+        assert(name);
+
+        name->meta.type = _NAME_TYPE_INVALID;
+
+        STRV_FOREACH(n, name->meta.names) {
+                NameType t;
+
+                if ((t = name_type_from_string(*n)) == _NAME_TYPE_INVALID)
+                        return -EINVAL;
+
+                if (name->meta.type == _NAME_TYPE_INVALID) {
+                        name->meta.type = t;
+                        continue;
+                }
+
+                if (name->meta.type != t)
+                        return -EINVAL;
+        }
+
+        return 0;
+}
+
+static int fragment_load(Name *n) {
+        assert(n);
+
+        /*... */
+
+        return 0;
+}
+
+static int sysv_load(Service *s) {
+        assert(s);
+
+        /*... */
+
+        return 0;
+}
+
+static int fstab_load(Name *n) {
+        assert(n);
+        assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
+
+        /*... */
+
+        return 0;
+}
+
+static int snapshot_load(Snapshot *s) {
+        assert(s);
+
+        /*... */
+
+        return 0;
+}
+
+static int load(Name *name) {
+        int r;
+
+        assert(name);
+
+        if (name->meta.state != NAME_STUB)
+                return 0;
+
+        if ((r = detect_type(name)) < 0)
+                return r;
+
+        if (name->meta.type == NAME_SERVICE) {
+
+                /* Load a .service file */
+                if ((r = fragment_load(name)) == 0)
+                        goto finish;
+
+                /* Load a classic init script */
+                if (r == -ENOENT)
+                        if ((r = sysv_load(SERVICE(name))) == 0)
+                                goto finish;
+
+        } else if (name->meta.type == NAME_MOUNT ||
+                   name->meta.type == NAME_AUTOMOUNT) {
+
+                if ((r = fstab_load(name)) == 0)
+                        goto finish;
+
+        } else if (name->meta.type == NAME_SNAPSHOT) {
+
+                if ((r = snapshot_load(SNAPSHOT(name))) == 0)
+                        goto finish;
+
+        } else {
+                if ((r = fragment_load(name)) == 0)
+                        goto finish;
+        }
+
+        name->meta.state = NAME_FAILED;
+        return r;
+
+finish:
+        name->meta.state = NAME_LOADED;
+        return 0;
+}
+
+static int dispatch_load_queue(Manager *m) {
+        Meta *meta;
+
+        assert(m);
+
+        /* Dispatches the load queue. Takes a name from the queue and
+         * tries to load its data until the queue is empty */
+
+        while ((meta = m->load_queue)) {
+                load(NAME(meta));
+                LIST_REMOVE(Meta, m->load_queue, meta);
+        }
+
+        return 0;
+}
+
+
+
+int manager_load_name(Manager *m, const char *name, Name **_ret) {
+        Name *ret;
+        NameType t;
+        int r;
+
+        assert(m);
+        assert(name);
+        assert(_ret);
+/* This will load the service information files, but not actually
+ * start any services or anything */
+
+
+        if ((ret = manager_get_name(m, name)))
+                goto finish;
+
+        if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID)
+                return -EINVAL;
+
+        if (!(ret = name_new(m)))
+                return -ENOMEM;
+
+        ret->meta.type = t;
+
+        if (!(ret->meta.names = strv_new(name, NULL))) {
+                name_free(ret);
+                return -ENOMEM;
+        }
+
+        if ((r = name_link(ret)) < 0) {
+                name_free(ret);
+                return r;
+        }
+
+        /* At this point the new entry is created and linked. However,
+         * not loaded. Now load this entry and all its dependencies
+         * recursively */
+
+        dispatch_load_queue(m);
+
+finish:
+
+        *_ret = ret;
+        return 0;
+}
diff --git a/manager.h b/manager.h
new file mode 100644 (file)
index 0000000..53cacdf
--- /dev/null
+++ b/manager.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foomanagerhfoo
+#define foomanagerhfoo
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+typedef struct Manager Manager;
+
+#include "name.h"
+#include "job.h"
+#include "hashmap.h"
+#include "list.h"
+#include "set.h"
+
+struct Manager {
+        uint32_t current_job_id;
+
+        /* Active jobs and names */
+        Hashmap *names;  /* name string => Name object n:1 */
+        Hashmap *jobs;   /* job id => Job object 1:1 */
+
+        /* Names that need to be loaded */
+        LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */
+
+        /* Jobs to be added resp. removed. */
+        Hashmap *jobs_to_add;  /* Name object => Job object 1:1 */
+        Set *jobs_to_remove;
+};
+
+Manager* manager_new(void);
+void manager_free(Manager *m);
+
+Job *manager_get_job(Manager *m, uint32_t id);
+Name *manager_get_name(Manager *m, const char *name);
+
+int manager_load_name(Manager *m, const char *name, Name **_ret);
+int manager_add_job(Manager *m, JobType job, Name *name, JobMode mode, Job **_ret);
+
+#endif
diff --git a/name.c b/name.c
new file mode 100644 (file)
index 0000000..4b4b0b8
--- /dev/null
+++ b/name.c
@@ -0,0 +1,267 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+#include <errno.h>
+
+#include "set.h"
+#include "name.h"
+#include "macro.h"
+#include "strv.h"
+
+NameType name_type_from_string(const char *n) {
+        NameType t;
+        static const char* suffixes[_NAME_TYPE_MAX] = {
+                [NAME_SERVICE] = ".service",
+                [NAME_TIMER] = ".timer",
+                [NAME_SOCKET] = ".socket",
+                [NAME_MILESTONE] = ".milestone",
+                [NAME_DEVICE] = ".device",
+                [NAME_MOUNT] = ".mount",
+                [NAME_AUTOMOUNT] = ".automount",
+                [NAME_SNAPSHOT] = ".snapshot",
+        };
+
+        assert(n);
+
+        for (t = 0; t < _NAME_TYPE_MAX; t++)
+                if (endswith(n, suffixes[t]))
+                        return t;
+
+        return _NAME_TYPE_INVALID;
+}
+
+Name *name_new(Manager *m) {
+        Name *n;
+
+        assert(m);
+
+        if (!(n = new0(Name, 1)))
+                return NULL;
+
+        /* Not much initialization happening here at this time */
+        n->meta.manager = m;
+        n->meta.type = _NAME_TYPE_INVALID;
+        n->meta.state = NAME_STUB;
+
+        /* We don't link the name here, that is left for name_link() */
+
+        return n;
+}
+
+int name_link(Name *n) {
+        char **t;
+        int r;
+
+        assert(n);
+        assert(!n->meta.linked);
+
+        STRV_FOREACH(t, n->meta.names)
+                if ((r = hashmap_put(n->meta.manager->names, *t, n)) < 0)
+                        goto fail;
+
+        if (n->meta.state == NAME_STUB)
+                LIST_PREPEND(Meta, n->meta.manager->load_queue, &n->meta);
+
+        n->meta.linked = true;
+
+        return 0;
+
+fail:
+        t--;
+        STRV_FOREACH_BACKWARDS(t, n->meta.names)
+                hashmap_remove(n->meta.manager->names, *t);
+
+        return r;
+}
+
+void name_free(Name *name) {
+
+        assert(name);
+
+        /* Detach from next 'bigger' objects */
+
+        if (name->meta.linked) {
+                char **t;
+
+                STRV_FOREACH(t, name->meta.names)
+                        hashmap_remove(name->meta.manager->names, *t);
+
+                if (name->meta.job)
+                        job_free(name->meta.job);
+        }
+
+        /* Free data and next 'smaller' objects */
+
+        if (name->meta.job)
+                job_free(name->meta.job);
+
+        /* FIXME: Other names pointing to us should probably drop their refs to us when we get destructed */
+        set_free(name->meta.requires);
+        set_free(name->meta.soft_requires);
+        set_free(name->meta.wants);
+        set_free(name->meta.requisite);
+        set_free(name->meta.soft_requires);
+        set_free(name->meta.conflicts);
+        set_free(name->meta.before);
+        set_free(name->meta.after);
+
+        switch (name->meta.type) {
+
+                case NAME_SOCKET: {
+                        unsigned i;
+                        Socket *s = SOCKET(name);
+
+                        for (i = 0; i < s->n_fds; i++)
+                                nointr_close(s->fds[i]);
+                        break;
+                }
+
+                case NAME_DEVICE: {
+                        Device *d = DEVICE(name);
+
+                        free(d->sysfs);
+                        break;
+                }
+
+                case NAME_MOUNT: {
+                        Mount *m = MOUNT(name);
+
+                        free(m->path);
+                        break;
+                }
+
+                case NAME_AUTOMOUNT: {
+                        Automount *a = AUTOMOUNT(name);
+
+                        free(a->path);
+                        break;
+                }
+
+                default:
+                        ;
+        }
+
+        free(name->meta.description);
+        strv_free(name->meta.names);
+
+        free(name);
+}
+
+bool name_is_ready(Name *name) {
+
+        assert(name);
+
+        if (name->meta.state != NAME_LOADED)
+                return false;
+
+        assert(name->meta.type < _NAME_TYPE_MAX);
+
+        switch (name->meta.type) {
+                case NAME_SERVICE: {
+                        Service *s = SERVICE(name);
+
+                        return
+                                s->state == SERVICE_RUNNING ||
+                                s->state == SERVICE_RELOAD_PRE ||
+                                s->state == SERVICE_RELOAD ||
+                                s->state == SERVICE_RELOAD_POST;
+                }
+
+                case NAME_TIMER: {
+                        Timer *t = TIMER(name);
+
+                        return
+                                t->state == TIMER_WAITING ||
+                                t->state == TIMER_RUNNING;
+                }
+
+                case NAME_SOCKET: {
+                        Socket *s = SOCKET(name);
+
+                        return
+                                s->state == SOCKET_LISTENING ||
+                                s->state == SOCKET_RUNNING;
+                }
+
+                case NAME_MILESTONE:
+                        return MILESTONE(name)->state == MILESTONE_ACTIVE;
+
+                case NAME_DEVICE:
+                        return DEVICE(name)->state == DEVICE_AVAILABLE;
+
+                case NAME_MOUNT:
+                        return MOUNT(name)->state == MOUNT_MOUNTED;
+
+                case NAME_AUTOMOUNT: {
+                        Automount *a = AUTOMOUNT(name);
+
+                        return
+                                a->state == AUTOMOUNT_WAITING ||
+                                a->state == AUTOMOUNT_RUNNING;
+                }
+
+                case NAME_SNAPSHOT:
+                        return SNAPSHOT(name)->state == SNAPSHOT_ACTIVE;
+
+
+                case _NAME_TYPE_MAX:
+                case _NAME_TYPE_INVALID:
+                        ;
+        }
+
+        assert_not_reached("Unknown name type.");
+        return false;
+}
+
+static int ensure_in_set(Set **s, void *data) {
+        int r;
+
+        assert(s);
+        assert(data);
+
+        if (!*s)
+                if (!(*s = set_new(trivial_hash_func, trivial_compare_func)))
+                        return -ENOMEM;
+
+        if ((r = set_put(*s, data) < 0))
+                if (r != -EEXIST)
+                        return r;
+
+        return 0;
+}
+
+int name_augment(Name *n) {
+        int r;
+        void* state;
+        Name *other;
+
+        assert(n);
+
+        /* Adds in the missing links to make all dependencies both-ways */
+
+        SET_FOREACH(other, n->meta.before, state)
+                if ((r = ensure_in_set(&other->meta.after, n) < 0))
+                        return r;
+        SET_FOREACH(other, n->meta.after, state)
+                if ((r = ensure_in_set(&other->meta.before, n) < 0))
+                        return r;
+
+        SET_FOREACH(other, n->meta.conflicts, state)
+                if ((r = ensure_in_set(&other->meta.conflicts, n) < 0))
+                        return r;
+
+        SET_FOREACH(other, n->meta.requires, state)
+                if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+                        return r;
+        SET_FOREACH(other, n->meta.soft_requires, state)
+                if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+                        return r;
+        SET_FOREACH(other, n->meta.requisite, state)
+                if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+                        return r;
+        SET_FOREACH(other, n->meta.soft_requisite, state)
+                if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+                        return r;
+
+        return r;
+}
diff --git a/name.h b/name.h
new file mode 100644 (file)
index 0000000..3b364f0
--- /dev/null
+++ b/name.h
@@ -0,0 +1,270 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foonamehfoo
+#define foonamehfoo
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef union Name Name;
+typedef struct Meta Meta;
+typedef struct Service Service;
+typedef struct Timer Timer;
+typedef struct Socket Socket;
+typedef struct Milestone Milestone;
+typedef struct Device Device;
+typedef struct Mount Mount;
+typedef struct Automount Automount;
+typedef struct Snapshot Snapshot;
+
+#include "job.h"
+#include "manager.h"
+#include "set.h"
+#include "util.h"
+#include "list.h"
+
+typedef enum NameType {
+        NAME_SERVICE = 0,
+        NAME_TIMER,
+        NAME_SOCKET,
+        NAME_MILESTONE,
+        NAME_DEVICE,
+        NAME_MOUNT,
+        NAME_AUTOMOUNT,
+        NAME_SNAPSHOT,
+        _NAME_TYPE_MAX,
+        _NAME_TYPE_INVALID = -1,
+} NameType;
+
+typedef enum NameState {
+        NAME_STUB,
+        NAME_LOADED,
+        NAME_FAILED
+} NameState;
+
+struct Meta {
+        Manager *manager;
+        NameType type;
+        NameState state;
+
+        char **names;
+
+        /* Positive dependencies */
+        Set *requires, *soft_requires, *wants, *requisite, *soft_requisite;
+        Set *required_by;    /* inverse of 'requires', 'soft_requires', 'requisite' and 'soft_requisite' is 'required_by' */
+
+        /* Negative dependencies */
+        Set *conflicts;      /* inverse of 'conflicts' is 'conflicts' */
+
+        /* Order */
+        Set *before, *after; /* inverse of before is after and vice versa */
+
+        /* Information */
+        char *description;
+
+        /* If there is something to do with this name, then this is
+         * the job for it */
+        Job *job;
+
+        bool linked:1;
+
+        /* Load queue */
+        LIST_FIELDS(Meta);
+};
+
+typedef enum ServiceState {
+        SERVICE_DEAD,
+        SERVICE_BEFORE,
+        SERVICE_START_PRE,
+        SERVICE_START,
+        SERVICE_START_POST,
+        SERVICE_RUNNING,
+        SERVICE_RELOAD_PRE,
+        SERVICE_RELOAD,
+        SERVICE_RELOAD_POST,
+        SERVICE_STOP_PRE,
+        SERVICE_STOP,
+        SERVICE_SIGTERM,
+        SERVICE_SIGKILL,
+        SERVICE_STOP_POST,
+        SERVICE_HOLDOFF,
+        SERVICE_MAINTAINANCE
+} ServiceState;
+
+typedef enum ServiceMode {
+        SERVICE_ONCE,
+        SERVICE_RESTART
+} ServiceMode;
+
+struct Service {
+        Meta meta;
+
+        ServiceState state;
+        ServiceMode mode;
+};
+
+typedef enum TimerState {
+        TIMER_DEAD,
+        TIMER_BEFORE,
+        TIMER_START_PRE,
+        TIMER_START,
+        TIMER_START_POST,
+        TIMER_WAITING,
+        TIMER_RUNNING,
+        TIMER_STOP_PRE,
+        TIMER_STOP,
+        TIMER_STOP_POST,
+        TIMER_MAINTAINANCE
+} TimerState;
+
+struct Timer {
+        Meta meta;
+
+        TimerState state;
+        Service *subject;
+
+        clockid_t clock_id;
+        usec_t next_elapse;
+};
+
+typedef enum SocketState {
+        SOCKET_DEAD,
+        SOCKET_BEFORE,
+        SOCKET_START_PRE,
+        SOCKET_START,
+        SOCKET_START_POST,
+        SOCKET_LISTENING,
+        SOCKET_RUNNING,
+        SOCKET_STOP_PRE,
+        SOCKET_STOP,
+        SOCKET_STOP_POST,
+        SOCKET_MAINTAINANCE
+} SocketState;
+
+struct Socket {
+        Meta meta;
+
+        SocketState state;
+        int *fds;
+        unsigned n_fds;
+
+        Service *subject;
+};
+
+typedef enum MilestoneState {
+        MILESTONE_DEAD,
+        MILESTONE_BEFORE,
+        MILESTONE_ACTIVE
+} MilestoneState;
+
+struct Milestone {
+        Meta meta;
+
+        MilestoneState state;
+};
+
+typedef enum DeviceState {
+        DEVICE_DEAD,
+        DEVICE_BEFORE,
+        DEVICE_AVAILABLE
+} DeviceState;
+
+struct Device {
+        Meta meta;
+
+        DeviceState state;
+        char *sysfs;
+};
+
+typedef enum MountState {
+        MOUNT_DEAD,
+        MOUNT_BEFORE,
+        MOUNT_MOUNTED
+} MountState;
+
+struct Mount {
+        Meta meta;
+
+        MountState state;
+        char *path;
+};
+
+typedef enum AutomountState {
+        AUTOMOUNT_DEAD,
+        AUTOMOUNT_BEFORE,
+        AUTOMOUNT_START_PRE,
+        AUTOMOUNT_START,
+        AUTOMOUNT_START_POST,
+        AUTOMOUNT_WAITING,
+        AUTOMOUNT_RUNNING,
+        AUTOMOUNT_STOP_PRE,
+        AUTOMOUNT_STOP,
+        AUTOMOUNT_STOP_POST,
+        AUTOMOUNT_MAINTAINANCE
+} AutomountState;
+
+struct Automount {
+        Meta meta;
+
+        AutomountState state;
+        char *path;
+        Mount *subject;
+};
+
+typedef enum SnapshotState {
+        SNAPSHOT_DEAD,
+        SNAPSHOT_BEFORE,
+        SNAPSHOT_ACTIVE
+} SnapshotState;
+
+struct Snapshot {
+        Meta meta;
+
+        SnapshotState state;
+        bool cleanup:1;
+};
+
+union Name {
+        Meta meta;
+        Service service;
+        Timer timer;
+        Socket socket;
+        Milestone milestone;
+        Device device;
+        Mount mount;
+        Automount automount;
+        Snapshot snapshot;
+};
+
+/* For casting a name into the various name types */
+
+#define DEFINE_CAST(UPPERCASE, MixedCase, lowercase)                    \
+        static inline MixedCase* UPPERCASE(Name *name) {                \
+                if (name->meta.type != NAME_##UPPERCASE)                \
+                        return NULL;                                    \
+                                                                        \
+                return &name->lowercase;                                \
+        }
+
+DEFINE_CAST(SERVICE, Service, service);
+DEFINE_CAST(TIMER, Timer, timer);
+DEFINE_CAST(SOCKET, Socket, socket);
+DEFINE_CAST(MILESTONE, Milestone, milestone);
+DEFINE_CAST(DEVICE, Device, device);
+DEFINE_CAST(MOUNT, Mount, mount);
+DEFINE_CAST(AUTOMOUNT, Automount, automount);
+DEFINE_CAST(SNAPSHOT, Snapshot, snapshot);
+
+/* For casting the various name types into a name */
+#define NAME(o) ((Name*) (o))
+
+bool name_is_ready(Name *name);
+NameType name_type_from_string(const char *n);
+
+Name *name_new(Manager *m);
+void name_free(Name *name);
+int name_link(Name *name);
+
+int name_augment(Name *n);
+
+#endif
diff --git a/set.c b/set.c
new file mode 100644 (file)
index 0000000..3aa227b
--- /dev/null
+++ b/set.c
@@ -0,0 +1,63 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "set.h"
+#include "hashmap.h"
+
+#define MAKE_SET(h) ((Set*) (h))
+#define MAKE_HASHMAP(s) ((Hashmap*) (s))
+
+/* For now this is not much more than a wrapper around a hashmap */
+
+Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
+        return MAKE_SET(hashmap_new(hash_func, compare_func));
+}
+
+void set_free(Set* s) {
+        hashmap_free(MAKE_HASHMAP(s));
+}
+
+int set_put(Set *s, void *value) {
+        return hashmap_put(MAKE_HASHMAP(s), value, value);
+}
+
+void *set_get(Set *s, void *value) {
+        return hashmap_get(MAKE_HASHMAP(s), value);
+}
+
+void *set_remove(Set *s, void *value) {
+        return hashmap_remove(MAKE_HASHMAP(s), value);
+}
+
+unsigned set_size(Set *s) {
+        return hashmap_size(MAKE_HASHMAP(s));
+}
+
+bool set_isempty(Set *s) {
+        return hashmap_isempty(MAKE_HASHMAP(s));
+}
+
+void *set_iterate(Set *s, void **state) {
+        return hashmap_iterate(MAKE_HASHMAP(s), state, NULL);
+}
+
+void *set_iterate_backwards(Set *s, void **state) {
+        return hashmap_iterate_backwards(MAKE_HASHMAP(s), state, NULL);
+}
+
+void *set_steal_first(Set *s) {
+        return hashmap_steal_first(MAKE_HASHMAP(s));
+}
+
+void* set_first(Set *s) {
+        return hashmap_first(MAKE_HASHMAP(s));
+}
+
+void* set_last(Set *s) {
+        return hashmap_last(MAKE_HASHMAP(s));
+}
diff --git a/set.h b/set.h
new file mode 100644 (file)
index 0000000..9aefdbc
--- /dev/null
+++ b/set.h
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foosethfoo
+#define foosethfoo
+
+/* Pretty straightforward set implementation. Internally based on the
+ * hashmap. That means that as a minor optimization a NULL set
+ * object will be treated as empty set for all read
+ * operations. That way it is not necessary to instantiate an object
+ * for each set use. */
+
+#include "hashmap.h"
+
+typedef struct Set Set;
+
+Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
+void set_free(Set* set);
+
+int set_put(Set *s, void *value);
+void *set_get(Set *s, void *value);
+void *set_remove(Set *s, void *value);
+
+unsigned set_size(Set *s);
+bool set_isempty(Set *s);
+
+void *set_iterate(Set *h, void **state);
+void *set_iterate_backwards(Set *h, void **state);
+
+void *set_steal_first(Set *h);
+void* set_first(Set *h);
+void* set_last(Set *h);
+
+#define SET_FOREACH(e, s, state) \
+        for ((state) = NULL, (e) = set_iterate((s), &(state)); (e); (e) = set_iterate((s), &(state)))
+
+#define SET_FOREACH_BACKWARDS(e, s, state) \
+        for ((state) = NULL, (e) = set_iterate_backwards((s), &(state)); (e); (e) = set_iterate_backwards((s), &(state)))
+
+#endif
diff --git a/strv.c b/strv.c
new file mode 100644 (file)
index 0000000..ecad6d5
--- /dev/null
+++ b/strv.c
@@ -0,0 +1,117 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "util.h"
+#include "strv.h"
+
+char *strv_find(char **l, const char *name) {
+        assert(l);
+        assert(name);
+
+        for (; *l; l++)
+                if (streq(*l, name))
+                        return *l;
+
+        return NULL;
+}
+
+void strv_free(char **l) {
+        char **k;
+
+        if (!l)
+                return;
+
+        for (k = l; *k; k++)
+                free(*k);
+
+        free(l);
+}
+
+char **strv_copy(char **l) {
+        char **r, **k;
+
+        if (!(r = new(char*, strv_length(l)+1)))
+                return NULL;
+
+        for (k = r; *l; k++, l++)
+                if (!(*k = strdup(*l)))
+                        goto fail;
+
+        *k = NULL;
+        return r;
+
+fail:
+        for (k--, l--; k >= r; k--, l--)
+                free(*k);
+
+        return NULL;
+}
+
+unsigned strv_length(char **l) {
+        unsigned n = 0;
+
+        if (!l)
+                return 0;
+
+        for (; *l; l++)
+                n++;
+
+        return n;
+}
+
+char **strv_new(const char *x, ...) {
+        const char *s;
+        char **a;
+        unsigned n = 0, i = 0;
+        va_list ap;
+
+        if (x) {
+                n = 1;
+
+                va_start(ap, x);
+
+                while (va_arg(ap, const char*))
+                        n++;
+
+                va_end(ap);
+        }
+
+        if (!(a = new(char*, n+1)))
+                return NULL;
+
+        if (x) {
+                if (!(a[i] = strdup(x))) {
+                        free(a);
+                        return NULL;
+                }
+
+                i++;
+
+                va_start(ap, x);
+
+                while ((s = va_arg(ap, const char*))) {
+                        if (!(a[i] = strdup(s)))
+                                goto fail;
+
+                        i++;
+                }
+
+                va_end(ap);
+        }
+
+        a[i] = NULL;
+        return a;
+
+fail:
+
+        for (; i > 0; i--)
+                if (a[i-1])
+                        free(a[i-1]);
+
+        free(a);
+        return NULL;
+}
diff --git a/strv.h b/strv.h
new file mode 100644 (file)
index 0000000..43a5c59
--- /dev/null
+++ b/strv.h
@@ -0,0 +1,21 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foostrvhfoo
+#define foostrvhfoo
+
+#include "macro.h"
+
+char *strv_find(char **l, const char *name);
+void strv_free(char **l);
+char **strv_copy(char **l);
+unsigned strv_length(char **l);
+
+char **strv_new(const char *x, ...) __sentinel;
+
+#define STRV_FOREACH(s, l)                      \
+        for ((s) = (l); (l) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l)            \
+        for (; (l) && ((s) >= (l)); (s)--)
+
+#endif
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..3ef190f
--- /dev/null
+++ b/util.c
@@ -0,0 +1,95 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "macro.h"
+#include "util.h"
+
+usec_t now(clockid_t clock) {
+        struct timespec ts;
+
+        assert_se(clock_gettime(clock, &ts) == 0);
+
+        return timespec_load(&ts);
+}
+
+usec_t timespec_load(const struct timespec *ts) {
+        assert(ts);
+
+        return
+                (usec_t) ts->tv_sec * USEC_PER_SEC +
+                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
+        assert(ts);
+
+        ts->tv_sec = (time_t) (u / USEC_PER_SEC);
+        ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+
+        return ts;
+}
+
+usec_t timeval_load(const struct timeval *tv) {
+        assert(tv);
+
+        return
+                (usec_t) tv->tv_sec * USEC_PER_SEC +
+                (usec_t) tv->tv_usec;
+}
+
+struct timeval *timeval_store(struct timeval *tv, usec_t u) {
+        assert(tv);
+
+        tv->tv_sec = (time_t) (u / USEC_PER_SEC);
+        tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
+
+        return tv;
+}
+
+bool endswith(const char *s, const char *postfix) {
+        size_t sl, pl;
+
+        assert(s);
+        assert(postfix);
+
+        sl = strlen(s);
+        pl = strlen(postfix);
+
+        if (sl < pl)
+                return false;
+
+        return memcmp(s + sl - pl, postfix, pl) == 0;
+}
+
+bool startswith(const char *s, const char *prefix) {
+        size_t sl, pl;
+
+        assert(s);
+        assert(prefix);
+
+        sl = strlen(s);
+        pl = strlen(prefix);
+
+        if (sl < pl)
+                return false;
+
+        return memcmp(s, prefix, pl) == 0;
+}
+
+int nointr_close(int fd) {
+        assert(fd >= 0);
+
+        for (;;) {
+                int r;
+
+                if ((r = close(fd)) >= 0)
+                        return r;
+
+                if (errno != EINTR)
+                        return r;
+        }
+}
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..5d9ef39
--- /dev/null
+++ b/util.h
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdbool.h>
+
+typedef uint64_t usec_t;
+
+#define USEC_PER_SEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+usec_t now(clockid_t clock);
+
+usec_t timespec_load(const struct timespec *ts);
+struct timespec *timespec_store(struct timespec *ts, usec_t u);
+
+usec_t timeval_load(const struct timeval *tv);
+struct timeval *timeval_store(struct timeval *tv, usec_t u);
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+#define new(t, n) ((t*) malloc(sizeof(t)*(n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define malloc0(n) (calloc((n), 1))
+
+static inline const char* yes_no(bool b) {
+        return b ? "yes" : "no";
+}
+
+static inline const char* strempty(const char *s) {
+        return s ? s : "";
+}
+
+static inline const char* strnull(const char *s) {
+        return s ? s : "(null)";
+}
+
+bool endswith(const char *s, const char *postfix);
+bool startswith(const char *s, const char *prefix);
+
+int nointr_close(int fd);
+
+#endif