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
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;
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);
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);
* 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) {
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);
/* 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) {
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;
}
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);
* 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)) {
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;
}
/* 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;
}
* 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;
}
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)
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);
* 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;
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);
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);
}
}
}
manager_dispatch_load_queue(m);
- *_ret = ret;
+ *_ret = unit_follow_merge(ret);
return 0;
}
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++;
}
assert(m);
- for (;;) {
+ do {
struct epoll_event event;
int n;
sleep(1);
}
+ if (manager_dispatch_cleanup_queue(m) > 0)
+ continue;
+
if (manager_dispatch_load_queue(m) > 0)
continue;
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) {