mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
initial commit
This commit is contained in:
commit
6091827530
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
systemd
|
||||
*.o
|
16
CODING_STYLE
Normal file
16
CODING_STYLE
Normal 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
8
Makefile
Normal 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
325
hashmap.c
Normal 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
47
hashmap.h
Normal 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
59
job.c
Normal 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
57
job.h
Normal 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
90
list.h
Normal 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
76
macro.h
Normal 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
38
main.c
Normal 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
309
manager.c
Normal 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
41
manager.h
Normal 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
267
name.c
Normal 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
270
name.h
Normal 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
63
set.c
Normal 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
39
set.h
Normal 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
117
strv.c
Normal 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
21
strv.h
Normal 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
95
util.c
Normal 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
49
util.h
Normal 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
|
Loading…
Reference in New Issue
Block a user