]> git.saurik.com Git - apt.git/blob - apt-pkg/statechanges.cc
save and restore selection states before/after calling dpkg
[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 #include <apt-pkg/prettyprinters.h>
7
8 #include <algorithm>
9 #include <memory>
10
11 namespace APT
12 {
13
14 class StateChanges::Private
15 {
16 public:
17 APT::VersionVector hold;
18 APT::VersionVector unhold;
19 APT::VersionVector install;
20 APT::VersionVector deinstall;
21 APT::VersionVector purge;
22 APT::VersionVector error;
23 };
24
25 #define APT_GETTERSETTER(Name, Container) \
26 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
27 { \
28 if (Ver.end() == false) \
29 Container.push_back(Ver); \
30 }\
31 APT::VersionVector& StateChanges::Name() \
32 { \
33 return Container; \
34 }
35 APT_GETTERSETTER(Hold, d->hold)
36 APT_GETTERSETTER(Unhold, d->unhold)
37 APT_GETTERSETTER(Install, d->install)
38 APT_GETTERSETTER(Remove, d->deinstall)
39 APT_GETTERSETTER(Purge, d->purge)
40 #undef APT_GETTERSETTER
41 APT::VersionVector& StateChanges::Error()
42 {
43 return d->error;
44 }
45
46 void StateChanges::clear()
47 {
48 d->hold.clear();
49 d->unhold.clear();
50 d->install.clear();
51 d->deinstall.clear();
52 d->purge.clear();
53 d->error.clear();
54 }
55
56 bool StateChanges::empty() const
57 {
58 return d->hold.empty() &&
59 d->unhold.empty() &&
60 d->install.empty() &&
61 d->deinstall.empty() &&
62 d->purge.empty() &&
63 d->error.empty();
64 }
65
66 bool StateChanges::Save(bool const DiscardOutput)
67 {
68 bool const Debug = _config->FindB("Debug::pkgDpkgPm", false);
69 d->error.clear();
70 if (d->hold.empty() && d->unhold.empty() && d->install.empty() && d->deinstall.empty() && d->purge.empty())
71 return true;
72
73 std::vector<std::string> Args = debSystem::GetDpkgBaseCommand();
74 // ensure dpkg knows about the package so that it keeps the status we set
75 if (d->hold.empty() == false || d->install.empty() == false)
76 {
77 APT::VersionVector makeDpkgAvailable;
78 auto const notInstalled = [](pkgCache::VerIterator const &V) { return V.ParentPkg()->CurrentVer == 0; };
79 std::copy_if(d->hold.begin(), d->hold.end(), std::back_inserter(makeDpkgAvailable), notInstalled);
80 std::copy_if(d->install.begin(), d->install.end(), std::back_inserter(makeDpkgAvailable), notInstalled);
81
82 if (makeDpkgAvailable.empty() == false)
83 {
84 auto const BaseArgs = Args.size();
85 Args.push_back("--merge-avail");
86 // FIXME: supported only since 1.17.7 in dpkg
87 Args.push_back("-");
88 int dummyAvail = -1;
89 if (Debug)
90 {
91 for (auto const &V: makeDpkgAvailable)
92 {
93 std::clog << "echo 'Dummy record for " << V.ParentPkg().FullName(false) << "' | ";
94 std::copy(Args.begin(), Args.end(), std::ostream_iterator<std::string>(std::clog, " "));
95 std::clog << std::endl;
96 }
97 }
98 else
99 {
100 pid_t const dpkgMergeAvail = debSystem::ExecDpkg(Args, &dummyAvail, nullptr, true);
101
102 FILE* dpkg = fdopen(dummyAvail, "w");
103 for (auto const &V: makeDpkgAvailable)
104 fprintf(dpkg, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example <dummy@example.org>\n"
105 "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());
106 fclose(dpkg);
107
108 ExecWait(dpkgMergeAvail, "dpkg --merge-avail", true);
109 }
110 Args.erase(Args.begin() + BaseArgs, Args.end());
111 }
112 }
113 bool const dpkgMultiArch = _system->MultiArchSupported();
114
115 Args.push_back("--set-selections");
116 if (Debug)
117 {
118 std::string state;
119 auto const dpkgName = [&](pkgCache::VerIterator const &V) {
120 pkgCache::PkgIterator P = V.ParentPkg();
121 if (strcmp(V.Arch(), "none") == 0)
122 ioprintf(std::clog, "echo '%s %s' | ", P.Name(), state.c_str());
123 else if (dpkgMultiArch == false)
124 ioprintf(std::clog, "echo '%s %s' | ", P.FullName(true).c_str(), state.c_str());
125 else
126 ioprintf(std::clog, "echo '%s:%s %s' | ", P.Name(), V.Arch(), state.c_str());
127 std::copy(Args.begin(), Args.end(), std::ostream_iterator<std::string>(std::clog, " "));
128 std::clog << std::endl;
129 };
130 for (auto const &V: d->unhold)
131 {
132 if (V.ParentPkg()->CurrentVer != 0)
133 state = "install";
134 else
135 state = "deinstall";
136 dpkgName(V);
137 }
138 if (d->purge.empty() == false)
139 {
140 state = "purge";
141 std::for_each(d->purge.begin(), d->purge.end(), dpkgName);
142 }
143 if (d->deinstall.empty() == false)
144 {
145 state = "deinstall";
146 std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName);
147 }
148 if (d->hold.empty() == false)
149 {
150 state = "hold";
151 std::for_each(d->hold.begin(), d->hold.end(), dpkgName);
152 }
153 if (d->install.empty() == false)
154 {
155 state = "install";
156 std::for_each(d->install.begin(), d->install.end(), dpkgName);
157 }
158 }
159 else
160 {
161 int selections = -1;
162 pid_t const dpkgSelections = debSystem::ExecDpkg(Args, &selections, nullptr, DiscardOutput);
163
164 FILE* dpkg = fdopen(selections, "w");
165 std::string state;
166 auto const dpkgName = [&](pkgCache::VerIterator const &V) {
167 pkgCache::PkgIterator P = V.ParentPkg();
168 if (strcmp(V.Arch(), "none") == 0)
169 fprintf(dpkg, "%s %s\n", P.Name(), state.c_str());
170 else if (dpkgMultiArch == false)
171 fprintf(dpkg, "%s %s\n", P.FullName(true).c_str(), state.c_str());
172 else
173 fprintf(dpkg, "%s:%s %s\n", P.Name(), V.Arch(), state.c_str());
174 };
175 for (auto const &V: d->unhold)
176 {
177 if (V.ParentPkg()->CurrentVer != 0)
178 state = "install";
179 else
180 state = "deinstall";
181 dpkgName(V);
182 }
183 if (d->purge.empty() == false)
184 {
185 state = "purge";
186 std::for_each(d->purge.begin(), d->purge.end(), dpkgName);
187 }
188 if (d->deinstall.empty() == false)
189 {
190 state = "deinstall";
191 std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName);
192 }
193 if (d->hold.empty() == false)
194 {
195 state = "hold";
196 std::for_each(d->hold.begin(), d->hold.end(), dpkgName);
197 }
198 if (d->install.empty() == false)
199 {
200 state = "install";
201 std::for_each(d->install.begin(), d->install.end(), dpkgName);
202 }
203 fclose(dpkg);
204
205 if (ExecWait(dpkgSelections, "dpkg --set-selections") == false)
206 {
207 std::move(d->purge.begin(), d->purge.end(), std::back_inserter(d->error));
208 d->purge.clear();
209 std::move(d->deinstall.begin(), d->deinstall.end(), std::back_inserter(d->error));
210 d->deinstall.clear();
211 std::move(d->hold.begin(), d->hold.end(), std::back_inserter(d->error));
212 d->hold.clear();
213 std::move(d->unhold.begin(), d->unhold.end(), std::back_inserter(d->error));
214 d->unhold.clear();
215 std::move(d->install.begin(), d->install.end(), std::back_inserter(d->error));
216 d->install.clear();
217 }
218 }
219 return d->error.empty();
220 }
221
222 StateChanges::StateChanges() : d(new StateChanges::Private()) {}
223 StateChanges::StateChanges(StateChanges&&) = default;
224 StateChanges& StateChanges::operator=(StateChanges&&) = default;
225 StateChanges::~StateChanges() = default;
226
227 }