/* Detach from next 'bigger' objects */
if (j->linked) {
- if (j->name && j->name->meta.job == j)
- j->name->meta.job = NULL;
+ assert(j->name);
+ assert(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 */
+ hashmap_remove(j->manager->jobs_to_add, j->name);
+ set_remove(j->manager->jobs_to_remove, j);
+ /* Free data and next 'smaller' objects */
free(j);
}
#include "conf-parser.h"
#include "load-fragment.h"
-int config_parse_names(
+int config_parse_deps(
const char *filename,
unsigned line,
const char *section,
return 0;
}
+int config_parse_names(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Set **set = data;
+ Name *name = userdata;
+ char *w;
+ size_t l;
+ char *state;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD(w, &l, rvalue, state) {
+ char *t;
+ int r;
+ Name *other;
+
+ if (!(t = strndup(w, l)))
+ return -ENOMEM;
+
+ other = manager_get_name(name->meta.manager, t);
+
+ if (other) {
+
+ if (other != name) {
+
+ if (other->meta.state != NAME_STUB) {
+ free(t);
+ return -EEXIST;
+ }
+
+ if ((r = name_merge(name, other) < 0)) {
+ free(t);
+ return r;
+ }
+ }
+
+ } else {
+
+ if (!*set)
+ if (!(*set = set_new(trivial_hash_func, trivial_compare_func))) {
+ free(t);
+ return -ENOMEM;
+ }
+
+ if ((r = set_put(*set, t)) < 0) {
+ free(t);
+ return r;
+ }
+ }
+
+ free(t);
+ }
+
+ return 0;
+}
+
int name_load_fragment(Name *n) {
const ConfigItem items[] = {
- { "Names", config_parse_strv, &n->meta.names, "Meta" },
- { "Description", config_parse_string, &n->meta.description, "Meta" },
- { "Requires", config_parse_names, &n->meta.requires, "Meta" },
- { "SoftRequires", config_parse_names, &n->meta.soft_requires, "Meta" },
- { "Wants", config_parse_names, &n->meta.wants, "Meta" },
- { "Requisite", config_parse_names, &n->meta.requisite, "Meta" },
- { "SoftRequisite", config_parse_names, &n->meta.soft_requisite, "Meta" },
- { "Conflicts", config_parse_names, &n->meta.conflicts, "Meta" },
- { "Before", config_parse_names, &n->meta.before, "Meta" },
- { "After", config_parse_names, &n->meta.after, "Meta" },
+ { "Names", config_parse_names, &n->meta.names, "Meta" },
+ { "Description", config_parse_string, &n->meta.description, "Meta" },
+ { "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" },
+ { "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" },
+ { "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" },
+ { "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" },
+ { "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" },
+ { "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" },
+ { "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" },
+ { "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" },
{ NULL, NULL, NULL, NULL }
};
- char **t, **l;
+ char *t;
int r;
+ void *state;
assert(n);
assert(n->meta.state == NAME_STUB);
- /* We copy the strv here so that we can iterate through it
- * while being safe for modification */
- if (!(l = strv_copy(n->meta.names)))
- return -ENOMEM;
-
- STRV_FOREACH(t, n->meta.names)
- if ((r = config_parse(*t, items, n)) < 0)
+ SET_FOREACH(t, n->meta.names, state)
+ if ((r = config_parse(t, items, n)) < 0)
goto fail;
- return 0;
+ r = 0;
fail:
-
- strv_free(l);
- return 0;
+ return r;
}
#include <assert.h>
#include <errno.h>
+#include <string.h>
#include "manager.h"
#include "hashmap.h"
goto fail;
if (type == JOB_START || type == JOB_VERIFY_STARTED || type == JOB_RESTART_FINISH) {
- SET_FOREACH(dep, ret->name->meta.requires, state)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state)
if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
goto fail;
- SET_FOREACH(dep, ret->name->meta.soft_requires, state)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_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)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state)
if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
goto fail;
- SET_FOREACH(dep, ret->name->meta.requisite, state)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_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)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_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)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_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)
+ SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state)
if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
goto fail;
}
return hashmap_get(m->names, name);
}
-static int detect_type(Name *name) {
- char **n;
+static int verify_type(Name *name) {
+ char *n;
+ void *state;
assert(name);
- name->meta.type = _NAME_TYPE_INVALID;
+ /* Checks that all aliases of this name have the same and valid type */
- STRV_FOREACH(n, name->meta.names) {
+ SET_FOREACH(n, name->meta.names, state) {
NameType t;
- if ((t = name_type_from_string(*n)) == _NAME_TYPE_INVALID)
+ if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID)
return -EINVAL;
if (name->meta.type == _NAME_TYPE_INVALID) {
return -EINVAL;
}
+ if (name->meta.type == _NAME_TYPE_INVALID)
+ return -EINVAL;
+
return 0;
}
if (name->meta.state != NAME_STUB)
return 0;
- if ((r = detect_type(name)) < 0)
+ if ((r = verify_type(name)) < 0)
return r;
if (name->meta.type == NAME_SERVICE) {
Name *ret;
NameType t;
int r;
+ char *n;
assert(m);
assert(name);
ret->meta.type = t;
- if (!(ret->meta.names = strv_new(name, NULL))) {
+ if (!(n = strdup(name))) {
+ name_free(ret);
+ return -ENOMEM;
+ }
+
+ if (set_put(ret->meta.names, n) < 0) {
name_free(ret);
+ free(n);
return -ENOMEM;
}
struct Manager {
uint32_t current_job_id;
+ /* Note that the set of names we know of is allowed to be
+ * incosistent. However the subset of it that is loaded may
+ * not, and the list of jobs may neither. */
+
/* Active jobs and names */
Hashmap *names; /* name string => Name object n:1 */
Hashmap *jobs; /* job id => Job object 1:1 */
if (!(n = new0(Name, 1)))
return NULL;
+ if (!(n->meta.names = set_new(string_hash_func, string_compare_func))) {
+ free(n);
+ return NULL;
+ }
+
/* Not much initialization happening here at this time */
n->meta.manager = m;
n->meta.type = _NAME_TYPE_INVALID;
int name_link(Name *n) {
char **t;
int r;
+ void *state;
assert(n);
+ assert(!set_isempty(n->meta.names));
assert(!n->meta.linked);
- STRV_FOREACH(t, n->meta.names)
- if ((r = hashmap_put(n->meta.manager->names, *t, n)) < 0)
+ SET_FOREACH(t, n->meta.names, state)
+ if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0)
goto fail;
if (n->meta.state == NAME_STUB)
return 0;
fail:
- t--;
- STRV_FOREACH_BACKWARDS(t, n->meta.names)
- hashmap_remove(n->meta.manager->names, *t);
+ SET_FOREACH(t, n->meta.names, state)
+ assert_se(hashmap_remove(n->meta.manager->names, t) == n);
return r;
}
+static void bidi_set_free(Name *name, Set *s) {
+ void *state;
+ Name *other;
+
+ assert(name);
+ assert(s);
+
+ /* Frees the set and makes sure we are dropped from the
+ * inverse pointers */
+
+ SET_FOREACH(other, s, state) {
+ NameDependency d;
+
+ for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
+ set_remove(other->meta.dependencies[d], name);
+ }
+
+ set_free(s);
+}
+
void name_free(Name *name) {
+ NameDependency d;
+ char *t;
assert(name);
/* Detach from next 'bigger' objects */
-
if (name->meta.linked) {
char **t;
+ void *state;
- STRV_FOREACH(t, name->meta.names)
- hashmap_remove(name->meta.manager->names, *t);
+ SET_FOREACH(t, name->meta.names, state)
+ assert_se(hashmap_remove(name->meta.manager->names, t) == name);
- if (name->meta.job)
- job_free(name->meta.job);
+ if (name->meta.state == NAME_STUB)
+ LIST_REMOVE(Meta, name->meta.manager->load_queue, &name->meta);
}
/* 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);
+ for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
+ bidi_set_free(name, name->meta.dependencies[d]);
switch (name->meta.type) {
}
free(name->meta.description);
- strv_free(name->meta.names);
+
+ while ((t = set_steal_first(name->meta.names)))
+ free(t);
+ set_free(name->meta.names);
free(name);
}
assert(n);
- /* Adds in the missing links to make all dependencies both-ways */
+ /* Adds in the missing links to make all dependencies bidirectional */
- SET_FOREACH(other, n->meta.before, state)
- if ((r = ensure_in_set(&other->meta.after, n) < 0))
+ SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n) < 0))
return r;
- SET_FOREACH(other, n->meta.after, state)
- if ((r = ensure_in_set(&other->meta.before, n) < 0))
+ SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n) < 0))
return r;
- SET_FOREACH(other, n->meta.conflicts, state)
- if ((r = ensure_in_set(&other->meta.conflicts, n) < 0))
+ SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n) < 0))
return r;
- SET_FOREACH(other, n->meta.requires, state)
- if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+ SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_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))
+ SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n) < 0))
return r;
- SET_FOREACH(other, n->meta.requisite, state)
- if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+
+ SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
return r;
- SET_FOREACH(other, n->meta.soft_requisite, state)
- if ((r = ensure_in_set(&other->meta.required_by, n) < 0))
+ SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
+ return r;
+ SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUISITE], state)
+ if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n) < 0))
return r;
return r;
}
+
+static int ensure_merge(Set **s, Set *other) {
+
+ if (!other)
+ return 0;
+
+ if (*s)
+ return set_merge(*s, other);
+
+ if (!(*s = set_copy(other)))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int name_merge(Name *name, Name *other) {
+ int r;
+ NameDependency d;
+
+ assert(name);
+ assert(other);
+
+ assert(name->meta.manager == other->meta.manager);
+
+ if (name->meta.type != other->meta.type)
+ return -EINVAL;
+
+ if (other->meta.state != NAME_STUB)
+ return -EINVAL;
+
+ /* Merge names */
+ if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0)
+ return r;
+
+ /* Merge dependencies */
+ for (d = 0; d < _NAME_DEPENDENCY_MAX; d++)
+ if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0)
+ return r;
+
+ return 0;
+}
NAME_FAILED
} NameState;
-struct Meta {
- Manager *manager;
- NameType type;
- NameState state;
-
- char **names;
-
+typedef enum NameDependency {
/* 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' */
+ NAME_REQUIRES,
+ NAME_SOFT_REQUIRES,
+ NAME_WANTS,
+ NAME_REQUISITE,
+ NAME_SOFT_REQUISITE,
+ NAME_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
+ NAME_WANTED_BY, /* inverse of 'wants', 'soft_requires' and 'soft_requisite' is 'wanted_by' */
/* Negative dependencies */
- Set *conflicts; /* inverse of 'conflicts' is 'conflicts' */
+ NAME_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */
/* Order */
- Set *before, *after; /* inverse of before is after and vice versa */
+ NAME_BEFORE, /* inverse of before is after and vice versa */
+ NAME_AFTER,
+ _NAME_DEPENDENCY_MAX
+} NameDependency;
+
+struct Meta {
+ Manager *manager;
+ NameType type;
+ NameState state;
+
+ Set *names;
+ Set *dependencies[_NAME_DEPENDENCY_MAX];
- /* Information */
char *description;
/* If there is something to do with this name, then this is
Name *name_new(Manager *m);
void name_free(Name *name);
int name_link(Name *name);
-
+int name_merge(Name *name, Name *other);
int name_augment(Name *n);
#endif