2009-11-18 06:42:52 +07:00
|
|
|
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
2010-01-19 05:50:13 +07:00
|
|
|
#include <string.h>
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
#include "manager.h"
|
|
|
|
#include "hashmap.h"
|
|
|
|
#include "macro.h"
|
|
|
|
#include "strv.h"
|
2009-11-19 08:52:17 +07:00
|
|
|
#include "load-fragment.h"
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-01-19 10:15:20 +07:00
|
|
|
static void transaction_abort(Manager *m) {
|
|
|
|
Job *j;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(m->n_dependency_depth == 0);
|
|
|
|
|
|
|
|
while ((j = hashmap_steal_first(m->jobs_to_add)))
|
|
|
|
job_free(j);
|
|
|
|
|
|
|
|
set_clear(m->jobs_to_remove);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int transaction_activate(Manager *m) {
|
|
|
|
Job *j;
|
|
|
|
int r;
|
|
|
|
void *state;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(m->n_dependency_depth == 0);
|
|
|
|
|
|
|
|
/* This applies the changes recorded in jobs_to_add and
|
|
|
|
* jobs_to_remove to the actual list of jobs */
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(j, m->jobs_to_add, state) {
|
|
|
|
assert(!j->linked);
|
|
|
|
|
|
|
|
if ((r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j)) < 0)
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* all entries are now registered, now make sure the names
|
|
|
|
* know about that. */
|
|
|
|
|
|
|
|
while ((j = hashmap_steal_first(m->jobs_to_add))) {
|
|
|
|
j->name->meta.job = j;
|
|
|
|
j->linked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((j = set_steal_first(m->jobs_to_remove)))
|
|
|
|
job_free(j);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rollback:
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(j, m->jobs_to_add, state)
|
|
|
|
hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
|
|
|
|
|
|
|
|
transaction_abort(m);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
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);
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2010-01-19 10:15:20 +07:00
|
|
|
m->n_dependency_depth ++;
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
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) {
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
|
|
|
|
goto fail;
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
|
|
|
|
goto fail;
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
if ((r = manager_add_job(m, type, dep, JOB_FAIL, NULL)) < 0)
|
|
|
|
goto fail;
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, mode, NULL)) < 0)
|
|
|
|
goto fail;
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
if ((r = manager_add_job(m, JOB_VERIFY_STARTED, dep, JOB_FAIL, NULL)) < 0)
|
|
|
|
goto fail;
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
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) {
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state)
|
2009-11-18 06:42:52 +07:00
|
|
|
if ((r = manager_add_job(m, type, dep, mode, NULL)) < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-01-19 10:15:20 +07:00
|
|
|
if (--m->n_dependency_depth <= 0)
|
|
|
|
if ((r = transaction_activate(m)) < 0) {
|
|
|
|
transaction_abort(m);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
if (_ret)
|
|
|
|
*_ret = ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
job_free(ret);
|
|
|
|
|
2010-01-19 10:15:20 +07:00
|
|
|
if (--m->n_dependency_depth <= 0)
|
|
|
|
transaction_abort(m);
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
static int verify_type(Name *name) {
|
|
|
|
char *n;
|
|
|
|
void *state;
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
/* Checks that all aliases of this name have the same and valid type */
|
2009-11-18 06:42:52 +07:00
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
SET_FOREACH(n, name->meta.names, state) {
|
2009-11-18 06:42:52 +07:00
|
|
|
NameType t;
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID)
|
2009-11-18 06:42:52 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (name->meta.type == _NAME_TYPE_INVALID) {
|
|
|
|
name->meta.type = t;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name->meta.type != t)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
if (name->meta.type == _NAME_TYPE_INVALID)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
static int service_load_sysv(Service *s) {
|
2009-11-18 06:42:52 +07:00
|
|
|
assert(s);
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
/* Load service data from SysV init scripts, preferably with
|
|
|
|
* LSB headers ... */
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
static int name_load_fstab(Name *n) {
|
2009-11-18 06:42:52 +07:00
|
|
|
assert(n);
|
|
|
|
assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT);
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
/* Load mount data from /etc/fstab */
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snapshot_load(Snapshot *s) {
|
|
|
|
assert(s);
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
/* Load snapshots from disk */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int name_load_dropin(Name *n) {
|
|
|
|
assert(n);
|
|
|
|
|
|
|
|
/* Load dependencies from drop-in directories */
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int load(Name *name) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
if (name->meta.state != NAME_STUB)
|
|
|
|
return 0;
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
if ((r = verify_type(name)) < 0)
|
2009-11-18 06:42:52 +07:00
|
|
|
return r;
|
|
|
|
|
|
|
|
if (name->meta.type == NAME_SERVICE) {
|
|
|
|
|
|
|
|
/* Load a .service file */
|
2009-11-19 08:52:17 +07:00
|
|
|
if ((r = name_load_fragment(name)) == 0)
|
2009-11-18 06:42:52 +07:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
/* Load a classic init script */
|
|
|
|
if (r == -ENOENT)
|
2009-11-19 08:52:17 +07:00
|
|
|
if ((r = service_load_sysv(SERVICE(name))) == 0)
|
2009-11-18 06:42:52 +07:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
} else if (name->meta.type == NAME_MOUNT ||
|
|
|
|
name->meta.type == NAME_AUTOMOUNT) {
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
if ((r = name_load_fstab(name)) == 0)
|
2009-11-18 06:42:52 +07:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
} else if (name->meta.type == NAME_SNAPSHOT) {
|
|
|
|
|
|
|
|
if ((r = snapshot_load(SNAPSHOT(name))) == 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
} else {
|
2009-11-19 08:52:17 +07:00
|
|
|
if ((r = name_load_fragment(name)) == 0)
|
2009-11-18 06:42:52 +07:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
name->meta.state = NAME_FAILED;
|
|
|
|
return r;
|
|
|
|
|
|
|
|
finish:
|
2009-11-19 08:52:17 +07:00
|
|
|
if ((r = name_load_dropin(name)) < 0)
|
|
|
|
return r;
|
|
|
|
|
2010-01-19 10:15:20 +07:00
|
|
|
if ((r = name_link_names(name)) < 0)
|
|
|
|
return r;
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
name->meta.state = NAME_LOADED;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dispatch_load_queue(Manager *m) {
|
|
|
|
Meta *meta;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
/* Make sure we are not run recursively */
|
|
|
|
if (m->dispatching_load_queue)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
m->dispatching_load_queue = true;
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
m->dispatching_load_queue = false;
|
|
|
|
|
2009-11-18 06:42:52 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_load_name(Manager *m, const char *name, Name **_ret) {
|
|
|
|
Name *ret;
|
|
|
|
NameType t;
|
|
|
|
int r;
|
2010-01-19 05:50:13 +07:00
|
|
|
char *n;
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(name);
|
|
|
|
assert(_ret);
|
|
|
|
|
2009-11-19 08:52:17 +07:00
|
|
|
if (!name_is_valid(name))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* This will load the service information files, but not actually
|
|
|
|
* start any services or anything */
|
2009-11-18 06:42:52 +07:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2010-01-19 05:50:13 +07:00
|
|
|
if (!(n = strdup(name))) {
|
|
|
|
name_free(ret);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (set_put(ret->meta.names, n) < 0) {
|
2009-11-18 06:42:52 +07:00
|
|
|
name_free(ret);
|
2010-01-19 05:50:13 +07:00
|
|
|
free(n);
|
2009-11-18 06:42:52 +07:00
|
|
|
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;
|
|
|
|
}
|
2010-01-19 06:22:34 +07:00
|
|
|
|
|
|
|
void manager_dump_jobs(Manager *s, FILE *f) {
|
|
|
|
void *state;
|
|
|
|
Job *j;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(f);
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(j, s->jobs, state)
|
|
|
|
job_dump(j, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
void manager_dump_names(Manager *s, FILE *f) {
|
|
|
|
void *state;
|
|
|
|
Name *n;
|
2010-01-19 10:15:20 +07:00
|
|
|
const char *t;
|
2010-01-19 06:22:34 +07:00
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(f);
|
|
|
|
|
2010-01-19 10:15:20 +07:00
|
|
|
HASHMAP_FOREACH_KEY(n, t, s->names, state)
|
|
|
|
if (name_id(n) == t)
|
|
|
|
name_dump(n, f);
|
2010-01-19 06:22:34 +07:00
|
|
|
}
|