]> git.saurik.com Git - apt.git/blob - apt-pkg/statechanges.cc
a20319d2df96251434ec33a5e1d8e3c8fea9152b
[apt.git] / apt-pkg / statechanges.cc
1 #include <apt-pkg/pkgcache.h>
2 #include <apt-pkg/cacheset.h>
3 #include <apt-pkg/debsystem.h>
4 #include <apt-pkg/fileutl.h>
5 #include <apt-pkg/statechanges.h>
6
7 #include <algorithm>
8 #include <memory>
9
10 namespace APT
11 {
12
13 class StateChanges::Private
14 {
15 public:
16 APT::VersionVector hold;
17 APT::VersionVector install;
18 APT::VersionVector error;
19 };
20
21 void StateChanges::Hold(pkgCache::VerIterator const &Ver)
22 {
23 d->hold.push_back(Ver);
24 }
25 APT::VersionVector& StateChanges::Hold()
26 {
27 return d->hold;
28 }
29 void StateChanges::Unhold(pkgCache::VerIterator const &Ver)
30 {
31 d->install.push_back(Ver);
32 }
33 APT::VersionVector& StateChanges::Unhold()
34 {
35 return d->install;
36 }
37 APT::VersionVector& StateChanges::Error()
38 {
39 return d->error;
40 }
41
42 void StateChanges::Discard()
43 {
44 d->hold.clear();
45 d->install.clear();
46 d->error.clear();
47 }
48
49 bool StateChanges::Save(bool const DiscardOutput)
50 {
51 d->error.clear();
52 if (d->hold.empty() && d->install.empty())
53 return true;
54
55 std::vector<std::string> Args = debSystem::GetDpkgBaseCommand();
56 // ensure dpkg knows about the package so that it keeps the status we set
57 {
58 APT::VersionVector makeDpkgAvailable;
59 auto const notInstalled = [](pkgCache::VerIterator const &V) { return V.ParentPkg()->CurrentVer == 0; };
60 std::copy_if(d->hold.begin(), d->hold.end(), std::back_inserter(makeDpkgAvailable), notInstalled);
61 std::copy_if(d->install.begin(), d->install.end(), std::back_inserter(makeDpkgAvailable), notInstalled);
62
63 if (makeDpkgAvailable.empty() == false)
64 {
65 auto const BaseArgs = Args.size();
66 Args.push_back("--merge-avail");
67 // FIXME: supported only since 1.17.7 in dpkg
68 Args.push_back("-");
69 int dummyAvail = -1;
70 pid_t const dpkgMergeAvail = debSystem::ExecDpkg(Args, &dummyAvail, nullptr, true);
71
72 FILE* dpkg = fdopen(dummyAvail, "w");
73 for (auto const &V: makeDpkgAvailable)
74 fprintf(dpkg, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example <dummy@example.org>\n"
75 "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());
76 fclose(dpkg);
77
78 ExecWait(dpkgMergeAvail, "dpkg --merge-avail", true);
79 Args.erase(Args.begin() + BaseArgs, Args.end());
80 }
81 }
82 bool const dpkgMultiArch = _system->MultiArchSupported();
83
84 Args.push_back("--set-selections");
85 int selections = -1;
86 pid_t const dpkgSelections = debSystem::ExecDpkg(Args, &selections, nullptr, DiscardOutput);
87
88 FILE* dpkg = fdopen(selections, "w");
89 std::string state;
90 auto const dpkgName = [&](pkgCache::VerIterator const &V) {
91 pkgCache::PkgIterator P = V.ParentPkg();
92 if (dpkgMultiArch == false)
93 fprintf(dpkg, "%s %s\n", P.FullName(true).c_str(), state.c_str());
94 else
95 fprintf(dpkg, "%s:%s %s\n", P.Name(), V.Arch(), state.c_str());
96 };
97 if (d->hold.empty() == false)
98 {
99 state = "hold";
100 std::for_each(d->hold.begin(), d->hold.end(), dpkgName);
101 }
102 if (d->install.empty() == false)
103 {
104 state = "install";
105 std::for_each(d->install.begin(), d->install.end(), dpkgName);
106 }
107 fclose(dpkg);
108
109 if (ExecWait(dpkgSelections, "dpkg --set-selections") == false)
110 {
111 if (d->hold.empty())
112 std::swap(d->install, d->error);
113 else if (d->install.empty())
114 std::swap(d->hold, d->error);
115 else
116 {
117 std::swap(d->hold, d->error);
118 std::move(d->install.begin(), d->install.end(), std::back_inserter(d->error));
119 d->install.clear();
120 }
121 }
122 return d->error.empty();
123 }
124
125 StateChanges::StateChanges() : d(new StateChanges::Private()) {}
126 StateChanges::StateChanges(StateChanges&&) = default;
127 StateChanges& StateChanges::operator=(StateChanges&&) = default;
128 StateChanges::~StateChanges() = default;
129
130 }