summaryrefslogtreecommitdiff
path: root/manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'manager.c')
-rw-r--r--manager.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/manager.c b/manager.c
new file mode 100644
index 0000000000..d6bc35a289
--- /dev/null
+++ b/manager.c
@@ -0,0 +1,309 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <assert.h>
+#include <errno.h>
+
+#include "manager.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "strv.h"
+
+Manager* manager_new(void) {
+ Manager *m;
+
+ if (!(m = new0(Manager, 1)))
+ return NULL;
+
+ if (!(m->names = hashmap_new(string_hash_func, string_compare_func)))
+ goto fail;
+
+ if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
+ goto fail;
+
+ if (!(m->jobs_to_add = hashmap_new(trivial_hash_func, trivial_compare_func)))
+ goto fail;
+
+ if (!(m->jobs_to_remove = set_new(trivial_hash_func, trivial_compare_func)))
+ goto fail;
+
+ return m;
+
+fail:
+ manager_free(m);
+ return NULL;
+}
+
+void manager_free(Manager *m) {
+ Name *n;
+
+ assert(m);
+
+ while ((n = hashmap_first(m->names)))
+ name_free(n);
+
+ hashmap_free(m->names);
+ hashmap_free(m->jobs);
+
+ /* FIXME: This is incomplete */
+
+ hashmap_free(m->jobs_to_add);
+ set_free(m->jobs_to_remove);
+
+ free(m);
+}
+
+int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, Job **_ret) {
+ Job *ret, *other;
+ void *state;
+ Name *dep;
+ int r;
+
+ assert(m);
+ assert(type < _JOB_TYPE_MAX);
+ assert(name);
+ assert(mode < _JOB_MODE_MAX);
+ assert(_ret);
+
+ /* Check for conflicts, first against the jobs we shall
+ * create */
+ if ((other = hashmap_get(m->jobs_to_add, name))) {
+
+ if (other->type != type)
+ return -EEXIST;
+
+ } else if (name->meta.job) {
+
+ if (name->meta.job->type != type) {
+
+ if (mode == JOB_FAIL)
+ return -EEXIST;
+
+ if ((r = set_put(m->jobs_to_remove, name->meta.job)) < 0)
+ return r;
+ }
+ }
+
+ if (!(ret = job_new(m, type, name)))
+ return -ENOMEM;
+
+ if ((r = hashmap_put(m->jobs_to_add, name, ret)) < 0)
+ goto fail;
+
+ if (type == JOB_START || type == JOB_VERIFY_STARTED || type == JOB_RESTART_FINISH) {
+ SET_FOREACH(dep, ret->name->meta.requires, state)
+ if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
+ goto fail;
+ SET_FOREACH(dep, ret->name->meta.soft_requires, state)
+ if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
+ goto fail;
+ SET_FOREACH(dep, ret->name->meta.wants, state)
+ if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
+ goto fail;
+ SET_FOREACH(dep, ret->name->meta.requisite, state)
+ if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, mode, NULL)) < 0)
+ goto fail;
+ SET_FOREACH(dep, ret->name->meta.soft_requisite, state)
+ if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, JOB_FAIL, NULL)) < 0)
+ goto fail;
+ SET_FOREACH(dep, ret->name->meta.conflicts, state)
+ if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
+ goto fail;
+
+ } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
+
+ SET_FOREACH(dep, ret->name->meta.required_by, state)
+ if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
+ goto fail;
+ }
+
+ if (_ret)
+ *_ret = ret;
+
+ return 0;
+
+fail:
+ job_free(ret);
+
+ return r;
+}
+
+
+Job *manager_get_job(Manager *m, uint32_t id) {
+ assert(m);
+
+ return hashmap_get(m->jobs, UINT32_TO_PTR(id));
+}
+
+Name *manager_get_name(Manager *m, const char *name) {
+ assert(m);
+ assert(name);
+
+ return hashmap_get(m->names, name);
+}
+
+static int detect_type(Name *name) {
+ char **n;
+
+ assert(name);
+
+ name->meta.type = _NAME_TYPE_INVALID;
+
+ STRV_FOREACH(n, name->meta.names) {
+ NameType t;
+
+ if ((t = name_type_from_string(*n)) == _NAME_TYPE_INVALID)
+ return -EINVAL;
+
+ if (name->meta.type == _NAME_TYPE_INVALID) {
+ name->meta.type = t;
+ continue;
+ }
+
+ if (name->meta.type != t)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fragment_load(Name *n) {
+ assert(n);
+
+ /*... */
+
+ return 0;
+}
+
+static int sysv_load(Service *s) {
+ assert(s);
+
+ /*... */
+
+ return 0;
+}
+
+static int fstab_load(Name *n) {
+ assert(n);
+ assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
+
+ /*... */
+
+ return 0;
+}
+
+static int snapshot_load(Snapshot *s) {
+ assert(s);
+
+ /*... */
+
+ return 0;
+}
+
+static int load(Name *name) {
+ int r;
+
+ assert(name);
+
+ if (name->meta.state != NAME_STUB)
+ return 0;
+
+ if ((r = detect_type(name)) < 0)
+ return r;
+
+ if (name->meta.type == NAME_SERVICE) {
+
+ /* Load a .service file */
+ if ((r = fragment_load(name)) == 0)
+ goto finish;
+
+ /* Load a classic init script */
+ if (r == -ENOENT)
+ if ((r = sysv_load(SERVICE(name))) == 0)
+ goto finish;
+
+ } else if (name->meta.type == NAME_MOUNT ||
+ name->meta.type == NAME_AUTOMOUNT) {
+
+ if ((r = fstab_load(name)) == 0)
+ goto finish;
+
+ } else if (name->meta.type == NAME_SNAPSHOT) {
+
+ if ((r = snapshot_load(SNAPSHOT(name))) == 0)
+ goto finish;
+
+ } else {
+ if ((r = fragment_load(name)) == 0)
+ goto finish;
+ }
+
+ name->meta.state = NAME_FAILED;
+ return r;
+
+finish:
+ name->meta.state = NAME_LOADED;
+ return 0;
+}
+
+static int dispatch_load_queue(Manager *m) {
+ Meta *meta;
+
+ assert(m);
+
+ /* Dispatches the load queue. Takes a name from the queue and
+ * tries to load its data until the queue is empty */
+
+ while ((meta = m->load_queue)) {
+ load(NAME(meta));
+ LIST_REMOVE(Meta, m->load_queue, meta);
+ }
+
+ return 0;
+}
+
+
+
+int manager_load_name(Manager *m, const char *name, Name **_ret) {
+ Name *ret;
+ NameType t;
+ int r;
+
+ assert(m);
+ assert(name);
+ assert(_ret);
+/* This will load the service information files, but not actually
+ * start any services or anything */
+
+
+ if ((ret = manager_get_name(m, name)))
+ goto finish;
+
+ if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID)
+ return -EINVAL;
+
+ if (!(ret = name_new(m)))
+ return -ENOMEM;
+
+ ret->meta.type = t;
+
+ if (!(ret->meta.names = strv_new(name, NULL))) {
+ name_free(ret);
+ return -ENOMEM;
+ }
+
+ if ((r = name_link(ret)) < 0) {
+ name_free(ret);
+ return r;
+ }
+
+ /* At this point the new entry is created and linked. However,
+ * not loaded. Now load this entry and all its dependencies
+ * recursively */
+
+ dispatch_load_queue(m);
+
+finish:
+
+ *_ret = ret;
+ return 0;
+}