mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-18 06:03:42 +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…
x
Reference in New Issue
Block a user