/* * Note: If anyone wants to set some triggers-pending, they must also * set status appropriately, or we will undo it. That is, it is legal * to call this when pkg->status and pkg->trigpend_head disagree and * in that case pkg->status takes precedence and pkg->trigpend_head * will be adjusted. */ void modstatdb_note(struct pkginfo *pkg) { struct trigaw *ta; onerr_abort++; /* Clear pending triggers here so that only code that sets the status * to interesting (for triggers) values has to care about triggers. */ if (pkg->status != PKG_STAT_TRIGGERSPENDING && pkg->status != PKG_STAT_TRIGGERSAWAITED) pkg->trigpend_head = NULL; if (pkg->status <= PKG_STAT_CONFIGFILES) { for (ta = pkg->trigaw.head; ta; ta = ta->sameaw.next) ta->aw = NULL; pkg->trigaw.head = pkg->trigaw.tail = NULL; } log_message("status %s %s %s", pkg_status_name(pkg), pkg_name(pkg, pnaw_always), versiondescribe(&pkg->installed.version, vdew_nonambig)); statusfd_send("status: %s: %s", pkg_name(pkg, pnaw_nonambig), pkg_status_name(pkg)); if (cstatus >= msdbrw_write) modstatdb_note_core(pkg); if (!pkg->trigpend_head && pkg->othertrigaw_head) { /* Automatically remove us from other packages' Triggers-Awaited. * We do this last because we want to maximize our chances of * successfully recording the status of the package we were * pointed at by our caller, although there is some risk of * leaving us in a slightly odd situation which is cleared up * by the trigger handling logic in deppossi_ok_found. */ trig_clear_awaiters(pkg); } onerr_abort--; }
/* * cstatus might be msdbrw_readonly if we're in --no-act mode, in which * case we don't write out all of the interest files etc. but we do * invent all of the activations for our own benefit. */ static void trig_transitional_activate(enum modstatdb_rw cstatus) { struct pkgiterator *iter; struct pkginfo *pkg; iter = pkg_db_iter_new(); while ((pkg = pkg_db_iter_next_pkg(iter))) { if (pkg->status <= PKG_STAT_HALFINSTALLED) continue; debug(dbg_triggersdetail, "trig_transitional_activate %s %s", pkg_name(pkg, pnaw_always), pkg_status_name(pkg)); pkg->trigpend_head = NULL; trig_parse_ci(pkg_infodb_get_file(pkg, &pkg->installed, TRIGGERSCIFILE), cstatus >= msdbrw_write ? transitional_interest_callback : transitional_interest_callback_ro, NULL, pkg, &pkg->installed); /* Ensure we're not creating incoherent data that can't * be written down. This should never happen in theory but * can happen if you restore an old status file that is * not in sync with the infodb files. */ if (pkg->status < PKG_STAT_TRIGGERSAWAITED) continue; if (pkg->trigaw.head) pkg_set_status(pkg, PKG_STAT_TRIGGERSAWAITED); else if (pkg->trigpend_head) pkg_set_status(pkg, PKG_STAT_TRIGGERSPENDING); else pkg_set_status(pkg, PKG_STAT_INSTALLED); } pkg_db_iter_free(iter); if (cstatus >= msdbrw_write) { modstatdb_checkpoint(); trig_file_interests_save(); } }
/* * Return values: * 0: cannot be satisfied. * 1: defer: may be satisfied later, when other packages are better or * at higher dependtry due to --force * will set *fixbytrig to package whose trigger processing would help * if applicable (and leave it alone otherwise). * 2: not satisfied but forcing * (*interestingwarnings >= 0 on exit? caller is to print oemsgs). * 3: satisfied now. */ static enum found_status deppossi_ok_found(struct pkginfo *possdependee, struct pkginfo *requiredby, struct pkginfo *removing, struct deppossi *provider, struct pkginfo **fixbytrig, bool *matched, struct deppossi *checkversion, int *interestingwarnings, struct varbuf *oemsgs) { enum found_status thisf; if (ignore_depends(possdependee)) { debug(dbg_depcondetail," ignoring depended package so ok and found"); return FOUND_OK; } thisf = FOUND_NONE; if (possdependee == removing) { if (provider) { varbuf_printf(oemsgs, _(" Package %s which provides %s is to be removed.\n"), pkg_name(possdependee, pnaw_nonambig), provider->ed->name); } else { varbuf_printf(oemsgs, _(" Package %s is to be removed.\n"), pkg_name(possdependee, pnaw_nonambig)); } *matched = true; debug(dbg_depcondetail," removing possdependee, returning %d",thisf); return thisf; } switch (possdependee->status) { case PKG_STAT_UNPACKED: case PKG_STAT_HALFCONFIGURED: case PKG_STAT_TRIGGERSAWAITED: case PKG_STAT_TRIGGERSPENDING: case PKG_STAT_INSTALLED: if (checkversion) { if (provider) { debug(dbg_depcondetail, " checking package %s provided by pkg %s", checkversion->ed->name, pkg_name(possdependee, pnaw_always)); if (!pkg_virtual_deppossi_satisfied(checkversion, provider)) { varbuf_printf(oemsgs, _(" Version of %s on system, provided by %s, is %s.\n"), checkversion->ed->name, pkg_name(possdependee, pnaw_always), versiondescribe(&provider->version, vdew_nonambig)); if (in_force(FORCE_DEPENDS_VERSION)) thisf = found_forced_on(DEPEND_TRY_FORCE_DEPENDS_VERSION); debug(dbg_depcondetail, " bad version"); goto unsuitable; } } else { debug(dbg_depcondetail, " checking non-provided pkg %s", pkg_name(possdependee, pnaw_always)); if (!versionsatisfied(&possdependee->installed, checkversion)) { varbuf_printf(oemsgs, _(" Version of %s on system is %s.\n"), pkg_name(possdependee, pnaw_nonambig), versiondescribe(&possdependee->installed.version, vdew_nonambig)); if (in_force(FORCE_DEPENDS_VERSION)) thisf = found_forced_on(DEPEND_TRY_FORCE_DEPENDS_VERSION); debug(dbg_depcondetail, " bad version"); goto unsuitable; } } } if (possdependee->status == PKG_STAT_INSTALLED || possdependee->status == PKG_STAT_TRIGGERSPENDING) { debug(dbg_depcondetail," is installed, ok and found"); return FOUND_OK; } if (possdependee->status == PKG_STAT_TRIGGERSAWAITED) { if (possdependee->trigaw.head == NULL) internerr("package %s in state %s, has no awaited triggers", pkg_name(possdependee, pnaw_always), pkg_status_name(possdependee)); if (removing || !(f_triggers || (possdependee->clientdata && possdependee->clientdata->istobe == PKG_ISTOBE_INSTALLNEW))) { if (provider) { varbuf_printf(oemsgs, _(" Package %s which provides %s awaits trigger processing.\n"), pkg_name(possdependee, pnaw_nonambig), provider->ed->name); } else { varbuf_printf(oemsgs, _(" Package %s awaits trigger processing.\n"), pkg_name(possdependee, pnaw_nonambig)); } debug(dbg_depcondetail, " triggers-awaited, no fixbytrig"); goto unsuitable; } /* We don't check the status of trigaw.head->pend here, just in case * we get into the pathological situation where Triggers-Awaited but * the named package doesn't actually have any pending triggers. In * that case we queue the non-pending package for trigger processing * anyway, and that trigger processing will be a noop except for * sorting out all of the packages which name it in Triggers-Awaited. * * (This situation can only arise if modstatdb_note success in * clearing the triggers-pending status of the pending package * but then fails to go on to update the awaiters.) */ *fixbytrig = possdependee->trigaw.head->pend; debug(dbg_depcondetail, " triggers-awaited, fixbytrig '%s', defer", pkg_name(*fixbytrig, pnaw_always)); return FOUND_DEFER; } if (possdependee->clientdata && possdependee->clientdata->istobe == PKG_ISTOBE_INSTALLNEW) { debug(dbg_depcondetail," unpacked/halfconfigured, defer"); return FOUND_DEFER; } else if (!removing && in_force(FORCE_CONFIGURE_ANY) && !skip_due_to_hold(possdependee) && !(possdependee->status == PKG_STAT_HALFCONFIGURED)) { notice(_("also configuring '%s' (required by '%s')"), pkg_name(possdependee, pnaw_nonambig), pkg_name(requiredby, pnaw_nonambig)); enqueue_package(possdependee); sincenothing = 0; return FOUND_DEFER; } else { if (provider) { varbuf_printf(oemsgs, _(" Package %s which provides %s is not configured yet.\n"), pkg_name(possdependee, pnaw_nonambig), provider->ed->name); } else { varbuf_printf(oemsgs, _(" Package %s is not configured yet.\n"), pkg_name(possdependee, pnaw_nonambig)); } debug(dbg_depcondetail, " not configured/able"); goto unsuitable; } default: if (provider) { varbuf_printf(oemsgs, _(" Package %s which provides %s is not installed.\n"), pkg_name(possdependee, pnaw_nonambig), provider->ed->name); } else { varbuf_printf(oemsgs, _(" Package %s is not installed.\n"), pkg_name(possdependee, pnaw_nonambig)); } debug(dbg_depcondetail, " not installed"); goto unsuitable; } unsuitable: debug(dbg_depcondetail, " returning %d", thisf); (*interestingwarnings)++; return thisf; }
void process_queue(void) { struct pkg_list *rundown; struct pkginfo *volatile pkg; volatile enum action action_todo; jmp_buf ejbuf; enum pkg_istobe istobe = PKG_ISTOBE_NORMAL; if (abort_processing) return; clear_istobes(); switch (cipaction->arg_int) { case act_triggers: case act_configure: case act_install: istobe = PKG_ISTOBE_INSTALLNEW; break; case act_remove: case act_purge: istobe = PKG_ISTOBE_REMOVE; break; default: internerr("unknown action '%d'", cipaction->arg_int); } for (rundown = queue.head; rundown; rundown = rundown->next) { ensure_package_clientdata(rundown->pkg); /* We have processed this package more than once. There are no duplicates * as we make sure of that when enqueuing them. */ if (rundown->pkg->clientdata->cmdline_seen > 1) { switch (cipaction->arg_int) { case act_triggers: case act_configure: case act_remove: case act_purge: printf(_("Package %s listed more than once, only processing once.\n"), pkg_name(rundown->pkg, pnaw_nonambig)); break; case act_install: printf(_("More than one copy of package %s has been unpacked\n" " in this run ! Only configuring it once.\n"), pkg_name(rundown->pkg, pnaw_nonambig)); break; default: internerr("unknown action '%d'", cipaction->arg_int); } } rundown->pkg->clientdata->istobe = istobe; } while (!pkg_queue_is_empty(&queue)) { pkg = pkg_queue_pop(&queue); if (!pkg) continue; /* Duplicate, which we removed earlier. */ ensure_package_clientdata(pkg); pkg->clientdata->enqueued = false; action_todo = cipaction->arg_int; if (sincenothing++ > queue.length * 3 + 2) { /* Make sure that even if we have exceeded the queue since not having * made any progress, we are not getting stuck trying to progress by * trigger processing, w/o jumping into the next dependtry. */ dependtry++; sincenothing = 0; if (dependtry >= DEPEND_TRY_LAST) internerr("exceeded dependtry %d (sincenothing=%d; queue.length=%d)", dependtry, sincenothing, queue.length); } else if (sincenothing > queue.length * 2 + 2) { if (dependtry >= DEPEND_TRY_TRIGGERS && progress_bytrigproc && progress_bytrigproc->trigpend_head) { enqueue_package(pkg); pkg = progress_bytrigproc; progress_bytrigproc = NULL; action_todo = act_configure; } else { dependtry++; sincenothing = 0; if (dependtry >= DEPEND_TRY_LAST) internerr("exceeded dependtry %d (sincenothing=%d, queue.length=%d)", dependtry, sincenothing, queue.length); } } debug(dbg_general, "process queue pkg %s queue.len %d progress %d, try %d", pkg_name(pkg, pnaw_always), queue.length, sincenothing, dependtry); if (pkg->status > PKG_STAT_INSTALLED) internerr("package %s status %d is out-of-bounds", pkg_name(pkg, pnaw_always), pkg->status); if (setjmp(ejbuf)) { /* Give up on it from the point of view of other packages, i.e. reset * istobe. */ pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; pop_error_context(ehflag_bombout); if (abort_processing) return; continue; } push_error_context_jump(&ejbuf, print_error_perpackage, pkg_name(pkg, pnaw_nonambig)); switch (action_todo) { case act_triggers: if (!pkg->trigpend_head) ohshit(_("package %.250s is not ready for trigger processing\n" " (current status '%.250s' with no pending triggers)"), pkg_name(pkg, pnaw_nonambig), pkg_status_name(pkg)); /* Fall through. */ case act_install: /* Don't try to configure pkgs that we've just disappeared. */ if (pkg->status == PKG_STAT_NOTINSTALLED) break; /* Fall through. */ case act_configure: /* Do whatever is most needed. */ if (pkg->trigpend_head) trigproc(pkg, TRIGPROC_TRY_QUEUED); else deferred_configure(pkg); break; case act_remove: case act_purge: deferred_remove(pkg); break; default: internerr("unknown action '%d'", cipaction->arg_int); } m_output(stdout, _("<standard output>")); m_output(stderr, _("<standard error>")); pop_error_context(ehflag_normaltidy); } if (queue.length) internerr("finished package processing with non-empty queue length %d", queue.length); }