Commit 23a177ef authored by Lennart Poettering's avatar Lennart Poettering
Browse files

rework merging/loading logic

parent 5af98f82
......@@ -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;
}
......
......@@ -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,
......
......@@ -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);
......
......@@ -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;
}
/* Now, parse the file contents */
r = config_parse(filename, f, sections, items, u);
if (r < 0)
merged = u;
if ((r = merge_by_names(&merged, symlink_names, id)) < 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);
if (merged != u) {
*new_state = UNIT_MERGED;
r = 0;
goto finish;
}
/* Now, parse the file contents */
if ((r = config_parse(filename, f, sections, items, u)) < 0)
goto finish;
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) {
if ((r = load_from_path(u, u->meta.fragment_path, new_state)) < 0)
return r;
if (u->meta.fragment_path)
r = load_from_path(u, u->meta.fragment_path);
else {
} 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)
break;
}
if (*new_state == UNIT_STUB)
SET_FOREACH(t, u->meta.names, i) {
if (r >= 0) {
ExecContext *c;
if (unit_id(u) == t)
continue;
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 ((r = load_from_path(u, t, new_state)) < 0)
return r;
if (u->meta.manager->running_as != MANAGER_SESSION)
if ((k = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0)
return k;
}
if (*new_state != UNIT_STUB)
break;
}
}
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
......@@ -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);
......
......@@ -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,
......