/* * Returns package we're to give up on. */ static struct pkginfo * check_trigger_cycle(struct pkginfo *processing_now) { struct trigcyclenode *tcn; struct trigcycleperpkg *tcpp, *tortoise_pkg; struct trigpend *tortoise_trig; struct pkgiterator *iter; struct pkginfo *pkg, *giveup; const char *sep; debug(dbg_triggers, "check_triggers_cycle pnow=%s", pkg_name(processing_now, pnaw_always)); tcn = nfmalloc(sizeof(*tcn)); tcn->pkgs = NULL; tcn->then_processed = processing_now; iter = pkg_db_iter_new(); while ((pkg = pkg_db_iter_next_pkg(iter))) { if (!pkg->trigpend_head) continue; tcpp = nfmalloc(sizeof(*tcpp)); tcpp->pkg = pkg; tcpp->then_trigs = pkg->trigpend_head; tcpp->next = tcn->pkgs; tcn->pkgs = tcpp; } pkg_db_iter_free(iter); if (!hare) { debug(dbg_triggersdetail, "check_triggers_cycle pnow=%s first", pkg_name(processing_now, pnaw_always)); tcn->next = NULL; hare = tortoise = tcn; return NULL; } tcn->next = NULL; hare->next = tcn; hare = tcn; if (tortoise_advance) tortoise = tortoise->next; tortoise_advance = !tortoise_advance; /* Now we compare hare to tortoise. * We want to find a trigger pending in tortoise which is not in hare * if we find such a thing we have proved that hare isn't a superset * of tortoise and so that we haven't found a loop (yet). */ for (tortoise_pkg = tortoise->pkgs; tortoise_pkg; tortoise_pkg = tortoise_pkg->next) { if (tortoise_not_in_hare(processing_now, tortoise_pkg)) return NULL; } /* Oh dear. hare is a superset of tortoise. We are making no * progress. */ notice(_("cycle found while processing triggers:\n" " chain of packages whose triggers are or may be responsible:")); sep = " "; for (tcn = tortoise; tcn; tcn = tcn->next) { fprintf(stderr, "%s%s", sep, pkg_name(tcn->then_processed, pnaw_nonambig)); sep = " -> "; } fprintf(stderr, _("\n" " packages' pending triggers which are" " or may be unresolvable:\n")); for (tortoise_pkg = tortoise->pkgs; tortoise_pkg; tortoise_pkg = tortoise_pkg->next) { fprintf(stderr, " %s", pkg_name(tortoise_pkg->pkg, pnaw_nonambig)); sep = ": "; for (tortoise_trig = tortoise_pkg->then_trigs; tortoise_trig; tortoise_trig = tortoise_trig->next) { fprintf(stderr, "%s%s", sep, tortoise_trig->name); } fprintf(stderr, "\n"); } /* We give up on the _earliest_ package involved. */ giveup = tortoise->pkgs->pkg; debug(dbg_triggers, "check_triggers_cycle pnow=%s giveup=%s", pkg_name(processing_now, pnaw_always), pkg_name(giveup, pnaw_always)); assert(giveup->status == PKG_STAT_TRIGGERSAWAITED || giveup->status == PKG_STAT_TRIGGERSPENDING); pkg_set_status(giveup, PKG_STAT_HALFCONFIGURED); modstatdb_note(giveup); print_error_perpackage(_("triggers looping, abandoned"), pkg_name(giveup, pnaw_nonambig)); return giveup; }
/* * Returns package we're to give up on. */ static struct pkginfo * check_trigger_cycle(struct pkginfo *processing_now) { struct trigcyclenode *tcn; struct trigcycleperpkg *tcpp, *tortoise_pkg; struct trigpend *hare_trig, *tortoise_trig; struct pkgiterator *it; struct pkginfo *pkg, *giveup; const char *sep; debug(dbg_triggers, "check_triggers_cycle pnow=%s", pkg_describe(processing_now, pdo_foreign)); tcn = nfmalloc(sizeof(*tcn)); tcn->pkgs = NULL; tcn->then_processed = processing_now; it = pkg_db_iter_new(); while ((pkg = pkg_db_iter_next_pkg(it))) { if (!pkg->trigpend_head) continue; tcpp = nfmalloc(sizeof(*tcpp)); tcpp->pkg = pkg; tcpp->then_trigs = pkg->trigpend_head; tcpp->next = tcn->pkgs; tcn->pkgs = tcpp; } pkg_db_iter_free(it); if (!hare) { debug(dbg_triggersdetail, "check_triggers_cycle pnow=%s first", pkg_describe(processing_now, pdo_foreign)); tcn->next = NULL; hare = tortoise = tcn; return NULL; } tcn->next = NULL; hare->next = tcn; hare = tcn; if (tortoise_advance) tortoise = tortoise->next; tortoise_advance = !tortoise_advance; /* Now we compare hare to tortoise. * We want to find a trigger pending in tortoise which is not in hare * if we find such a thing we have proved that hare isn't a superset * of tortoise and so that we haven't found a loop (yet). */ for (tortoise_pkg = tortoise->pkgs; tortoise_pkg; tortoise_pkg = tortoise_pkg->next) { const char *pnow_name, *tortoise_name; pnow_name = pkg_describe(processing_now, pdo_foreign); tortoise_name = pkg_describe(tortoise_pkg->pkg, pdo_foreign); debug(dbg_triggersdetail, "check_triggers_cycle pnow=%s tortoise=%s", pnow_name, tortoise_name); for (tortoise_trig = tortoise_pkg->then_trigs; tortoise_trig; tortoise_trig = tortoise_trig->next) { debug(dbg_triggersdetail, "check_triggers_cycle pnow=%s tortoise=%s" " tortoisetrig=%s", pnow_name, tortoise_name, tortoise_trig->name); /* hare is now so we can just look up in the actual * data. */ for (hare_trig = tortoise_pkg->pkg->trigpend_head; hare_trig; hare_trig = hare_trig->next) { debug(dbg_triggersstupid, "check_triggers_cycle pnow=%s tortoise=%s" " tortoisetrig=%s haretrig=%s", pnow_name, tortoise_name, tortoise_trig->name, hare_trig->name); if (!strcmp(hare_trig->name, tortoise_trig->name)) goto found_in_hare; } /* Not found in hare, yay! */ debug(dbg_triggersdetail, "check_triggers_cycle pnow=%s tortoise=%s OK", pnow_name, tortoise_name); return NULL; found_in_hare:; } } /* Oh dear. hare is a superset of tortoise. We are making no * progress. */ fprintf(stderr, _("%s: cycle found while processing triggers:\n chain of" " packages whose triggers are or may be responsible:\n"), dpkg_get_progname()); sep = " "; for (tcn = tortoise; tcn; tcn = tcn->next) { fprintf(stderr, "%s%s", sep, pkg_describe(tcn->then_processed, pdo_foreign)); sep = " -> "; } fprintf(stderr, _("\n" " packages' pending triggers which are" " or may be unresolvable:\n")); for (tortoise_pkg = tortoise->pkgs; tortoise_pkg; tortoise_pkg = tortoise_pkg->next) { fprintf(stderr, " %s", pkg_describe(tortoise_pkg->pkg, pdo_foreign)); sep = ": "; for (tortoise_trig = tortoise_pkg->then_trigs; tortoise_trig; tortoise_trig = tortoise_trig->next) { fprintf(stderr, "%s%s", sep, tortoise_trig->name); } fprintf(stderr, "\n"); } /* We give up on the _earliest_ package involved. */ giveup = tortoise->pkgs->pkg; debug(dbg_triggers, "check_triggers_cycle pnow=%s giveup=%p", pkg_describe(processing_now, pdo_foreign), pkg_describe(giveup, pdo_foreign)); assert(giveup->status == stat_triggersawaited || giveup->status == stat_triggerspending); giveup->status = stat_halfconfigured; modstatdb_note(giveup); print_error_perpackage(_("triggers looping, abandoned"), pkg_describe(giveup, pdo_foreign)); return giveup; }