summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CODING_STYLE16
-rw-r--r--Makefile8
-rw-r--r--hashmap.c325
-rw-r--r--hashmap.h47
-rw-r--r--job.c59
-rw-r--r--job.h57
-rw-r--r--list.h90
-rw-r--r--macro.h76
-rw-r--r--main.c38
-rw-r--r--manager.c309
-rw-r--r--manager.h41
-rw-r--r--name.c267
-rw-r--r--name.h270
-rw-r--r--set.c63
-rw-r--r--set.h39
-rw-r--r--strv.c117
-rw-r--r--strv.h21
-rw-r--r--util.c95
-rw-r--r--util.h49
20 files changed, 1989 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..6169dd062e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+systemd
+*.o
diff --git a/CODING_STYLE b/CODING_STYLE
new file mode 100644
index 0000000000..1b8c8cf3c6
--- /dev/null
+++ b/CODING_STYLE
@@ -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
index 0000000000..ee144a3783
--- /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
index 0000000000..1b2e059dde
--- /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
index 0000000000..4c946e35b9
--- /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
index 0000000000..5cd8f73a91
--- /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
index 0000000000..d19f747dbd
--- /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
index 0000000000..c255e087e8
--- /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
index 0000000000..267da83a0f
--- /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
index 0000000000..08ccd4f8dc
--- /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
index 0000000000..d6bc35a289
--- /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
index 0000000000..53cacdfb2e
--- /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
index 0000000000..4b4b0b87d1
--- /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
index 0000000000..3b364f00f3
--- /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
index 0000000000..3aa227bbc7
--- /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
index 0000000000..9aefdbcb56
--- /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
index 0000000000..ecad6d5980
--- /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
index 0000000000..43a5c5954b
--- /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
index 0000000000..3ef190fa2a
--- /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
index 0000000000..5d9ef39e04
--- /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