/*-*- Mode: C; c-basic-offset: 8 -*-*/ #include #include #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; }