X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=name.c;h=340e8e424ab542bce3b1b5500a91cf2350c94657;hb=279ade1311982e91a8baadf4deb6dabf651a1e5f;hp=4b4b0b87d179720a4efd9f36b0f9206779833b06;hpb=6091827530d6dd43479d6709fb6e9f745c11e900;p=elogind.git diff --git a/name.c b/name.c index 4b4b0b87d..340e8e424 100644 --- a/name.c +++ b/name.c @@ -2,11 +2,13 @@ #include #include +#include #include "set.h" #include "name.h" #include "macro.h" #include "strv.h" +#include "load-fragment.h" NameType name_type_from_string(const char *n) { NameType t; @@ -30,6 +32,32 @@ NameType name_type_from_string(const char *n) { return _NAME_TYPE_INVALID; } +#define VALID_CHARS \ + "0123456789" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "-_" + +bool name_is_valid(const char *n) { + NameType t; + const char *e, *i; + + assert(n); + + t = name_type_from_string(n); + if (t < 0 || t >= _NAME_TYPE_MAX) + return false; + + if (!(e = strrchr(n, '.'))) + return false; + + for (i = n; i < e; i++) + if (!strchr(VALID_CHARS, *i)) + return false; + + return true; +} + Name *name_new(Manager *m) { Name *n; @@ -38,6 +66,11 @@ Name *name_new(Manager *m) { 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; @@ -48,62 +81,104 @@ Name *name_new(Manager *m) { return n; } +/* FIXME: Does not rollback on failure! */ +int name_link_names(Name *n, bool replace) { + char *t; + void *state; + int r; + + assert(n); + + if (!n->meta.linked) + return 0; + + /* Link all names that aren't linked yet. */ + + SET_FOREACH(t, n->meta.names, state) + if (replace) { + if ((r = hashmap_replace(n->meta.manager->names, t, n)) < 0) + return r; + } else { + if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0) + return r; + } + + return 0; +} + int name_link(Name *n) { - char **t; int r; 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) - goto fail; + if ((r = name_sanitize(n)) < 0) + return r; + + n->meta.linked = true; + + if ((r = name_link_names(n, false)) < 0) { + char *t; + void *state; + + /* Rollback the registered names */ + SET_FOREACH(t, n->meta.names, state) + hashmap_remove_value(n->meta.manager->names, t, n); + + n->meta.linked = false; + return r; + } 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); +static void bidi_set_free(Name *name, Set *s) { + void *state; + Name *other; - return r; + assert(name); + + /* 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; + char *t; + void *state; - STRV_FOREACH(t, name->meta.names) - hashmap_remove(name->meta.manager->names, *t); + SET_FOREACH(t, name->meta.names, state) + hashmap_remove_value(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) { @@ -112,7 +187,7 @@ void name_free(Name *name) { Socket *s = SOCKET(name); for (i = 0; i < s->n_fds; i++) - nointr_close(s->fds[i]); + close_nointr(s->fds[i]); break; } @@ -142,7 +217,10 @@ void name_free(Name *name) { } 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); } @@ -223,45 +301,351 @@ static int ensure_in_set(Set **s, void *data) { if (!(*s = set_new(trivial_hash_func, trivial_compare_func))) return -ENOMEM; - if ((r = set_put(*s, data) < 0)) + if ((r = set_put(*s, data)) < 0) if (r != -EEXIST) return r; return 0; } -int name_augment(Name *n) { +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; +} + +/* FIXME: Does not rollback on failure! */ +int name_merge(Name *name, Name *other) { + int r; + NameDependency d; + + assert(name); + assert(other); + assert(name->meta.manager == other->meta.manager); + + /* This merges 'other' into 'name'. FIXME: This does not + * rollback on failure. */ + + 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; + + /* Hookup new deps and names */ + if (name->meta.linked) { + if ((r = name_sanitize(name)) < 0) + return r; + + if ((r = name_link_names(name, true)) < 0) + return r; + } + + return 0; +} + +/* FIXME: Does not rollback on failure! */ +static int augment(Name *n) { int r; void* state; Name *other; 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 0; +} + +int name_sanitize(Name *n) { + NameDependency d; + assert(n); + + /* Remove loops */ + for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) + set_remove(n->meta.dependencies[d], n); + + return augment(n); +} + +const char* name_id(Name *n) { + assert(n); + + return set_first(n->meta.names); +} + +void name_dump(Name *n, FILE *f, const char *prefix) { + + static const char* const state_table[_NAME_STATE_MAX] = { + [NAME_STUB] = "stub", + [NAME_LOADED] = "loaded", + [NAME_FAILED] = "failed" + }; + + static const char* const socket_state_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = "dead", + [SOCKET_BEFORE] = "before", + [SOCKET_START_PRE] = "start-pre", + [SOCKET_START] = "start", + [SOCKET_START_POST] = "start-post", + [SOCKET_LISTENING] = "listening", + [SOCKET_RUNNING] = "running", + [SOCKET_STOP_PRE] = "stop-pre", + [SOCKET_STOP] = "stop", + [SOCKET_STOP_POST] = "stop-post", + [SOCKET_MAINTAINANCE] = "maintainance" + }; + + static const char* const dependency_table[_NAME_DEPENDENCY_MAX] = { + [NAME_REQUIRES] = "Requires", + [NAME_SOFT_REQUIRES] = "SoftRequires", + [NAME_WANTS] = "Wants", + [NAME_REQUISITE] = "Requisite", + [NAME_SOFT_REQUISITE] = "SoftRequisite", + [NAME_REQUIRED_BY] = "RequiredBy", + [NAME_WANTED_BY] = "WantedBy", + [NAME_CONFLICTS] = "Conflicts", + [NAME_BEFORE] = "Before", + [NAME_AFTER] = "After", + }; + + void *state; + char *t; + NameDependency d; + + assert(n); + + if (!prefix) + prefix = ""; + + fprintf(f, + "%sName %s:\n" + "%s\tDescription: %s\n" + "%s\tName State: %s\n", + prefix, name_id(n), + prefix, n->meta.description ? n->meta.description : name_id(n), + prefix, state_table[n->meta.state]); + + fprintf(f, "%s\tNames: ", prefix); + SET_FOREACH(t, n->meta.names, state) + fprintf(f, "%s ", t); + fprintf(f, "\n"); + + for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) { + void *state; + Name *other; + + if (set_isempty(n->meta.dependencies[d])) + continue; + + fprintf(f, "%s\t%s: ", prefix, dependency_table[d]); + + SET_FOREACH(other, n->meta.dependencies[d], state) + fprintf(f, "%s ", name_id(other)); + + fprintf(f, "\n"); + } + + + switch (n->meta.type) { + case NAME_SOCKET: { + int r; + char *s = NULL; + const char *t; + + if ((r = address_print(&n->socket.address, &s)) < 0) + t = strerror(-r); + else + t = s; + + fprintf(f, + "%s\tAddress: %s\n" + "%s\tSocket State: %s\n", + prefix, t, + prefix, socket_state_table[n->socket.state]); + + free(s); + break; + } + + default: + ; + } + + if (n->meta.job) { + char *p; + + if (asprintf(&p, "%s\t", prefix) >= 0) + prefix = p; + else + p = NULL; + + job_dump(n->meta.job, f, prefix); + free(p); + } +} + +static int verify_type(Name *name) { + char *n; + void *state; + + assert(name); + + /* Checks that all aliases of this name have the same and valid type */ + + SET_FOREACH(n, name->meta.names, state) { + 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; + } + + if (name->meta.type == _NAME_TYPE_INVALID) + return -EINVAL; + + return 0; +} + +static int service_load_sysv(Service *s) { + assert(s); + + /* Load service data from SysV init scripts, preferably with + * LSB headers ... */ + + return -ENOENT; +} + +static int name_load_fstab(Name *n) { + assert(n); + assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT); + + /* Load mount data from /etc/fstab */ + + return 0; +} + +static int snapshot_load(Snapshot *s) { + assert(s); + + /* Load snapshots from disk */ + + return 0; +} + +static int name_load_dropin(Name *n) { + assert(n); + + /* Load dependencies from drop-in directories */ + + return 0; +} + +int name_load(Name *name) { + int r; + + assert(name); + + if (name->meta.state != NAME_STUB) + return 0; + + if ((r = verify_type(name)) < 0) + return r; + + if (name->meta.type == NAME_SERVICE) { + + /* Load a .service file */ + if ((r = name_load_fragment(name)) == 0) + goto finish; + + /* Load a classic init script */ + if (r == -ENOENT) + if ((r = service_load_sysv(SERVICE(name))) == 0) + goto finish; + + } else if (name->meta.type == NAME_MOUNT || + name->meta.type == NAME_AUTOMOUNT) { + + if ((r = name_load_fstab(name)) == 0) + goto finish; + + } else if (name->meta.type == NAME_SNAPSHOT) { + + if ((r = snapshot_load(SNAPSHOT(name))) == 0) + goto finish; + + } else { + if ((r = name_load_fragment(name)) == 0) + goto finish; + } + + name->meta.state = NAME_FAILED; return r; + +finish: + if ((r = name_load_dropin(name)) < 0) + return r; + + if ((r = name_sanitize(name)) < 0) + return r; + + if ((r = name_link_names(name, false)) < 0) + return r; + + name->meta.state = NAME_LOADED; + return 0; }