summaryrefslogtreecommitdiff
path: root/apt-pkg
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2015-09-25 11:25:25 +0200
committerDavid Kalnischkies <david@kalnischkies.de>2015-11-04 18:04:00 +0100
commitb49068c566d749130e023536d54588c948c16edf (patch)
tree9d4b0921040f2b529eda14c2d2cc209be0815d37 /apt-pkg
parent05ef357e575e9c25eb6a35cb693d1eb19bb14d45 (diff)
provide public interface to hold/unhold packages
We had this code lying around in apt-mark for a while now, but other frontends need this (and similar) functionality as well, so its high time that we provide a public interface in libapt for this stuff.
Diffstat (limited to 'apt-pkg')
-rw-r--r--apt-pkg/deb/debsystem.cc8
-rw-r--r--apt-pkg/deb/debsystem.h3
-rw-r--r--apt-pkg/policy.cc4
-rw-r--r--apt-pkg/statechanges.cc130
-rw-r--r--apt-pkg/statechanges.h52
5 files changed, 190 insertions, 7 deletions
diff --git a/apt-pkg/deb/debsystem.cc b/apt-pkg/deb/debsystem.cc
index d6ef49a37..dcd115b50 100644
--- a/apt-pkg/deb/debsystem.cc
+++ b/apt-pkg/deb/debsystem.cc
@@ -302,7 +302,7 @@ void debSystem::DpkgChrootDirectory() /*{{{*/
_exit(100);
}
/*}}}*/
-static pid_t ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd, int * const outputFd, bool const showStderr)/*{{{*/
+pid_t debSystem::ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd, int * const outputFd, bool const DiscardOutput)/*{{{*/
{
std::vector<const char *> Args(sArgs.size(), NULL);
std::transform(sArgs.begin(), sArgs.end(), Args.begin(), [](std::string const &s) { return s.c_str(); });
@@ -333,7 +333,7 @@ static pid_t ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd
close(external[0]);
dup2(external[1], STDOUT_FILENO);
}
- if (showStderr == false)
+ if (DiscardOutput == true)
dup2(nullfd, STDERR_FILENO);
debSystem::DpkgChrootDirectory();
execvp(Args[0], (char**) &Args[0]);
@@ -357,7 +357,7 @@ bool debSystem::SupportsMultiArch() /*{{{*/
{
std::vector<std::string> Args = GetDpkgBaseCommand();
Args.push_back("--assert-multi-arch");
- pid_t const dpkgAssertMultiArch = ExecDpkg(Args, nullptr, nullptr, false);
+ pid_t const dpkgAssertMultiArch = ExecDpkg(Args, nullptr, nullptr, true);
if (dpkgAssertMultiArch > 0)
{
int Status = 0;
@@ -386,7 +386,7 @@ std::vector<std::string> debSystem::SupportedArchitectures() /*{{{*/
std::vector<std::string> sArgs = GetDpkgBaseCommand();
sArgs.push_back("--print-foreign-architectures");
int outputFd = -1;
- pid_t const dpkgMultiArch = ExecDpkg(sArgs, nullptr, &outputFd, false);
+ pid_t const dpkgMultiArch = ExecDpkg(sArgs, nullptr, &outputFd, true);
if (dpkgMultiArch == -1)
return archs;
diff --git a/apt-pkg/deb/debsystem.h b/apt-pkg/deb/debsystem.h
index efb33a3ed..5185c92d8 100644
--- a/apt-pkg/deb/debsystem.h
+++ b/apt-pkg/deb/debsystem.h
@@ -34,7 +34,7 @@ class debSystem : public pkgSystem
public:
virtual bool Lock() APT_OVERRIDE;
- virtual bool UnLock(bool NoErrors = false) APT_OVERRIDE;
+ virtual bool UnLock(bool NoErrors = false) APT_OVERRIDE;
virtual pkgPackageManager *CreatePM(pkgDepCache *Cache) const APT_OVERRIDE;
virtual bool Initialize(Configuration &Cnf) APT_OVERRIDE;
virtual bool ArchiveSupported(const char *Type) APT_OVERRIDE;
@@ -49,6 +49,7 @@ class debSystem : public pkgSystem
APT_HIDDEN static std::string GetDpkgExecutable();
APT_HIDDEN static std::vector<std::string> GetDpkgBaseCommand();
APT_HIDDEN static void DpkgChrootDirectory();
+ APT_HIDDEN static pid_t ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd, int * const outputFd, bool const DiscardOutput);
APT_HIDDEN static bool SupportsMultiArch();
APT_HIDDEN static std::vector<std::string> SupportedArchitectures();
};
diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc
index bea4bec89..4f953bd50 100644
--- a/apt-pkg/policy.cc
+++ b/apt-pkg/policy.cc
@@ -222,8 +222,8 @@ pkgCache::VerIterator pkgPolicy::GetCandidateVer(pkgCache::PkgIterator const &Pk
return Pref;
}
-
-// Policy::GetCandidateVer - Get the candidate install version /*{{{*/
+ /*}}}*/
+// Policy::GetCandidateVerNew - Get the candidate install version /*{{{*/
// ---------------------------------------------------------------------
/* Evaluate the package pins and the default list to deteremine what the
best package is. */
diff --git a/apt-pkg/statechanges.cc b/apt-pkg/statechanges.cc
new file mode 100644
index 000000000..a20319d2d
--- /dev/null
+++ b/apt-pkg/statechanges.cc
@@ -0,0 +1,130 @@
+#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 <algorithm>
+#include <memory>
+
+namespace APT
+{
+
+class StateChanges::Private
+{
+public:
+ APT::VersionVector hold;
+ APT::VersionVector install;
+ APT::VersionVector error;
+};
+
+void StateChanges::Hold(pkgCache::VerIterator const &Ver)
+{
+ d->hold.push_back(Ver);
+}
+APT::VersionVector& StateChanges::Hold()
+{
+ return d->hold;
+}
+void StateChanges::Unhold(pkgCache::VerIterator const &Ver)
+{
+ d->install.push_back(Ver);
+}
+APT::VersionVector& StateChanges::Unhold()
+{
+ return d->install;
+}
+APT::VersionVector& StateChanges::Error()
+{
+ return d->error;
+}
+
+void StateChanges::Discard()
+{
+ d->hold.clear();
+ d->install.clear();
+ d->error.clear();
+}
+
+bool StateChanges::Save(bool const DiscardOutput)
+{
+ d->error.clear();
+ if (d->hold.empty() && d->install.empty())
+ return true;
+
+ std::vector<std::string> Args = debSystem::GetDpkgBaseCommand();
+ // ensure dpkg knows about the package so that it keeps the status we set
+ {
+ 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;
+ 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");
+ 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 (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());
+ };
+ 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)
+ {
+ if (d->hold.empty())
+ std::swap(d->install, d->error);
+ else if (d->install.empty())
+ std::swap(d->hold, d->error);
+ else
+ {
+ std::swap(d->hold, d->error);
+ 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;
+
+}
diff --git a/apt-pkg/statechanges.h b/apt-pkg/statechanges.h
new file mode 100644
index 000000000..fa60c5864
--- /dev/null
+++ b/apt-pkg/statechanges.h
@@ -0,0 +1,52 @@
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/macros.h>
+
+#include <memory>
+
+namespace APT
+{
+
+/** Simple wrapper class to abstract away the differences in storing different
+ * states in different places potentially in different versions.
+ */
+class APT_PUBLIC StateChanges
+{
+public:
+ // getter/setter for the different states
+ APT::VersionVector& Hold();
+ void Hold(pkgCache::VerIterator const &Ver);
+ APT::VersionVector& Unhold();
+ void Unhold(pkgCache::VerIterator const &Ver);
+ APT::VersionVector& Error();
+
+ // forgets all unsaved changes
+ void Discard();
+
+ /** commit the staged changes to the database(s).
+ *
+ * Makes the needed calls to store the requested states.
+ * After this call the state containers will hold only versions
+ * for which the storing operation succeeded. Versions where the
+ * storing operation failed are collected in #Error(). Note that
+ * error is an upper bound as states are changed in batches so it
+ * isn't always clear which version triggered the failure exactly.
+ *
+ * @param DiscardOutput controls if stdout/stderr should be used
+ * by subprocesses for (detailed) error reporting if needed.
+ * @return \b false if storing failed, true otherwise.
+ * Note that some states might be applied even if the whole operation failed.
+ */
+ bool Save(bool const DiscardOutput = false);
+
+ StateChanges();
+ StateChanges(StateChanges&&);
+ StateChanges& operator=(StateChanges&&);
+ ~StateChanges();
+
+private:
+ class APT_HIDDEN Private;
+ std::unique_ptr<Private> d;
+};
+
+}