summaryrefslogtreecommitdiff
path: root/apt-pkg/deb/dpkgpm.cc
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2016-11-23 17:32:20 +0100
committerDavid Kalnischkies <david@kalnischkies.de>2016-11-24 00:21:35 +0100
commit4b10240cca0dc0a4e82e42959545d2ae7e622d29 (patch)
treeed7b12303674629b2ee2509bed21920666027d40 /apt-pkg/deb/dpkgpm.cc
parentdabe9e2482180ada77d2adda2b3c03db22059fb8 (diff)
improve arch-unqualified dpkg-progress parsing
Our old idea was to look for the first package which would be "touched" and take this as the package dpkg is talking about, but that is incorrect in complicated situations like a package upgraded to/from multiple M-A:same siblings installed. As we us the progress report to decide what is still needed we have to be reasonabily right about the package dpkg is talking about, so we jump to quite a few loops to get it.
Diffstat (limited to 'apt-pkg/deb/dpkgpm.cc')
-rw-r--r--apt-pkg/deb/dpkgpm.cc216
1 files changed, 154 insertions, 62 deletions
diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc
index 70e7592a7..abf91fe2a 100644
--- a/apt-pkg/deb/dpkgpm.cc
+++ b/apt-pkg/deb/dpkgpm.cc
@@ -619,63 +619,152 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
action = APT::String::Strip(list[1]);
}
// "status" has the form: "status: pkg: state"
- // with state in ["half-installed", "unpacked", "half-configured",
+ // with state in ["half-installed", "unpacked", "half-configured",
// "installed", "config-files", "not-installed"]
else if (prefix == "status")
{
pkgname = APT::String::Strip(list[1]);
action = APT::String::Strip(list[2]);
- } else {
- if (Debug == true)
- std::clog << "unknown prefix '" << prefix << "'" << std::endl;
- return;
- }
+ /* handle the special cases first:
- /* handle the special cases first:
-
- errors look like this:
- 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
- and conffile-prompt like this
- 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
- */
- if (prefix == "status")
- {
+ errors look like this:
+ 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
+ and conffile-prompt like this
+ 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
+ */
if(action == "error")
{
- d->progress->Error(pkgname, PackagesDone, PackagesTotal,
- list[3]);
- pkgFailures++;
+ d->progress->Error(pkgname, PackagesDone, PackagesTotal, list[3]);
+ ++pkgFailures;
WriteApportReport(pkgname.c_str(), list[3].c_str());
return;
}
else if(action == "conffile-prompt")
{
- d->progress->ConffilePrompt(pkgname, PackagesDone, PackagesTotal,
- list[3]);
+ d->progress->ConffilePrompt(pkgname, PackagesDone, PackagesTotal, list[3]);
return;
}
+ } else {
+ if (Debug == true)
+ std::clog << "unknown prefix '" << prefix << "'" << std::endl;
+ return;
}
- // at this point we know that we should have a valid pkgname, so build all
- // the info from it
-
- // dpkg does not always send "pkgname:arch" so we add it here if needed
+ // At this point we have a pkgname, but it might not be arch-qualified !
if (pkgname.find(":") == std::string::npos)
{
- // find the package in the group that is touched by dpkg
- // if there are multiple pkgs dpkg would send us a full pkgname:arch
pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
- if (Grp.end() == false)
- for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
- if(Cache[P].Keep() == false || Cache[P].ReInstall() == true)
+ /* No arch means that dpkg believes there can only be one package
+ this can refer to so lets see what could be candidates here: */
+ std::vector<pkgCache::PkgIterator> candset;
+ for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
+ {
+ if (PackageOps.find(P.FullName()) != PackageOps.end())
+ candset.push_back(P);
+ // packages can disappear without them having any interaction itself
+ // so we have to consider these as candidates, too
+ else if (P->CurrentVer != 0 && action == "disappear")
+ candset.push_back(P);
+ }
+ if (unlikely(candset.empty()))
+ {
+ if (Debug == true)
+ std::clog << "unable to figure out which package is dpkg refering to with '" << pkgname << "'! (1)" << std::endl;
+ return;
+ }
+ else if (candset.size() == 1) // we are lucky
+ pkgname = candset.cbegin()->FullName();
+ else
+ {
+ /* here be dragons^Wassumptions about dpkg:
+ - an M-A:same version is always arch-qualified
+ - a package from a foreign arch is (in newer versions) */
+ size_t installedInstances = 0, wannabeInstances = 0;
+ for (auto const &P: candset)
+ {
+ if (P->CurrentVer != 0)
+ {
+ ++installedInstances;
+ if (Cache[P].Delete() == false)
+ ++wannabeInstances;
+ }
+ else if (Cache[P].Install())
+ ++wannabeInstances;
+ }
+ // the package becomes M-A:same, so we are still talking about current
+ if (installedInstances == 1 && wannabeInstances >= 2)
+ {
+ for (auto const &P: candset)
{
- auto fullname = P.FullName();
- if (Cache[P].Delete() && PackageOps[fullname].size() <= PackageOpsDone[fullname])
+ if (P->CurrentVer == 0)
continue;
- pkgname = std::move(fullname);
+ pkgname = P.FullName();
break;
}
+ }
+ // the package was M-A:same, it isn't now, so we can only talk about that
+ else if (installedInstances >= 2 && wannabeInstances == 1)
+ {
+ for (auto const &P: candset)
+ {
+ auto const IV = Cache[P].InstVerIter(Cache);
+ if (IV.end())
+ continue;
+ pkgname = P.FullName();
+ break;
+ }
+ }
+ // that is a crossgrade
+ else if (installedInstances == 1 && wannabeInstances == 1 && candset.size() == 2)
+ {
+ auto const PkgHasCurrentVersion = [](pkgCache::PkgIterator const &P) { return P->CurrentVer != 0; };
+ auto const P = std::find_if(candset.begin(), candset.end(), PkgHasCurrentVersion);
+ if (unlikely(P == candset.end()))
+ {
+ if (Debug == true)
+ std::clog << "situation for '" << pkgname << "' looked like a crossgrade, but no current version?!" << std::endl;
+ return;
+ }
+ auto fullname = P->FullName();
+ if (PackageOps[fullname].size() != PackageOpsDone[fullname])
+ pkgname = std::move(fullname);
+ else
+ pkgname = std::find_if_not(candset.begin(), candset.end(), PkgHasCurrentVersion)->FullName();
+ }
+ // we are desperate: so "just" take the native one, but that might change mid-air,
+ // so we have to ask dpkg what it believes native is at the moment… all the time
+ else
+ {
+ std::vector<std::string> sArgs = debSystem::GetDpkgBaseCommand();
+ sArgs.push_back("--print-architecture");
+ int outputFd = -1;
+ pid_t const dpkgNativeArch = debSystem::ExecDpkg(sArgs, nullptr, &outputFd, true);
+ if (unlikely(dpkgNativeArch == -1))
+ {
+ if (Debug == true)
+ std::clog << "calling dpkg failed to ask it for its current native architecture to expand '" << pkgname << "'!" << std::endl;
+ return;
+ }
+ FILE *dpkg = fdopen(outputFd, "r");
+ if(dpkg != NULL)
+ {
+ char* buf = NULL;
+ size_t bufsize = 0;
+ if (getline(&buf, &bufsize, dpkg) != -1)
+ pkgname += ':' + bufsize;
+ free(buf);
+ fclose(dpkg);
+ }
+ ExecWait(dpkgNativeArch, "dpkg --print-architecture", true);
+ if (pkgname.find(':') != std::string::npos)
+ {
+ if (Debug == true)
+ std::clog << "unable to figure out which package is dpkg refering to with '" << pkgname << "'! (2)" << std::endl;
+ return;
+ }
+ }
+ }
}
std::string arch = "";
@@ -706,36 +795,7 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
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;
- }
- }
- }
- }
- }
+ handleCrossUpgradeAction(pkgname);
return;
}
@@ -849,6 +909,38 @@ void pkgDPkgPM::handleDisappearAction(string const &pkgname)
}
}
/*}}}*/
+void pkgDPkgPM::handleCrossUpgradeAction(string const &pkgname) /*{{{*/
+{
+ // 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;
+ }
+ }
+ }
+ }
+}
+ /*}}}*/
// DPkgPM::DoDpkgStatusFd /*{{{*/
void pkgDPkgPM::DoDpkgStatusFd(int statusfd)
{