summaryrefslogtreecommitdiff
path: root/apt-pkg
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2016-11-12 11:32:13 +0100
committerDavid Kalnischkies <david@kalnischkies.de>2016-11-23 16:37:39 +0100
commitdabe9e2482180ada77d2adda2b3c03db22059fb8 (patch)
treeb9d61ac92c520c246b615c276bb277c9e97ad627 /apt-pkg
parent066d4a5bab628ef8220971bb5763ff8f3a13de07 (diff)
correct cross & disappear progress detection
Given that we use the progress information to skip over actions dpkg has already done like not purging a package which was already removed and had no config files or not acting on disappeared packages and such it is important that apt and dpkg agree on which states the package has to pass through. To ensure that we keep tabs on this in the future a warning is added at the end if apt hasn't seen all the action it was supposed to see. I can't wait for the first bugreporters to wonder about this…
Diffstat (limited to 'apt-pkg')
-rw-r--r--apt-pkg/deb/dpkgpm.cc93
1 files changed, 58 insertions, 35 deletions
diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc
index 7c41e02b4..70e7592a7 100644
--- a/apt-pkg/deb/dpkgpm.cc
+++ b/apt-pkg/deb/dpkgpm.cc
@@ -143,6 +143,8 @@ namespace
// of each array is the key, entry 1 is the value.
const std::pair<const char *, const char *> PackageProcessingOps[] = {
std::make_pair("install", N_("Installing %s")),
+ // we don't care for the difference
+ std::make_pair("upgrade", N_("Installing %s")),
std::make_pair("configure", N_("Configuring %s")),
std::make_pair("remove", N_("Removing %s")),
std::make_pair("purge", N_("Completely removing %s")),
@@ -615,9 +617,6 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
{
pkgname = APT::String::Strip(list[2]);
action = APT::String::Strip(list[1]);
- // we don't care for the difference (as dpkg doesn't really either)
- if (action == "upgrade")
- action = "install";
}
// "status" has the form: "status: pkg: state"
// with state in ["half-installed", "unpacked", "half-configured",
@@ -690,10 +689,7 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
// 'processing: action: pkg'
if(prefix == "processing")
{
- const std::pair<const char *, const char *> * const iter =
- std::find_if(PackageProcessingOpsBegin,
- PackageProcessingOpsEnd,
- MatchProcessingOp(action.c_str()));
+ auto const iter = std::find_if(PackageProcessingOpsBegin, PackageProcessingOpsEnd, MatchProcessingOp(action.c_str()));
if(iter == PackageProcessingOpsEnd)
{
if (Debug == true)
@@ -709,6 +705,37 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
// short pkgnames?
if (action == "disappear")
handleDisappearAction(pkgname);
+ else if (action == "upgrade")
+ {
+ // in a crossgrade what looked like a remove first is really an unpack over it
+ auto const Pkg = Cache.FindPkg(pkgname);
+ if (likely(Pkg.end() == false) && Cache[Pkg].Delete())
+ {
+ auto const Grp = Pkg.Group();
+ if (likely(Grp.end() == false))
+ {
+ for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
+ if(Cache[P].Install())
+ {
+ auto && Ops = PackageOps[P.FullName()];
+ auto const unpackOp = std::find_if(Ops.cbegin(), Ops.cend(), [](DpkgState const &s) { return strcmp(s.state, "unpacked") == 0; });
+ if (unpackOp != Ops.cend())
+ {
+ // skip ahead in the crossgraded packages
+ auto const skipped = std::distance(Ops.cbegin(), unpackOp);
+ PackagesDone += skipped;
+ PackageOpsDone[P.FullName()] += skipped;
+ // finish the crossremoved package
+ auto const ROps = PackageOps[Pkg.FullName()].size();
+ auto && ROpsDone = PackageOpsDone[Pkg.FullName()];
+ PackagesDone += ROps - ROpsDone;
+ ROpsDone = ROps;
+ break;
+ }
+ }
+ }
+ }
+ }
return;
}
@@ -752,34 +779,6 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
strprintf(msg, translation, i18n_pkgname.c_str());
d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
}
- else if (action == "unpacked" && strcmp(next_action, "config-files") == 0)
- {
- // in a crossgrade what looked like a remove first is really an unpack over it
- ++PackageOpsDone[pkgname];
- ++PackagesDone;
-
- auto const Pkg = Cache.FindPkg(pkgname);
- if (likely(Pkg.end() == false))
- {
- auto const Grp = Pkg.Group();
- if (likely(Grp.end() == false))
- {
- for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
- if(Cache[P].Install())
- {
- auto && Ops = PackageOps[P.FullName()];
- auto const unpackOp = std::find_if(Ops.cbegin(), Ops.cend(), [](DpkgState const &s) { return strcmp(s.state, "unpacked") == 0; });
- if (unpackOp != Ops.cend())
- {
- auto const skipped = std::distance(Ops.cbegin(), unpackOp);
- PackagesDone += skipped;
- PackageOpsDone[P.FullName()] += skipped;
- break;
- }
- }
- }
- }
- }
}
}
else if (action == "triggers-pending")
@@ -803,6 +802,12 @@ void pkgDPkgPM::handleDisappearAction(string const &pkgname)
if (unlikely(Pkg.end() == true))
return;
+ // a disappeared package has no further actions
+ auto const ROps = PackageOps[Pkg.FullName()].size();
+ auto && ROpsDone = PackageOpsDone[Pkg.FullName()];
+ PackagesDone += ROps - ROpsDone;
+ ROpsDone = ROps;
+
// record the package name for display and stuff later
disappearedPkgs.insert(Pkg.FullName(true));
@@ -1994,6 +1999,24 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
if (noopDPkgInvocation == false)
{
+ if (d->dpkg_error.empty() && (PackagesDone + 1) != PackagesTotal)
+ {
+ std::string pkglist;
+ for (auto const &PO: PackageOps)
+ if (PO.second.size() != PackageOpsDone[PO.first])
+ {
+ if (pkglist.empty() == false)
+ pkglist.append(" ");
+ pkglist.append(PO.first);
+ }
+ /* who cares about correct progress? As we depend on it for skipping actions
+ our parsing should be correct. People will no doubt be confused if they see
+ this message, but the dpkg warning about unknown packages isn't much better
+ from a user POV and combined we might have a chance to figure out what is wrong */
+ _error->Warning("APT had planned for dpkg to do more than it reported back (%u vs %u).\n"
+ "Affected packages: %s", PackagesDone, PackagesTotal, pkglist.c_str());
+ }
+
std::string const oldpkgcache = _config->FindFile("Dir::cache::pkgcache");
if (oldpkgcache.empty() == false && RealFileExists(oldpkgcache) == true &&
RemoveFile("pkgDPkgPM::Go", oldpkgcache))