From 6091827530d6dd43479d6709fb6e9f745c11e900 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 18 Nov 2009 00:42:52 +0100 Subject: [PATCH] initial commit --- .gitignore | 2 + CODING_STYLE | 16 +++ Makefile | 8 ++ hashmap.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++ hashmap.h | 47 ++++++++ job.c | 59 ++++++++++ job.h | 57 +++++++++ list.h | 90 ++++++++++++++ macro.h | 76 ++++++++++++ main.c | 38 ++++++ manager.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++ manager.h | 41 +++++++ name.c | 267 ++++++++++++++++++++++++++++++++++++++++++ name.h | 270 ++++++++++++++++++++++++++++++++++++++++++ set.c | 63 ++++++++++ set.h | 39 +++++++ strv.c | 117 +++++++++++++++++++ strv.h | 21 ++++ util.c | 95 +++++++++++++++ util.h | 49 ++++++++ 20 files changed, 1989 insertions(+) create mode 100644 .gitignore create mode 100644 CODING_STYLE create mode 100644 Makefile create mode 100644 hashmap.c create mode 100644 hashmap.h create mode 100644 job.c create mode 100644 job.h create mode 100644 list.h create mode 100644 macro.h create mode 100644 main.c create mode 100644 manager.c create mode 100644 manager.h create mode 100644 name.c create mode 100644 name.h create mode 100644 set.c create mode 100644 set.h create mode 100644 strv.c create mode 100644 strv.h create mode 100644 util.c create mode 100644 util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6169dd06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +systemd +*.o diff --git a/CODING_STYLE b/CODING_STYLE new file mode 100644 index 00000000..1b8c8cf3 --- /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 00000000..ee144a37 --- /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 00000000..1b2e059d --- /dev/null +++ b/hashmap.c @@ -0,0 +1,325 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#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 00000000..4c946e35 --- /dev/null +++ b/hashmap.h @@ -0,0 +1,47 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foohashmaphfoo +#define foohashmaphfoo + +#include + +/* 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 00000000..5cd8f73a --- /dev/null +++ b/job.c @@ -0,0 +1,59 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include + +#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 00000000..d19f747d --- /dev/null +++ b/job.h @@ -0,0 +1,57 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foojobhfoo +#define foojobhfoo + +#include +#include + +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 00000000..c255e087 --- /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 00000000..267da83a --- /dev/null +++ b/macro.h @@ -0,0 +1,76 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foomacrohfoo +#define foomacrohfoo + +#include +#include + +#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 00000000..08ccd4f8 --- /dev/null +++ b/main.c @@ -0,0 +1,38 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include + +#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 00000000..d6bc35a2 --- /dev/null +++ b/manager.c @@ -0,0 +1,309 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include + +#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 00000000..53cacdfb --- /dev/null +++ b/manager.h @@ -0,0 +1,41 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foomanagerhfoo +#define foomanagerhfoo + +#include +#include + +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 00000000..4b4b0b87 --- /dev/null +++ b/name.c @@ -0,0 +1,267 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include + +#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 00000000..3b364f00 --- /dev/null +++ b/name.h @@ -0,0 +1,270 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foonamehfoo +#define foonamehfoo + +#include +#include + +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 00000000..3aa227bb --- /dev/null +++ b/set.c @@ -0,0 +1,63 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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 00000000..9aefdbcb --- /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 00000000..ecad6d59 --- /dev/null +++ b/strv.c @@ -0,0 +1,117 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include +#include + +#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 00000000..43a5c595 --- /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 00000000..3ef190fa --- /dev/null +++ b/util.c @@ -0,0 +1,95 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include +#include + +#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 00000000..5d9ef39e --- /dev/null +++ b/util.h @@ -0,0 +1,49 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef fooutilhfoo +#define fooutilhfoo + +#include +#include +#include +#include + +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 -- 2.39.5