diff options
Diffstat (limited to 'apt-pkg/statechanges.cc')
-rw-r--r-- | apt-pkg/statechanges.cc | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/apt-pkg/statechanges.cc b/apt-pkg/statechanges.cc new file mode 100644 index 000000000..35af45538 --- /dev/null +++ b/apt-pkg/statechanges.cc @@ -0,0 +1,227 @@ +#include <apt-pkg/pkgcache.h> +#include <apt-pkg/cacheset.h> +#include <apt-pkg/debsystem.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/statechanges.h> +#include <apt-pkg/prettyprinters.h> + +#include <algorithm> +#include <memory> + +namespace APT +{ + +class StateChanges::Private +{ +public: + APT::VersionVector hold; + APT::VersionVector unhold; + APT::VersionVector install; + APT::VersionVector deinstall; + APT::VersionVector purge; + APT::VersionVector error; +}; + +#define APT_GETTERSETTER(Name, Container) \ +void StateChanges::Name(pkgCache::VerIterator const &Ver) \ +{ \ + if (Ver.end() == false) \ + Container.push_back(Ver); \ +}\ +APT::VersionVector& StateChanges::Name() \ +{ \ + return Container; \ +} +APT_GETTERSETTER(Hold, d->hold) +APT_GETTERSETTER(Unhold, d->unhold) +APT_GETTERSETTER(Install, d->install) +APT_GETTERSETTER(Remove, d->deinstall) +APT_GETTERSETTER(Purge, d->purge) +#undef APT_GETTERSETTER +APT::VersionVector& StateChanges::Error() +{ + return d->error; +} + +void StateChanges::clear() +{ + d->hold.clear(); + d->unhold.clear(); + d->install.clear(); + d->deinstall.clear(); + d->purge.clear(); + d->error.clear(); +} + +bool StateChanges::empty() const +{ + return d->hold.empty() && + d->unhold.empty() && + d->install.empty() && + d->deinstall.empty() && + d->purge.empty() && + d->error.empty(); +} + +bool StateChanges::Save(bool const DiscardOutput) +{ + bool const Debug = _config->FindB("Debug::pkgDpkgPm", false); + d->error.clear(); + if (d->hold.empty() && d->unhold.empty() && d->install.empty() && d->deinstall.empty() && d->purge.empty()) + return true; + + std::vector<std::string> Args = debSystem::GetDpkgBaseCommand(); + // ensure dpkg knows about the package so that it keeps the status we set + if (d->hold.empty() == false || d->install.empty() == false) + { + APT::VersionVector makeDpkgAvailable; + auto const notInstalled = [](pkgCache::VerIterator const &V) { return V.ParentPkg()->CurrentVer == 0; }; + std::copy_if(d->hold.begin(), d->hold.end(), std::back_inserter(makeDpkgAvailable), notInstalled); + std::copy_if(d->install.begin(), d->install.end(), std::back_inserter(makeDpkgAvailable), notInstalled); + + if (makeDpkgAvailable.empty() == false) + { + auto const BaseArgs = Args.size(); + Args.push_back("--merge-avail"); + // FIXME: supported only since 1.17.7 in dpkg + Args.push_back("-"); + int dummyAvail = -1; + if (Debug) + { + for (auto const &V: makeDpkgAvailable) + { + std::clog << "echo 'Dummy record for " << V.ParentPkg().FullName(false) << "' | "; + std::copy(Args.begin(), Args.end(), std::ostream_iterator<std::string>(std::clog, " ")); + std::clog << std::endl; + } + } + else + { + pid_t const dpkgMergeAvail = debSystem::ExecDpkg(Args, &dummyAvail, nullptr, true); + + FILE* dpkg = fdopen(dummyAvail, "w"); + for (auto const &V: makeDpkgAvailable) + fprintf(dpkg, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example <dummy@example.org>\n" + "Description: dummy package record\n A record is needed to put a package on hold, so here it is.\n\n", V.ParentPkg().Name(), V.Arch()); + fclose(dpkg); + + ExecWait(dpkgMergeAvail, "dpkg --merge-avail", true); + } + Args.erase(Args.begin() + BaseArgs, Args.end()); + } + } + bool const dpkgMultiArch = _system->MultiArchSupported(); + + Args.push_back("--set-selections"); + if (Debug) + { + std::string state; + auto const dpkgName = [&](pkgCache::VerIterator const &V) { + pkgCache::PkgIterator P = V.ParentPkg(); + if (strcmp(V.Arch(), "none") == 0) + ioprintf(std::clog, "echo '%s %s' | ", P.Name(), state.c_str()); + else if (dpkgMultiArch == false) + ioprintf(std::clog, "echo '%s %s' | ", P.FullName(true).c_str(), state.c_str()); + else + ioprintf(std::clog, "echo '%s:%s %s' | ", P.Name(), V.Arch(), state.c_str()); + std::copy(Args.begin(), Args.end(), std::ostream_iterator<std::string>(std::clog, " ")); + std::clog << std::endl; + }; + for (auto const &V: d->unhold) + { + if (V.ParentPkg()->CurrentVer != 0) + state = "install"; + else + state = "deinstall"; + dpkgName(V); + } + if (d->purge.empty() == false) + { + state = "purge"; + std::for_each(d->purge.begin(), d->purge.end(), dpkgName); + } + if (d->deinstall.empty() == false) + { + state = "deinstall"; + std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName); + } + if (d->hold.empty() == false) + { + state = "hold"; + std::for_each(d->hold.begin(), d->hold.end(), dpkgName); + } + if (d->install.empty() == false) + { + state = "install"; + std::for_each(d->install.begin(), d->install.end(), dpkgName); + } + } + else + { + int selections = -1; + pid_t const dpkgSelections = debSystem::ExecDpkg(Args, &selections, nullptr, DiscardOutput); + + FILE* dpkg = fdopen(selections, "w"); + std::string state; + auto const dpkgName = [&](pkgCache::VerIterator const &V) { + pkgCache::PkgIterator P = V.ParentPkg(); + if (strcmp(V.Arch(), "none") == 0) + fprintf(dpkg, "%s %s\n", P.Name(), state.c_str()); + else if (dpkgMultiArch == false) + fprintf(dpkg, "%s %s\n", P.FullName(true).c_str(), state.c_str()); + else + fprintf(dpkg, "%s:%s %s\n", P.Name(), V.Arch(), state.c_str()); + }; + for (auto const &V: d->unhold) + { + if (V.ParentPkg()->CurrentVer != 0) + state = "install"; + else + state = "deinstall"; + dpkgName(V); + } + if (d->purge.empty() == false) + { + state = "purge"; + std::for_each(d->purge.begin(), d->purge.end(), dpkgName); + } + if (d->deinstall.empty() == false) + { + state = "deinstall"; + std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName); + } + if (d->hold.empty() == false) + { + state = "hold"; + std::for_each(d->hold.begin(), d->hold.end(), dpkgName); + } + if (d->install.empty() == false) + { + state = "install"; + std::for_each(d->install.begin(), d->install.end(), dpkgName); + } + fclose(dpkg); + + if (ExecWait(dpkgSelections, "dpkg --set-selections") == false) + { + std::move(d->purge.begin(), d->purge.end(), std::back_inserter(d->error)); + d->purge.clear(); + std::move(d->deinstall.begin(), d->deinstall.end(), std::back_inserter(d->error)); + d->deinstall.clear(); + std::move(d->hold.begin(), d->hold.end(), std::back_inserter(d->error)); + d->hold.clear(); + std::move(d->unhold.begin(), d->unhold.end(), std::back_inserter(d->error)); + d->unhold.clear(); + std::move(d->install.begin(), d->install.end(), std::back_inserter(d->error)); + d->install.clear(); + } + } + return d->error.empty(); +} + +StateChanges::StateChanges() : d(new StateChanges::Private()) {} +StateChanges::StateChanges(StateChanges&&) = default; +StateChanges& StateChanges::operator=(StateChanges&&) = default; +StateChanges::~StateChanges() = default; + +} |