summaryrefslogtreecommitdiff
path: root/apt-pkg/statechanges.cc
blob: a20319d2df96251434ec33a5e1d8e3c8fea9152b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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;

}