initial commit

This commit is contained in:
Lennart Poettering 2009-11-18 00:42:52 +01:00
commit 6091827530
20 changed files with 1989 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
systemd
*.o

16
CODING_STYLE Normal file
View File

@ -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".

8
Makefile Normal file
View File

@ -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

325
hashmap.c Normal file
View File

@ -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;
}

47
hashmap.h Normal file
View File

@ -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

59
job.c Normal file
View File

@ -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);
}

57
job.h Normal file
View File

@ -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

90
list.h Normal file
View File

@ -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

76
macro.h Normal file
View File

@ -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

38
main.c Normal file
View File

@ -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;
}

309
manager.c Normal file
View File

@ -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;
}

41
manager.h Normal file
View File

@ -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

267
name.c Normal file
View File

@ -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;
}

270
name.h Normal file
View File

@ -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

63
set.c Normal file
View File

@ -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));
}

39
set.h Normal file
View File

@ -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

117
strv.c Normal file
View File

@ -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;
}

21
strv.h Normal file
View File

@ -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

95
util.c Normal file
View File

@ -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;
}
}

49
util.h Normal file
View File

@ -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