summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt-pkg/deb/dpkgpm.cc216
-rw-r--r--apt-pkg/deb/dpkgpm.h1
-rwxr-xr-xtest/integration/test-bug-618288-multiarch-same-lockstep3
-rwxr-xr-xtest/integration/test-crossgrades13
4 files changed, 169 insertions, 64 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)
{
diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h
index 193754644..d1c2bcf41 100644
--- a/apt-pkg/deb/dpkgpm.h
+++ b/apt-pkg/deb/dpkgpm.h
@@ -53,6 +53,7 @@ class pkgDPkgPM : public pkgPackageManager
\param pkgname Name of the package that disappeared
*/
APT_HIDDEN void handleDisappearAction(std::string const &pkgname);
+ APT_HIDDEN void handleCrossUpgradeAction(std::string const &pkgname);
protected:
int pkgFailures;
diff --git a/test/integration/test-bug-618288-multiarch-same-lockstep b/test/integration/test-bug-618288-multiarch-same-lockstep
index d9582f6b6..eeecb40ee 100755
--- a/test/integration/test-bug-618288-multiarch-same-lockstep
+++ b/test/integration/test-bug-618288-multiarch-same-lockstep
@@ -36,3 +36,6 @@ test "$LS_U_AMD" -lt "$LS_C_INT" && msgpass || msgfail
msgtest 'Test if' 'libsame:i386 unpack is before libsame:amd64 configure'
test "$LS_U_INT" -lt "$LS_C_AMD" && msgpass || msgfail
+
+# lets see if it really does work out
+testsuccess apt dist-upgrade -y
diff --git a/test/integration/test-crossgrades b/test/integration/test-crossgrades
index d412546c1..30195d30f 100755
--- a/test/integration/test-crossgrades
+++ b/test/integration/test-crossgrades
@@ -11,6 +11,7 @@ configdpkgnoopchroot
buildsimplenativepackage 'unrelated' 'amd64' '1' 'stable'
buildsimplenativepackage 'crosser' 'i386,armel' '1' 'stable' 'Multi-Arch: same'
buildsimplenativepackage 'crosser' 'amd64' '2' 'unstable'
+buildsimplenativepackage 'crosser' 'i386,armel' '3' 'experimental' 'Multi-Arch: same'
setupaptarchive
singleinstance() {
@@ -43,7 +44,15 @@ multiinstance() {
testdpkgnotinstalled 'crosser:i386' 'crosser:armel' 'unrelated'
testdpkginstalled 'crosser:amd64'
- testsuccess apt purge crosser:amd64 -y --planner $1
- testdpkgnotinstalled 'crosser:amd64'
+ testsuccess apt install crosser:i386 crosser:armel -t experimental -y -o Debug::pkgDpkgPm=1 -o Dpkg::Use-Pty=0 --purge --planner $1
+ cp -a rootdir/tmp/testsuccess.output crosser.output
+ testsuccess grep -- '--remove.*crosser.*' crosser.output
+ testsuccess grep -- '--purge' crosser.output
+ testsuccess apt install crosser:i386 crosser:armel -t experimental -y -o Debug::pkgDPkgProgressReporting=1 -o Dpkg::Use-Pty=0 --purge --planner $1
+ testdpkgnotinstalled 'crosser:amd64' 'unrelated'
+ testdpkginstalled 'crosser:i386' 'crosser:armel'
+
+ testsuccess apt purge crosser:i386 crosser:armel -y --planner $1
+ testdpkgnotinstalled 'crosser:i386' 'crosser:armel' 'unrelated'
}
multiinstance 'internal'