mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
rework merging/loading logic
This commit is contained in:
parent
5af98f8284
commit
23a177ef7c
21
automount.c
21
automount.c
@ -26,7 +26,7 @@
|
||||
#include "load-fragment.h"
|
||||
#include "load-dropin.h"
|
||||
|
||||
static int automount_init(Unit *u) {
|
||||
static int automount_init(Unit *u, UnitLoadState *new_state) {
|
||||
int r;
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
@ -35,13 +35,28 @@ static int automount_init(Unit *u) {
|
||||
exec_context_init(&a->exec_context);
|
||||
|
||||
/* Load a .automount file */
|
||||
if ((r = unit_load_fragment(u)) < 0)
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
*new_state = UNIT_LOADED;
|
||||
|
||||
/* Load drop-in directory data */
|
||||
if ((r = unit_load_dropin(u)) < 0)
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_LOADED) {
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_exec_dependencies(u, &a->exec_context)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_default_cgroup(u)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
2
device.c
2
device.c
@ -381,7 +381,7 @@ fail:
|
||||
const UnitVTable device_vtable = {
|
||||
.suffix = ".device",
|
||||
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.init = unit_load_fragment_and_dropin_optional,
|
||||
.done = device_done,
|
||||
.coldplug = device_coldplug,
|
||||
|
||||
|
2
job.c
2
job.c
@ -65,7 +65,7 @@ void job_free(Job *j) {
|
||||
}
|
||||
|
||||
/* Detach from next 'smaller' objects */
|
||||
manager_transaction_unlink_job(j->manager, j);
|
||||
manager_transaction_unlink_job(j->manager, j, true);
|
||||
|
||||
if (j->in_run_queue)
|
||||
LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
|
||||
|
175
load-fragment.c
175
load-fragment.c
@ -95,36 +95,15 @@ static int config_parse_names(
|
||||
FOREACH_WORD(w, l, rvalue, state) {
|
||||
char *t;
|
||||
int r;
|
||||
Unit *other;
|
||||
|
||||
if (!(t = strndup(w, l)))
|
||||
return -ENOMEM;
|
||||
|
||||
other = manager_get_unit(u->meta.manager, t);
|
||||
|
||||
if (other) {
|
||||
|
||||
if (other != u) {
|
||||
|
||||
if (other->meta.load_state != UNIT_STUB) {
|
||||
free(t);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if ((r = unit_merge(u, other)) < 0) {
|
||||
free(t);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((r = unit_add_name(u, t)) < 0) {
|
||||
free(t);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
r = unit_merge_by_name(u, t);
|
||||
free(t);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1070,7 +1049,48 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_from_path(Unit *u, const char *path) {
|
||||
static int merge_by_names(Unit **u, Set *names, const char *id) {
|
||||
char *k;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(*u);
|
||||
assert(names);
|
||||
|
||||
/* Let's try to add in all symlink names we found */
|
||||
while ((k = set_steal_first(names))) {
|
||||
|
||||
/* First try to merge in the other name into our
|
||||
* unit */
|
||||
if ((r = unit_merge_by_name(*u, k)) < 0) {
|
||||
Unit *other;
|
||||
|
||||
/* Hmm, we couldn't merge the other unit into
|
||||
* ours? Then let's try it the other way
|
||||
* round */
|
||||
|
||||
other = manager_get_unit((*u)->meta.manager, k);
|
||||
free(k);
|
||||
|
||||
if (other)
|
||||
if ((r = unit_merge(other, *u)) >= 0) {
|
||||
*u = other;
|
||||
return merge_by_names(u, names, NULL);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (id == k)
|
||||
unit_choose_id(*u, id);
|
||||
|
||||
free(k);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
|
||||
|
||||
static const char* const section_table[_UNIT_TYPE_MAX] = {
|
||||
[UNIT_SERVICE] = "Service",
|
||||
@ -1184,8 +1204,12 @@ static int load_from_path(Unit *u, const char *path) {
|
||||
char *k;
|
||||
int r;
|
||||
Set *symlink_names;
|
||||
FILE *f;
|
||||
char *filename = NULL, *id;
|
||||
FILE *f = NULL;
|
||||
char *filename = NULL, *id = NULL;
|
||||
Unit *merged;
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
|
||||
sections[0] = "Meta";
|
||||
sections[1] = section_table[u->meta.type];
|
||||
@ -1243,90 +1267,79 @@ static int load_from_path(Unit *u, const char *path) {
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
r = 0; /* returning 0 means: no suitable config file found */
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
merged = u;
|
||||
if ((r = merge_by_names(&merged, symlink_names, id)) < 0)
|
||||
goto finish;
|
||||
|
||||
if (merged != u) {
|
||||
*new_state = UNIT_MERGED;
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Now, parse the file contents */
|
||||
r = config_parse(filename, f, sections, items, u);
|
||||
if (r < 0)
|
||||
if ((r = config_parse(filename, f, sections, items, u)) < 0)
|
||||
goto finish;
|
||||
|
||||
/* Let's try to add in all symlink names we found */
|
||||
while ((k = set_steal_first(symlink_names))) {
|
||||
if ((r = unit_add_name(u, k)) < 0)
|
||||
goto finish;
|
||||
|
||||
|
||||
if (id == k)
|
||||
unit_choose_id(u, id);
|
||||
free(k);
|
||||
}
|
||||
|
||||
|
||||
free(u->meta.fragment_path);
|
||||
u->meta.fragment_path = filename;
|
||||
filename = NULL;
|
||||
|
||||
r = 1; /* returning 1 means: suitable config file found and loaded */
|
||||
*new_state = UNIT_LOADED;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
while ((k = set_steal_first(symlink_names)))
|
||||
free(k);
|
||||
|
||||
set_free(symlink_names);
|
||||
free(filename);
|
||||
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int unit_load_fragment(Unit *u) {
|
||||
int r = 0;
|
||||
int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB);
|
||||
|
||||
if (u->meta.fragment_path)
|
||||
r = load_from_path(u, u->meta.fragment_path);
|
||||
else {
|
||||
if (u->meta.fragment_path) {
|
||||
|
||||
if ((r = load_from_path(u, u->meta.fragment_path, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
} else {
|
||||
Iterator i;
|
||||
const char *t;
|
||||
|
||||
/* Try to find the unit under its id */
|
||||
if ((t = unit_id(u)))
|
||||
r = load_from_path(u, t);
|
||||
if ((r = load_from_path(u, t, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
/* Try to find an alias we can load this with */
|
||||
if (r == 0)
|
||||
SET_FOREACH(t, u->meta.names, i)
|
||||
if ((r = load_from_path(u, t)) != 0)
|
||||
if (*new_state == UNIT_STUB)
|
||||
SET_FOREACH(t, u->meta.names, i) {
|
||||
|
||||
if (unit_id(u) == t)
|
||||
continue;
|
||||
|
||||
if ((r = load_from_path(u, t, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state != UNIT_STUB)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
ExecContext *c;
|
||||
|
||||
if (u->meta.type == UNIT_SOCKET)
|
||||
c = &u->socket.exec_context;
|
||||
else if (u->meta.type == UNIT_SERVICE)
|
||||
c = &u->service.exec_context;
|
||||
else
|
||||
c = NULL;
|
||||
|
||||
if (c &&
|
||||
(c->output == EXEC_OUTPUT_KERNEL || c->output == EXEC_OUTPUT_SYSLOG)) {
|
||||
int k;
|
||||
|
||||
/* If syslog or kernel logging is requested, make sure
|
||||
* our own logging daemon is run first. */
|
||||
|
||||
if ((k = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0)
|
||||
return k;
|
||||
|
||||
if (u->meta.manager->running_as != MANAGER_SESSION)
|
||||
if ((k = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0)
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
@ -26,6 +26,6 @@
|
||||
|
||||
/* Read service data from .desktop file style configuration fragments */
|
||||
|
||||
int unit_load_fragment(Unit *u);
|
||||
int unit_load_fragment(Unit *u, UnitLoadState *new_state);
|
||||
|
||||
#endif
|
||||
|
124
manager.c
124
manager.c
@ -194,6 +194,7 @@ fail:
|
||||
static int manager_find_paths(Manager *m) {
|
||||
const char *e;
|
||||
char *t;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* First priority is whatever has been passed to us via env
|
||||
@ -326,6 +327,22 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
|
||||
Meta *meta;
|
||||
unsigned n = 0;
|
||||
|
||||
assert(m);
|
||||
|
||||
while ((meta = m->cleanup_queue)) {
|
||||
assert(meta->in_cleanup_queue);
|
||||
|
||||
unit_free(UNIT(meta));
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void manager_free(Manager *m) {
|
||||
UnitType c;
|
||||
Unit *u;
|
||||
@ -339,6 +356,8 @@ void manager_free(Manager *m) {
|
||||
while ((u = hashmap_first(m->units)))
|
||||
unit_free(u);
|
||||
|
||||
manager_dispatch_cleanup_queue(m);
|
||||
|
||||
for (c = 0; c < _UNIT_TYPE_MAX; c++)
|
||||
if (unit_vtable[c]->shutdown)
|
||||
unit_vtable[c]->shutdown(m);
|
||||
@ -402,13 +421,13 @@ int manager_coldplug(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void transaction_delete_job(Manager *m, Job *j) {
|
||||
static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
|
||||
assert(m);
|
||||
assert(j);
|
||||
|
||||
/* Deletes one job from the transaction */
|
||||
|
||||
manager_transaction_unlink_job(m, j);
|
||||
manager_transaction_unlink_job(m, j, delete_dependencies);
|
||||
|
||||
if (!j->installed)
|
||||
job_free(j);
|
||||
@ -421,7 +440,7 @@ static void transaction_delete_unit(Manager *m, Unit *u) {
|
||||
* transaction */
|
||||
|
||||
while ((j = hashmap_get(m->transaction_jobs, u)))
|
||||
transaction_delete_job(m, j);
|
||||
transaction_delete_job(m, j, true);
|
||||
}
|
||||
|
||||
static void transaction_clean_dependencies(Manager *m) {
|
||||
@ -449,7 +468,7 @@ static void transaction_abort(Manager *m) {
|
||||
|
||||
while ((j = hashmap_first(m->transaction_jobs)))
|
||||
if (j->installed)
|
||||
transaction_delete_job(m, j);
|
||||
transaction_delete_job(m, j, true);
|
||||
else
|
||||
job_free(j);
|
||||
|
||||
@ -541,7 +560,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
|
||||
/* Kill the other job */
|
||||
other->subject_list = NULL;
|
||||
other->object_list = NULL;
|
||||
transaction_delete_job(m, other);
|
||||
transaction_delete_job(m, other, true);
|
||||
}
|
||||
|
||||
static int delete_one_unmergeable_job(Manager *m, Job *j) {
|
||||
@ -574,8 +593,8 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) {
|
||||
return -ENOEXEC;
|
||||
|
||||
/* Ok, we can drop one, so let's do so. */
|
||||
log_debug("Try to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
|
||||
transaction_delete_job(m, d);
|
||||
log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
|
||||
transaction_delete_job(m, d, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -643,6 +662,46 @@ static int transaction_merge_jobs(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void transaction_drop_redundant(Manager *m) {
|
||||
bool again;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Goes through the transaction and removes all jobs that are
|
||||
* a noop */
|
||||
|
||||
do {
|
||||
Job *j;
|
||||
Iterator i;
|
||||
|
||||
again = false;
|
||||
|
||||
HASHMAP_FOREACH(j, m->transaction_jobs, i) {
|
||||
bool changes_something = false;
|
||||
Job *k;
|
||||
|
||||
LIST_FOREACH(transaction, k, j) {
|
||||
|
||||
if (!job_is_anchor(k) &&
|
||||
job_type_is_redundant(k->type, unit_active_state(k->unit)))
|
||||
continue;
|
||||
|
||||
changes_something = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (changes_something)
|
||||
continue;
|
||||
|
||||
log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type));
|
||||
transaction_delete_job(m, j, false);
|
||||
again = true;
|
||||
break;
|
||||
}
|
||||
|
||||
} while (again);
|
||||
}
|
||||
|
||||
static bool unit_matters_to_anchor(Unit *u, Job *j) {
|
||||
assert(u);
|
||||
assert(!j->transaction_prev);
|
||||
@ -680,11 +739,11 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
|
||||
* since smart how we are we stored our way back in
|
||||
* there. */
|
||||
|
||||
log_debug("Found cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
|
||||
log_debug("Found ordering cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
|
||||
|
||||
for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
|
||||
|
||||
log_debug("Walked on cycle path to %s/%s", unit_id(j->unit), job_type_to_string(j->type));
|
||||
log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type));
|
||||
|
||||
if (!k->installed &&
|
||||
!unit_matters_to_anchor(k->unit, k)) {
|
||||
@ -772,7 +831,7 @@ static void transaction_collect_garbage(Manager *m) {
|
||||
continue;
|
||||
|
||||
log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type));
|
||||
transaction_delete_job(m, j);
|
||||
transaction_delete_job(m, j, true);
|
||||
again = true;
|
||||
break;
|
||||
}
|
||||
@ -847,7 +906,7 @@ static void transaction_minimize_impact(Manager *m) {
|
||||
/* Ok, let's get rid of this */
|
||||
log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type));
|
||||
|
||||
transaction_delete_job(m, j);
|
||||
transaction_delete_job(m, j, true);
|
||||
again = true;
|
||||
break;
|
||||
}
|
||||
@ -932,12 +991,15 @@ static int transaction_activate(Manager *m, JobMode mode) {
|
||||
* jobs if we don't have to. */
|
||||
transaction_minimize_impact(m);
|
||||
|
||||
/* Third step: Drop redundant jobs */
|
||||
transaction_drop_redundant(m);
|
||||
|
||||
for (;;) {
|
||||
/* Third step: Let's remove unneeded jobs that might
|
||||
/* Fourth step: Let's remove unneeded jobs that might
|
||||
* be lurking. */
|
||||
transaction_collect_garbage(m);
|
||||
|
||||
/* Fourth step: verify order makes sense and correct
|
||||
/* Fifth step: verify order makes sense and correct
|
||||
* cycles if necessary and possible */
|
||||
if ((r = transaction_verify_order(m, &generation)) >= 0)
|
||||
break;
|
||||
@ -952,7 +1014,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* Fifth step: let's drop unmergeable entries if
|
||||
/* Sixth step: let's drop unmergeable entries if
|
||||
* necessary and possible, merge entries we can
|
||||
* merge */
|
||||
if ((r = transaction_merge_jobs(m)) >= 0)
|
||||
@ -963,7 +1025,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
/* Sixth step: an entry got dropped, let's garbage
|
||||
/* Seventh step: an entry got dropped, let's garbage
|
||||
* collect its dependencies. */
|
||||
transaction_collect_garbage(m);
|
||||
|
||||
@ -971,14 +1033,17 @@ static int transaction_activate(Manager *m, JobMode mode) {
|
||||
* unmergeable entries ... */
|
||||
}
|
||||
|
||||
/* Seventh step: check whether we can actually apply this */
|
||||
/* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
|
||||
transaction_drop_redundant(m);
|
||||
|
||||
/* Ninth step: check whether we can actually apply this */
|
||||
if (mode == JOB_FAIL)
|
||||
if ((r = transaction_is_destructive(m, mode)) < 0) {
|
||||
log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r));
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
/* Eights step: apply changes */
|
||||
/* Tenth step: apply changes */
|
||||
if ((r = transaction_apply(m, mode)) < 0) {
|
||||
log_debug("Failed to apply transaction: %s", strerror(-r));
|
||||
goto rollback;
|
||||
@ -1037,10 +1102,12 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool f
|
||||
if (is_new)
|
||||
*is_new = true;
|
||||
|
||||
log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type));
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
void manager_transaction_unlink_job(Manager *m, Job *j) {
|
||||
void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) {
|
||||
assert(m);
|
||||
assert(j);
|
||||
|
||||
@ -1064,11 +1131,11 @@ void manager_transaction_unlink_job(Manager *m, Job *j) {
|
||||
|
||||
job_dependency_free(j->object_list);
|
||||
|
||||
if (other) {
|
||||
if (other && delete_dependencies) {
|
||||
log_debug("Deleting job %s/%s as dependency of job %s/%s",
|
||||
unit_id(other->unit), job_type_to_string(other->type),
|
||||
unit_id(j->unit), job_type_to_string(j->type));
|
||||
transaction_delete_job(m, other);
|
||||
transaction_delete_job(m, other, delete_dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1244,7 +1311,7 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
|
||||
|
||||
manager_dispatch_load_queue(m);
|
||||
|
||||
*_ret = ret;
|
||||
*_ret = unit_follow_merge(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1317,10 +1384,9 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
|
||||
m->dispatching_dbus_queue = true;
|
||||
|
||||
while ((meta = m->dbus_unit_queue)) {
|
||||
Unit *u = (Unit*) meta;
|
||||
assert(u->meta.in_dbus_queue);
|
||||
assert(meta->in_dbus_queue);
|
||||
|
||||
bus_unit_send_change_signal(u);
|
||||
bus_unit_send_change_signal(UNIT(meta));
|
||||
n++;
|
||||
}
|
||||
|
||||
@ -1521,7 +1587,7 @@ int manager_loop(Manager *m) {
|
||||
|
||||
assert(m);
|
||||
|
||||
for (;;) {
|
||||
do {
|
||||
struct epoll_event event;
|
||||
int n;
|
||||
|
||||
@ -1531,6 +1597,9 @@ int manager_loop(Manager *m) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (manager_dispatch_cleanup_queue(m) > 0)
|
||||
continue;
|
||||
|
||||
if (manager_dispatch_load_queue(m) > 0)
|
||||
continue;
|
||||
|
||||
@ -1555,10 +1624,9 @@ int manager_loop(Manager *m) {
|
||||
|
||||
if ((r = process_event(m, &event, &quit)) < 0)
|
||||
return r;
|
||||
} while (!quit);
|
||||
|
||||
if (quit)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
|
||||
|
@ -125,6 +125,8 @@ struct Manager {
|
||||
LIST_HEAD(Meta, dbus_unit_queue);
|
||||
LIST_HEAD(Job, dbus_job_queue);
|
||||
|
||||
LIST_HEAD(Meta, cleanup_queue);
|
||||
|
||||
/* Jobs to be added */
|
||||
Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
|
||||
JobDependency *transaction_anchor;
|
||||
@ -181,7 +183,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for
|
||||
void manager_dump_units(Manager *s, FILE *f, const char *prefix);
|
||||
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
|
||||
|
||||
void manager_transaction_unlink_job(Manager *m, Job *j);
|
||||
void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies);
|
||||
|
||||
void manager_clear_jobs(Manager *m);
|
||||
|
||||
|
2
mount.c
2
mount.c
@ -472,7 +472,7 @@ void mount_fd_event(Manager *m, int events) {
|
||||
const UnitVTable mount_vtable = {
|
||||
.suffix = ".mount",
|
||||
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.init = unit_load_fragment_and_dropin_optional,
|
||||
.done = mount_done,
|
||||
.coldplug = mount_coldplug,
|
||||
|
||||
|
216
service.c
216
service.c
@ -35,6 +35,17 @@
|
||||
#define NEWLINES "\n\r"
|
||||
#define LINE_MAX 4096
|
||||
|
||||
static const char * const rcnd_table[] = {
|
||||
"../rc0.d", SPECIAL_RUNLEVEL0_TARGET,
|
||||
"../rc1.d", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"../rc2.d", SPECIAL_RUNLEVEL2_TARGET,
|
||||
"../rc3.d", SPECIAL_RUNLEVEL3_TARGET,
|
||||
"../rc4.d", SPECIAL_RUNLEVEL4_TARGET,
|
||||
"../rc5.d", SPECIAL_RUNLEVEL5_TARGET,
|
||||
"../rc6.d", SPECIAL_RUNLEVEL6_TARGET
|
||||
};
|
||||
|
||||
|
||||
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_DEAD] = UNIT_INACTIVE,
|
||||
[SERVICE_START_PRE] = UNIT_ACTIVATING,
|
||||
@ -128,8 +139,8 @@ static int sysv_chkconfig_order(Service *s) {
|
||||
if (s->sysv_start_priority < 0)
|
||||
return 0;
|
||||
|
||||
/* If no LSB header is found we try to order init scripts via
|
||||
* the start priority of the chkconfig header. */
|
||||
/* For each pair of services where at least one lacks a LSB
|
||||
* header, we use the start priority value to order things. */
|
||||
|
||||
LIST_FOREACH(units_per_type, other, UNIT(s)->meta.manager->units_per_type[UNIT_SERVICE]) {
|
||||
Service *t;
|
||||
@ -143,6 +154,9 @@ static int sysv_chkconfig_order(Service *s) {
|
||||
if (t->sysv_start_priority < 0)
|
||||
continue;
|
||||
|
||||
if (s->sysv_has_lsb && t->sysv_has_lsb)
|
||||
continue;
|
||||
|
||||
if (t->sysv_start_priority < s->sysv_start_priority)
|
||||
d = UNIT_AFTER;
|
||||
else if (t->sysv_start_priority > s->sysv_start_priority)
|
||||
@ -200,7 +214,67 @@ static int sysv_exec_commands(Service *s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_load_sysv_path(Service *s, const char *path) {
|
||||
static int priority_from_rcd(Service *s, const char *init_script) {
|
||||
char **p;
|
||||
unsigned i;
|
||||
|
||||
STRV_FOREACH(p, UNIT(s)->meta.manager->sysvinit_path)
|
||||
for (i = 0; i < ELEMENTSOF(rcnd_table); i += 2) {
|
||||
char *path;
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
|
||||
if (asprintf(&path, "%s/%s", *p, rcnd_table[i]) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
d = opendir(path);
|
||||
free(path);
|
||||
|
||||
if (!d) {
|
||||
if (errno != ENOENT)
|
||||
log_warning("opendir() failed on %s: %s", path, strerror(errno));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
int a, b;
|
||||
|
||||
if (ignore_file(de->d_name))
|
||||
continue;
|
||||
|
||||
if (de->d_name[0] != 'S')
|
||||
continue;
|
||||
|
||||
if (strlen(de->d_name) < 4)
|
||||
continue;
|
||||
|
||||
if (!streq(de->d_name + 3, init_script))
|
||||
continue;
|
||||
|
||||
/* Yay, we found it! Now decode the priority */
|
||||
|
||||
a = undecchar(de->d_name[1]);
|
||||
b = undecchar(de->d_name[2]);
|
||||
|
||||
if (a < 0 || b < 0)
|
||||
continue;
|
||||
|
||||
s->sysv_start_priority = a*10 + b;
|
||||
|
||||
log_debug("Determined priority %i from link farm for %s", s->sysv_start_priority, unit_id(UNIT(s)));
|
||||
|
||||
closedir(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *new_state) {
|
||||
FILE *f;
|
||||
Unit *u;
|
||||
unsigned line = 0;
|
||||
@ -211,7 +285,10 @@ static int service_load_sysv_path(Service *s, const char *path) {
|
||||
LSB,
|
||||
LSB_DESCRIPTION
|
||||
} state = NORMAL;
|
||||
bool has_lsb = false;
|
||||
|
||||
assert(s);
|
||||
assert(path);
|
||||
assert(new_state);
|
||||
|
||||
u = UNIT(s);
|
||||
|
||||
@ -249,7 +326,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
|
||||
|
||||
if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
|
||||
state = LSB;
|
||||
has_lsb = true;
|
||||
s->sysv_has_lsb = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -470,14 +547,21 @@ static int service_load_sysv_path(Service *s, const char *path) {
|
||||
}
|
||||
}
|
||||
|
||||
/* If the init script has no LSB header, then let's
|
||||
* enforce the ordering via the chkconfig
|
||||
* priorities */
|
||||
/* If init scripts have no LSB header, then we enforce the
|
||||
* ordering via the chkconfig priorities. We try to determine
|
||||
* a priority for *all* init scripts here, since they are
|
||||
* needed as soon as at least one non-LSB script is used. */
|
||||
|
||||
if (!has_lsb)
|
||||
if ((r = sysv_chkconfig_order(s)) < 0)
|
||||
if (s->sysv_start_priority < 0) {
|
||||
log_debug("%s has no chkconfig header, trying to determine SysV priority from link farm.", unit_id(u));
|
||||
|
||||
if ((r = priority_from_rcd(s, file_name_from_path(path))) < 0)
|
||||
goto finish;
|
||||
|
||||
if (s->sysv_start_priority < 0)
|
||||
log_warning("%s has neither a chkconfig header nor a directory link, cannot order unit!", unit_id(u));
|
||||
}
|
||||
|
||||
if ((r = sysv_exec_commands(s)) < 0)
|
||||
goto finish;
|
||||
|
||||
@ -485,7 +569,8 @@ static int service_load_sysv_path(Service *s, const char *path) {
|
||||
(r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_SYSINIT_SERVICE)) < 0)
|
||||
goto finish;
|
||||
|
||||
r = 1;
|
||||
*new_state = UNIT_LOADED;
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
|
||||
@ -495,7 +580,7 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int service_load_sysv_name(Service *s, const char *name) {
|
||||
static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *new_state) {
|
||||
char **p;
|
||||
|
||||
assert(s);
|
||||
@ -511,22 +596,26 @@ static int service_load_sysv_name(Service *s, const char *name) {
|
||||
assert(endswith(path, ".service"));
|
||||
path[strlen(path)-8] = 0;
|
||||
|
||||
r = service_load_sysv_path(s, path);
|
||||
r = service_load_sysv_path(s, path, new_state);
|
||||
free(path);
|
||||
|
||||
if (r != 0)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state != UNIT_STUB)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_load_sysv(Service *s) {
|
||||
static int service_load_sysv(Service *s, UnitLoadState *new_state) {
|
||||
const char *t;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(new_state);
|
||||
|
||||
/* Load service data from SysV init scripts, preferably with
|
||||
* LSB headers ... */
|
||||
@ -535,21 +624,28 @@ static int service_load_sysv(Service *s) {
|
||||
return 0;
|
||||
|
||||
if ((t = unit_id(UNIT(s))))
|
||||
if ((r = service_load_sysv_name(s, t)) != 0)
|
||||
if ((r = service_load_sysv_name(s, t, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
SET_FOREACH(t, UNIT(s)->meta.names, i)
|
||||
if ((r == service_load_sysv_name(s, t)) != 0)
|
||||
return r;
|
||||
if (*new_state == UNIT_STUB)
|
||||
SET_FOREACH(t, UNIT(s)->meta.names, i) {
|
||||
if ((r == service_load_sysv_name(s, t, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state != UNIT_STUB)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_init(Unit *u) {
|
||||
static int service_init(Unit *u, UnitLoadState *new_state) {
|
||||
int r;
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB);
|
||||
|
||||
/* First, reset everything to the defaults, in case this is a
|
||||
* reload */
|
||||
@ -570,31 +666,38 @@ static int service_init(Unit *u) {
|
||||
s->permissions_start_only = false;
|
||||
s->root_directory_start_only = false;
|
||||
|
||||
s->sysv_has_lsb = false;
|
||||
|
||||
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
|
||||
|
||||
/* Load a .service file */
|
||||
if ((r = unit_load_fragment(u)) < 0) {
|
||||
service_done(u);
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Load a classic init script as a fallback, if we couldn't find anything */
|
||||
if (r == 0)
|
||||
if ((r = service_load_sysv(s)) <= 0) {
|
||||
service_done(u);
|
||||
return r < 0 ? r : -ENOENT;
|
||||
}
|
||||
if (*new_state == UNIT_STUB)
|
||||
if ((r = service_load_sysv(s, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
/* Load dropin directory data */
|
||||
if ((r = unit_load_dropin(u)) < 0) {
|
||||
service_done(u);
|
||||
return r;
|
||||
}
|
||||
/* Still nothing found? Then let's give up */
|
||||
if (*new_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
|
||||
/* Add default cgroup */
|
||||
if ((r = unit_add_default_cgroup(u)) < 0) {
|
||||
service_done(u);
|
||||
/* We were able to load something, then let's add in the
|
||||
* dropin directories. */
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
return r;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (*new_state == UNIT_LOADED) {
|
||||
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_default_cgroup(u)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = sysv_chkconfig_order(s)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -645,14 +748,17 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
if (s->sysv_path)
|
||||
fprintf(f,
|
||||
"%sSysV Init Script Path: %s\n",
|
||||
prefix, s->sysv_path);
|
||||
"%sSysV Init Script Path: %s\n"
|
||||
"%sSysV Init Script has LSB Header: %s\n",
|
||||
prefix, s->sysv_path,
|
||||
prefix, yes_no(s->sysv_has_lsb));
|
||||
|
||||
if (s->sysv_start_priority >= 0)
|
||||
fprintf(f,
|
||||
"%sSysV Start Priority: %i\n",
|
||||
"%sSysVStartPriority: %i\n",
|
||||
prefix, s->sysv_start_priority);
|
||||
|
||||
|
||||
free(p2);
|
||||
}
|
||||
|
||||
@ -1638,17 +1744,6 @@ static void service_cgroup_notify_event(Unit *u) {
|
||||
}
|
||||
|
||||
static int service_enumerate(Manager *m) {
|
||||
|
||||
static const char * const rcnd[] = {
|
||||
"../rc0.d", SPECIAL_RUNLEVEL0_TARGET,
|
||||
"../rc1.d", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"../rc2.d", SPECIAL_RUNLEVEL2_TARGET,
|
||||
"../rc3.d", SPECIAL_RUNLEVEL3_TARGET,
|
||||
"../rc4.d", SPECIAL_RUNLEVEL4_TARGET,
|
||||
"../rc5.d", SPECIAL_RUNLEVEL5_TARGET,
|
||||
"../rc6.d", SPECIAL_RUNLEVEL6_TARGET
|
||||
};
|
||||
|
||||
char **p;
|
||||
unsigned i;
|
||||
DIR *d = NULL;
|
||||
@ -1658,12 +1753,12 @@ static int service_enumerate(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
STRV_FOREACH(p, m->sysvinit_path)
|
||||
for (i = 0; i < ELEMENTSOF(rcnd); i += 2) {
|
||||
for (i = 0; i < ELEMENTSOF(rcnd_table); i += 2) {
|
||||
struct dirent *de;
|
||||
|
||||
free(path);
|
||||
path = NULL;
|
||||
if (asprintf(&path, "%s/%s", *p, rcnd[i]) < 0) {
|
||||
if (asprintf(&path, "%s/%s", *p, rcnd_table[i]) < 0) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
@ -1692,7 +1787,7 @@ static int service_enumerate(Manager *m) {
|
||||
|
||||
free(fpath);
|
||||
fpath = NULL;
|
||||
if (asprintf(&fpath, "%s/%s/%s", *p, rcnd[i], de->d_name) < 0) {
|
||||
if (asprintf(&fpath, "%s/%s/%s", *p, rcnd_table[i], de->d_name) < 0) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
@ -1715,7 +1810,7 @@ static int service_enumerate(Manager *m) {
|
||||
if ((r = manager_load_unit(m, name, &service)) < 0)
|
||||
goto finish;
|
||||
|
||||
if ((r = manager_load_unit(m, rcnd[i+1], &runlevel)) < 0)
|
||||
if ((r = manager_load_unit(m, rcnd_table[i+1], &runlevel)) < 0)
|
||||
goto finish;
|
||||
|
||||
if (de->d_name[0] == 'S') {
|
||||
@ -1724,7 +1819,18 @@ static int service_enumerate(Manager *m) {
|
||||
|
||||
if ((r = unit_add_dependency(runlevel, UNIT_AFTER, service)) < 0)
|
||||
goto finish;
|
||||
} else {
|
||||
|
||||
} else if (de->d_name[0] == 'K' &&
|
||||
(streq(rcnd_table[i+1], SPECIAL_RUNLEVEL0_TARGET) ||
|
||||
streq(rcnd_table[i+1], SPECIAL_RUNLEVEL6_TARGET))) {
|
||||
|
||||
/* We honour K links only for
|
||||
* halt/reboot. For the normal
|
||||
* runlevels we assume the
|
||||
* stop jobs will be
|
||||
* implicitly added by the
|
||||
* core logic. */
|
||||
|
||||
if ((r = unit_add_dependency(runlevel, UNIT_CONFLICTS, service)) < 0)
|
||||
goto finish;
|
||||
|
||||
|
@ -100,6 +100,8 @@ struct Service {
|
||||
pid_t main_pid, control_pid;
|
||||
bool main_pid_known:1;
|
||||
|
||||
bool sysv_has_lsb:1;
|
||||
|
||||
bool failure:1; /* if we shut down, remember why */
|
||||
Watch timer_watch;
|
||||
|
||||
|
59
socket.c
59
socket.c
@ -30,6 +30,8 @@
|
||||
#include "unit.h"
|
||||
#include "socket.h"
|
||||
#include "log.h"
|
||||
#include "load-dropin.h"
|
||||
#include "load-fragment.h"
|
||||
|
||||
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
|
||||
[SOCKET_DEAD] = UNIT_INACTIVE,
|
||||
@ -92,7 +94,7 @@ static void socket_done(Unit *u) {
|
||||
unit_unwatch_timer(u, &s->timer_watch);
|
||||
}
|
||||
|
||||
static int socket_init(Unit *u) {
|
||||
static int socket_init(Unit *u, UnitLoadState *new_state) {
|
||||
Socket *s = SOCKET(u);
|
||||
char *t;
|
||||
int r;
|
||||
@ -109,35 +111,38 @@ static int socket_init(Unit *u) {
|
||||
s->socket_mode = 0666;
|
||||
exec_context_init(&s->exec_context);
|
||||
|
||||
if ((r = unit_load_fragment_and_dropin(u)) <= 0) {
|
||||
if (r == 0)
|
||||
r = -ENOENT;
|
||||
goto fail;
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
return r;
|
||||
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (*new_state == UNIT_LOADED) {
|
||||
|
||||
if (!(t = unit_name_change_suffix(unit_id(u), ".service")))
|
||||
return -ENOMEM;
|
||||
|
||||
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
|
||||
free(t);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_default_cgroup(u)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!(t = unit_name_change_suffix(unit_id(u), ".service"))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
|
||||
free(t);
|
||||
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
|
||||
goto fail;
|
||||
|
||||
/* Add default cgroup */
|
||||
if ((r = unit_add_default_cgroup(u)) < 0)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
socket_done(u);
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* listen_lookup(int type) {
|
||||
|
15
target.c
15
target.c
@ -20,6 +20,7 @@
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "unit.h"
|
||||
#include "target.h"
|
||||
@ -36,18 +37,6 @@ static const char* const state_string_table[_TARGET_STATE_MAX] = {
|
||||
[TARGET_ACTIVE] = "active"
|
||||
};
|
||||
|
||||
static int target_init(Unit *u) {
|
||||
int r;
|
||||
assert(u);
|
||||
|
||||
/* Make sure this config file actually exists */
|
||||
|
||||
if ((r = unit_load_fragment_and_dropin(u)) <= 0)
|
||||
return r < 0 ? r : -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void target_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Target *t = TARGET(u);
|
||||
|
||||
@ -100,7 +89,7 @@ static UnitActiveState target_active_state(Unit *u) {
|
||||
const UnitVTable target_vtable = {
|
||||
.suffix = ".target",
|
||||
|
||||
.init = target_init,
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
|
||||
.dump = target_dump,
|
||||
|
||||
|
15
timer.c
15
timer.c
@ -30,19 +30,6 @@ static void timer_done(Unit *u) {
|
||||
assert(t);
|
||||
}
|
||||
|
||||
static int timer_init(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Make sure this config file actually exists */
|
||||
|
||||
if ((r = unit_load_fragment_and_dropin(u)) <= 0)
|
||||
return r < 0 ? r : -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState timer_active_state(Unit *u) {
|
||||
|
||||
static const UnitActiveState table[_TIMER_STATE_MAX] = {
|
||||
@ -57,7 +44,7 @@ static UnitActiveState timer_active_state(Unit *u) {
|
||||
const UnitVTable timer_vtable = {
|
||||
.suffix = ".timer",
|
||||
|
||||
.init = timer_init,
|
||||
.init = unit_load_fragment_and_dropin,
|
||||
.done = timer_done,
|
||||
|
||||
.active_state = timer_active_state
|
||||
|
246
unit.c
246
unit.c
@ -220,6 +220,16 @@ void unit_add_to_load_queue(Unit *u) {
|
||||
u->meta.in_load_queue = true;
|
||||
}
|
||||
|
||||
void unit_add_to_cleanup_queue(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
if (u->meta.in_cleanup_queue)
|
||||
return;
|
||||
|
||||
LIST_PREPEND(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
|
||||
u->meta.in_cleanup_queue = true;
|
||||
}
|
||||
|
||||
void unit_add_to_dbus_queue(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
@ -274,7 +284,10 @@ void unit_free(Unit *u) {
|
||||
if (u->meta.in_dbus_queue)
|
||||
LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
|
||||
|
||||
if (u->meta.load_state == UNIT_LOADED)
|
||||
if (u->meta.in_cleanup_queue)
|
||||
LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
|
||||
|
||||
if (u->meta.load_state != UNIT_STUB)
|
||||
if (UNIT_VTABLE(u)->done)
|
||||
UNIT_VTABLE(u)->done(u);
|
||||
|
||||
@ -304,51 +317,145 @@ UnitActiveState unit_active_state(Unit *u) {
|
||||
return UNIT_VTABLE(u)->active_state(u);
|
||||
}
|
||||
|
||||
static int ensure_merge(Set **s, Set *other) {
|
||||
static void complete_move(Set **s, Set **other) {
|
||||
assert(s);
|
||||
assert(other);
|
||||
|
||||
if (!other)
|
||||
return 0;
|
||||
if (!*other)
|
||||
return;
|
||||
|
||||
if (*s)
|
||||
return set_merge(*s, other);
|
||||
|
||||
if (!(*s = set_copy(other)))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
set_move(*s, *other);
|
||||
else {
|
||||
*s = *other;
|
||||
*other = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: Does not rollback on failure! Needs to fix special unit
|
||||
* pointers. Needs to merge names and dependencies properly.*/
|
||||
int unit_merge(Unit *u, Unit *other) {
|
||||
static void merge_names(Unit *u, Unit *other) {
|
||||
char *t;
|
||||
Iterator i;
|
||||
|
||||
assert(u);
|
||||
assert(other);
|
||||
|
||||
complete_move(&u->meta.names, &other->meta.names);
|
||||
|
||||
while ((t = set_steal_first(other->meta.names)))
|
||||
free(t);
|
||||
|
||||
set_free(other->meta.names);
|
||||
other->meta.names = NULL;
|
||||
other->meta.id = NULL;
|
||||
|
||||
SET_FOREACH(t, u->meta.names, i)
|
||||
assert_se(hashmap_replace(u->meta.manager->units, t, u) == 0);
|
||||
}
|
||||
|
||||
static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) {
|
||||
Iterator i;
|
||||
Unit *back;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(other);
|
||||
assert(d < _UNIT_DEPENDENCY_MAX);
|
||||
|
||||
SET_FOREACH(back, other->meta.dependencies[d], i) {
|
||||
UnitDependency k;
|
||||
|
||||
for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
|
||||
if ((r = set_remove_and_put(back->meta.dependencies[k], other, u)) < 0) {
|
||||
|
||||
if (r == -EEXIST)
|
||||
set_remove(back->meta.dependencies[k], other);
|
||||
else
|
||||
assert(r == -ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
complete_move(&u->meta.dependencies[d], &other->meta.dependencies[d]);
|
||||
|
||||
set_free(other->meta.dependencies[d]);
|
||||
other->meta.dependencies[d] = NULL;
|
||||
}
|
||||
|
||||
int unit_merge(Unit *u, Unit *other) {
|
||||
UnitDependency d;
|
||||
|
||||
assert(u);
|
||||
assert(other);
|
||||
assert(u->meta.manager == other->meta.manager);
|
||||
|
||||
if (other == u)
|
||||
return 0;
|
||||
|
||||
/* This merges 'other' into 'unit'. FIXME: This does not
|
||||
* rollback on failure. */
|
||||
|
||||
if (u->meta.type != u->meta.type)
|
||||
return -EINVAL;
|
||||
|
||||
if (u->meta.load_state != UNIT_STUB)
|
||||
return -EINVAL;
|
||||
if (other->meta.load_state != UNIT_STUB)
|
||||
return -EEXIST;
|
||||
|
||||
/* Merge names */
|
||||
if ((r = ensure_merge(&u->meta.names, other->meta.names)) < 0)
|
||||
return r;
|
||||
merge_names(u, other);
|
||||
|
||||
/* Merge dependencies */
|
||||
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
|
||||
/* fixme, the inverse mapping is missing */
|
||||
if ((r = ensure_merge(&u->meta.dependencies[d], other->meta.dependencies[d])) < 0)
|
||||
return r;
|
||||
merge_dependencies(u, other, d);
|
||||
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
other->meta.load_state = UNIT_MERGED;
|
||||
other->meta.merged_into = u;
|
||||
|
||||
unit_add_to_cleanup_queue(other);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unit_merge_by_name(Unit *u, const char *name) {
|
||||
Unit *other;
|
||||
|
||||
assert(u);
|
||||
assert(name);
|
||||
|
||||
if (!(other = manager_get_unit(u->meta.manager, name)))
|
||||
return unit_add_name(u, name);
|
||||
|
||||
return unit_merge(u, other);
|
||||
}
|
||||
|
||||
Unit* unit_follow_merge(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
while (u->meta.load_state == UNIT_MERGED)
|
||||
assert_se(u = u->meta.merged_into);
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(c);
|
||||
|
||||
if (c->output != EXEC_OUTPUT_KERNEL && c->output != EXEC_OUTPUT_SYSLOG)
|
||||
return 0;
|
||||
|
||||
/* If syslog or kernel logging is requested, make sure our own
|
||||
* logging daemon is run first. */
|
||||
|
||||
if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0)
|
||||
return r;
|
||||
|
||||
if (u->meta.manager->running_as != MANAGER_SESSION)
|
||||
if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -389,22 +496,18 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%s→ Unit %s:\n"
|
||||
"%s\tDescription: %s\n"
|
||||
"%s\tUnit Load State: %s\n"
|
||||
"%s\tUnit Active State: %s\n"
|
||||
"%s\tRecursive Stop: %s\n"
|
||||
"%s\tStop When Unneeded: %s\n",
|
||||
"%s\tUnit Active State: %s\n",
|
||||
prefix, unit_id(u),
|
||||
prefix, unit_description(u),
|
||||
prefix, unit_load_state_to_string(u->meta.load_state),
|
||||
prefix, unit_active_state_to_string(unit_active_state(u)),
|
||||
prefix, yes_no(u->meta.recursive_stop),
|
||||
prefix, yes_no(u->meta.stop_when_unneeded));
|
||||
|
||||
if (u->meta.fragment_path)
|
||||
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path);
|
||||
prefix, unit_active_state_to_string(unit_active_state(u)));
|
||||
|
||||
SET_FOREACH(t, u->meta.names, i)
|
||||
fprintf(f, "%s\tName: %s\n", prefix, t);
|
||||
|
||||
if (u->meta.fragment_path)
|
||||
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->meta.fragment_path);
|
||||
|
||||
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
|
||||
Unit *other;
|
||||
|
||||
@ -415,12 +518,20 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other));
|
||||
}
|
||||
|
||||
LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings)
|
||||
fprintf(f, "%s\tControlGroup: %s:%s\n",
|
||||
prefix, b->controller, b->path);
|
||||
fprintf(f,
|
||||
"%s\tRecursive Stop: %s\n"
|
||||
"%s\tStop When Unneeded: %s\n",
|
||||
prefix, yes_no(u->meta.recursive_stop),
|
||||
prefix, yes_no(u->meta.stop_when_unneeded));
|
||||
|
||||
if (UNIT_VTABLE(u)->dump)
|
||||
UNIT_VTABLE(u)->dump(u, f, prefix2);
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings)
|
||||
fprintf(f, "%s\tControlGroup: %s:%s\n",
|
||||
prefix, b->controller, b->path);
|
||||
|
||||
if (UNIT_VTABLE(u)->dump)
|
||||
UNIT_VTABLE(u)->dump(u, f, prefix2);
|
||||
}
|
||||
|
||||
if (u->meta.job)
|
||||
job_dump(u->meta.job, f, prefix2);
|
||||
@ -429,26 +540,55 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
}
|
||||
|
||||
/* Common implementation for multiple backends */
|
||||
int unit_load_fragment_and_dropin(Unit *u) {
|
||||
int r, ret;
|
||||
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
|
||||
|
||||
/* Load a .socket file */
|
||||
if ((r = unit_load_fragment(u)) < 0)
|
||||
/* Load a .service file */
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
ret = r > 0;
|
||||
if (*new_state == UNIT_STUB)
|
||||
return -ENOENT;
|
||||
|
||||
/* Load drop-in directory data */
|
||||
if ((r = unit_load_dropin(u)) < 0)
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Common implementation for multiple backends */
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(new_state);
|
||||
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
|
||||
|
||||
/* Same as unit_load_fragment_and_dropin(), but whether
|
||||
* something can be loaded or not doesn't matter. */
|
||||
|
||||
/* Load a .service file */
|
||||
if ((r = unit_load_fragment(u, new_state)) < 0)
|
||||
return r;
|
||||
|
||||
if (*new_state == UNIT_STUB)
|
||||
*new_state = UNIT_LOADED;
|
||||
|
||||
/* Load drop-in directory data */
|
||||
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unit_load(Unit *u) {
|
||||
int r;
|
||||
UnitLoadState res;
|
||||
|
||||
assert(u);
|
||||
|
||||
@ -460,17 +600,30 @@ int unit_load(Unit *u) {
|
||||
if (u->meta.load_state != UNIT_STUB)
|
||||
return 0;
|
||||
|
||||
if (UNIT_VTABLE(u)->init)
|
||||
if ((r = UNIT_VTABLE(u)->init(u)) < 0)
|
||||
if (UNIT_VTABLE(u)->init) {
|
||||
res = UNIT_STUB;
|
||||
if ((r = UNIT_VTABLE(u)->init(u, &res)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (res == UNIT_STUB) {
|
||||
r = -ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->meta.load_state = res;
|
||||
assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into);
|
||||
|
||||
unit_add_to_dbus_queue(unit_follow_merge(u));
|
||||
|
||||
u->meta.load_state = UNIT_LOADED;
|
||||
unit_add_to_dbus_queue(u);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
u->meta.load_state = UNIT_FAILED;
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
log_error("Failed to load configuration for %s: %s", unit_id(u), strerror(-r));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1227,7 +1380,8 @@ DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
|
||||
static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
|
||||
[UNIT_STUB] = "stub",
|
||||
[UNIT_LOADED] = "loaded",
|
||||
[UNIT_FAILED] = "failed"
|
||||
[UNIT_FAILED] = "failed",
|
||||
[UNIT_MERGED] = "merged"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
|
||||
|
18
unit.h
18
unit.h
@ -60,6 +60,7 @@ enum UnitLoadState {
|
||||
UNIT_STUB,
|
||||
UNIT_LOADED,
|
||||
UNIT_FAILED,
|
||||
UNIT_MERGED,
|
||||
_UNIT_LOAD_STATE_MAX,
|
||||
_UNIT_LOAD_STATE_INVALID = -1
|
||||
};
|
||||
@ -116,8 +117,10 @@ enum UnitDependency {
|
||||
|
||||
struct Meta {
|
||||
Manager *manager;
|
||||
|
||||
UnitType type;
|
||||
UnitLoadState load_state;
|
||||
Unit *merged_into;
|
||||
|
||||
char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
|
||||
|
||||
@ -133,6 +136,7 @@ struct Meta {
|
||||
|
||||
bool in_load_queue:1;
|
||||
bool in_dbus_queue:1;
|
||||
bool in_cleanup_queue:1;
|
||||
bool sent_dbus_new_signal:1;
|
||||
|
||||
/* If we go down, pull down everything that depends on us, too */
|
||||
@ -155,6 +159,9 @@ struct Meta {
|
||||
|
||||
/* D-Bus queue */
|
||||
LIST_FIELDS(Meta, dbus_queue);
|
||||
|
||||
/* Cleanup queue */
|
||||
LIST_FIELDS(Meta, cleanup_queue);
|
||||
};
|
||||
|
||||
#include "service.h"
|
||||
@ -181,7 +188,7 @@ union Unit {
|
||||
struct UnitVTable {
|
||||
const char *suffix;
|
||||
|
||||
int (*init)(Unit *u);
|
||||
int (*init)(Unit *u, UnitLoadState *new_state);
|
||||
void (*done)(Unit *u);
|
||||
int (*coldplug)(Unit *u);
|
||||
|
||||
@ -251,6 +258,8 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other);
|
||||
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name);
|
||||
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name);
|
||||
|
||||
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
|
||||
|
||||
int unit_add_cgroup(Unit *u, CGroupBonding *b);
|
||||
int unit_add_cgroup_from_text(Unit *u, const char *name);
|
||||
int unit_add_default_cgroup(Unit *u);
|
||||
@ -261,10 +270,15 @@ int unit_set_description(Unit *u, const char *description);
|
||||
|
||||
void unit_add_to_load_queue(Unit *u);
|
||||
void unit_add_to_dbus_queue(Unit *u);
|
||||
void unit_add_to_cleanup_queue(Unit *u);
|
||||
|
||||
int unit_merge(Unit *u, Unit *other);
|
||||
int unit_merge_by_name(Unit *u, const char *other);
|
||||
|
||||
int unit_load_fragment_and_dropin(Unit *u);
|
||||
Unit *unit_follow_merge(Unit *u);
|
||||
|
||||
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state);
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state);
|
||||
int unit_load(Unit *unit);
|
||||
|
||||
const char* unit_id(Unit *u);
|
||||
|
Loading…
Reference in New Issue
Block a user