]> git.saurik.com Git - apt.git/blame - apt-pkg/edsp.cc
Fix buffer overflow in debListParser::VersionHash()
[apt.git] / apt-pkg / edsp.cc
CommitLineData
6d38011b
DK
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
6 /*}}}*/
7// Include Files /*{{{*/
ea542140
DK
8#include <config.h>
9
6d38011b 10#include <apt-pkg/error.h>
472ff00e 11#include <apt-pkg/cacheset.h>
453b82a3
DK
12#include <apt-pkg/depcache.h>
13#include <apt-pkg/pkgcache.h>
14#include <apt-pkg/cacheiterators.h>
f74d99c6
DK
15#include <apt-pkg/prettyprinters.h>
16#include <apt-pkg/packagemanager.h>
ac7f8f79
MV
17#include <apt-pkg/progress.h>
18#include <apt-pkg/fileutl.h>
19#include <apt-pkg/edsp.h>
20#include <apt-pkg/tagfile.h>
21#include <apt-pkg/strutl.h>
0b661a5c 22#include <apt-pkg/string_view.h>
307d9eb2 23#include <apt-pkg/pkgsystem.h>
6d38011b 24
3a487cc0 25#include <sys/stat.h>
453b82a3
DK
26#include <ctype.h>
27#include <stddef.h>
28#include <string.h>
453b82a3 29#include <unistd.h>
6d38011b 30#include <stdio.h>
a6f8c07f
DK
31
32#include <array>
453b82a3 33#include <limits>
472ff00e
DK
34#include <string>
35
ea542140 36#include <apti18n.h>
6d38011b
DK
37 /*}}}*/
38
472ff00e
DK
39using std::string;
40
d4f626ff 41// we could use pkgCache::DepType and ::Priority, but these would be localized strings…
a6f8c07f
DK
42constexpr char const * const PrioMap[] = {
43 nullptr, "important", "required", "standard",
44 "optional", "extra"
45};
46constexpr char const * const DepMap[] = {
47 nullptr, "Depends", "Pre-Depends", "Suggests",
48 "Recommends" , "Conflicts", "Replaces",
49 "Obsoletes", "Breaks", "Enhances"
50};
d4f626ff 51
4e92b116
DK
52// WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
53static bool WriteOkay_fn(FileFd &) { return true; }
54template<typename... Tail> static bool WriteOkay_fn(FileFd &output, APT::StringView data, Tail... more_data)
55{
56 return likely(output.Write(data.data(), data.length()) && WriteOkay_fn(output, more_data...));
57}
58template<typename... Tail> static bool WriteOkay_fn(FileFd &output, unsigned int data, Tail... more_data)
59{
60 std::string number;
61 strprintf(number, "%d", data);
62 return likely(output.Write(number.data(), number.length()) && WriteOkay_fn(output, more_data...));
63}
64template<typename... Data> static bool WriteOkay(bool &Okay, FileFd &output, Data&&... data)
65{
66 Okay = likely(Okay && WriteOkay_fn(output, std::forward<Data>(data)...));
67 return Okay;
68}
69template<typename... Data> static bool WriteOkay(FileFd &output, Data&&... data)
70{
71 bool Okay = likely(output.Failed() == false);
72 return WriteOkay(Okay, output, std::forward<Data>(data)...);
73}
74 /*}}}*/
c8a4ce6c
DK
75// WriteScenarioVersion /*{{{*/
76static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
d4f626ff
DK
77 pkgCache::VerIterator const &Ver)
78{
79 fprintf(output, "Package: %s\n", Pkg.Name());
a221efc3 80 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
d4f626ff
DK
81 fprintf(output, "Architecture: %s\n", Ver.Arch());
82 fprintf(output, "Version: %s\n", Ver.VerStr());
eed4639e 83 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
d4f626ff
DK
84 if (Pkg.CurrentVer() == Ver)
85 fprintf(output, "Installed: yes\n");
cbc702ea
DK
86 if (Pkg->SelectedState == pkgCache::State::Hold ||
87 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
d4f626ff
DK
88 fprintf(output, "Hold: yes\n");
89 fprintf(output, "APT-ID: %d\n", Ver->ID);
ef00bd7a
DK
90 if (PrioMap[Ver->Priority] != nullptr)
91 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
d4f626ff
DK
92 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
93 fprintf(output, "Essential: yes\n");
ef00bd7a
DK
94 if (Ver->Section != 0)
95 fprintf(output, "Section: %s\n", Ver.Section());
894d672e 96 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
d4f626ff 97 fprintf(output, "Multi-Arch: allowed\n");
894d672e 98 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
d4f626ff 99 fprintf(output, "Multi-Arch: foreign\n");
894d672e 100 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
d4f626ff 101 fprintf(output, "Multi-Arch: same\n");
b5ea5d4a
SZ
102 std::set<string> Releases;
103 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
104 pkgCache::PkgFileIterator File = I.File();
b07aeb1a 105 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
b5ea5d4a
SZ
106 string Release = File.RelStr();
107 if (!Release.empty())
108 Releases.insert(Release);
109 }
110 }
111 if (!Releases.empty()) {
112 fprintf(output, "APT-Release:\n");
113 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
114 fprintf(output, " %s\n", R->c_str());
d4f626ff 115 }
3c7567a5 116 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
294a8020 117 if (Cache.GetCandidateVersion(Pkg) == Ver)
d4f626ff
DK
118 fprintf(output, "APT-Candidate: yes\n");
119 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
120 fprintf(output, "APT-Automatic: yes\n");
ef00bd7a 121}
5caf35a7 122static bool WriteScenarioVersion(FileFd &output, pkgCache::PkgIterator const &Pkg,
ef00bd7a
DK
123 pkgCache::VerIterator const &Ver)
124{
4e92b116 125 bool Okay = WriteOkay(output, "Package: ", Pkg.Name(),
4e92b116 126 "\nArchitecture: ", Ver.Arch(),
5caf35a7 127 "\nVersion: ", Ver.VerStr());
4e92b116 128 WriteOkay(Okay, output, "\nAPT-ID: ", Ver->ID);
ef00bd7a 129 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
4e92b116 130 WriteOkay(Okay, output, "\nEssential: yes");
ef00bd7a 131 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
4e92b116 132 WriteOkay(Okay, output, "\nMulti-Arch: allowed");
ef00bd7a 133 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
4e92b116 134 WriteOkay(Okay, output, "\nMulti-Arch: foreign");
ef00bd7a 135 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
4e92b116 136 WriteOkay(Okay, output, "\nMulti-Arch: same");
ef00bd7a 137 return Okay;
d4f626ff
DK
138}
139 /*}}}*/
c8a4ce6c
DK
140// WriteScenarioDependency /*{{{*/
141static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
d4f626ff 142{
a6f8c07f 143 std::array<std::string, _count(DepMap)> dependencies;
d4f626ff
DK
144 bool orGroup = false;
145 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
146 {
8c7af4d4 147 if (Dep.IsImplicit() == true)
d4f626ff
DK
148 continue;
149 if (orGroup == false)
150 dependencies[Dep->Type].append(", ");
3addaba1 151 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
d4f626ff
DK
152 if (Dep->Version != 0)
153 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
154 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
155 {
156 dependencies[Dep->Type].append(" | ");
157 orGroup = true;
158 }
159 else
160 orGroup = false;
161 }
a6f8c07f 162 for (size_t i = 1; i < dependencies.size(); ++i)
d4f626ff
DK
163 if (dependencies[i].empty() == false)
164 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
165 string provides;
166 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
167 {
d5648746 168 if (Prv.IsMultiArchImplicit() == true)
d4f626ff 169 continue;
ef00bd7a
DK
170 if (provides.empty() == false)
171 provides.append(", ");
172 provides.append(Prv.Name());
2c53226b
DK
173 if (Prv->ProvideVersion != 0)
174 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
d4f626ff
DK
175 }
176 if (provides.empty() == false)
ef00bd7a
DK
177 fprintf(output, "Provides: %s\n", provides.c_str());
178}
5caf35a7 179static bool WriteScenarioDependency(FileFd &output, pkgCache::VerIterator const &Ver, bool const OnlyCritical)
ef00bd7a 180{
a6f8c07f 181 std::array<std::string, _count(DepMap)> dependencies;
ef00bd7a
DK
182 bool orGroup = false;
183 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
184 {
185 if (Dep.IsImplicit() == true)
186 continue;
5caf35a7
DK
187 if (OnlyCritical && Dep.IsCritical() == false)
188 continue;
ef00bd7a
DK
189 if (orGroup == false && dependencies[Dep->Type].empty() == false)
190 dependencies[Dep->Type].append(", ");
191 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
192 if (Dep->Version != 0)
193 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
194 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
195 {
196 dependencies[Dep->Type].append(" | ");
197 orGroup = true;
198 }
199 else
200 orGroup = false;
201 }
4e92b116 202 bool Okay = output.Failed() == false;
a6f8c07f 203 for (size_t i = 1; i < dependencies.size(); ++i)
ef00bd7a 204 if (dependencies[i].empty() == false)
4e92b116 205 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
ef00bd7a
DK
206 string provides;
207 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
208 {
209 if (Prv.IsMultiArchImplicit() == true)
210 continue;
211 if (provides.empty() == false)
212 provides.append(", ");
213 provides.append(Prv.Name());
214 if (Prv->ProvideVersion != 0)
215 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
216 }
217 if (provides.empty() == false)
4e92b116
DK
218 WriteOkay(Okay, output, "\nProvides: ", provides);
219 return WriteOkay(Okay, output, "\n");
d4f626ff
DK
220}
221 /*}}}*/
c8a4ce6c
DK
222// WriteScenarioLimitedDependency /*{{{*/
223static void WriteScenarioLimitedDependency(FILE* output,
d4f626ff
DK
224 pkgCache::VerIterator const &Ver,
225 APT::PackageSet const &pkgset)
226{
a6f8c07f 227 std::array<std::string, _count(DepMap)> dependencies;
d4f626ff
DK
228 bool orGroup = false;
229 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
230 {
8c7af4d4 231 if (Dep.IsImplicit() == true)
d4f626ff
DK
232 continue;
233 if (orGroup == false)
234 {
235 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
236 continue;
ef00bd7a
DK
237 if (dependencies[Dep->Type].empty() == false)
238 dependencies[Dep->Type].append(", ");
d4f626ff
DK
239 }
240 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
241 {
242 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
243 continue;
244 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
245 orGroup = false;
246 continue;
247 }
3addaba1 248 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
d4f626ff
DK
249 if (Dep->Version != 0)
250 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
251 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
252 {
253 dependencies[Dep->Type].append(" | ");
254 orGroup = true;
255 }
256 else
257 orGroup = false;
258 }
a6f8c07f 259 for (size_t i = 1; i < dependencies.size(); ++i)
d4f626ff 260 if (dependencies[i].empty() == false)
ef00bd7a 261 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str());
d4f626ff
DK
262 string provides;
263 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
264 {
d5648746 265 if (Prv.IsMultiArchImplicit() == true)
d4f626ff
DK
266 continue;
267 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
268 continue;
ef00bd7a
DK
269 if (provides.empty() == false)
270 provides.append(", ");
271 provides.append(Prv.Name());
272 if (Prv->ProvideVersion != 0)
273 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
d4f626ff
DK
274 }
275 if (provides.empty() == false)
ef00bd7a
DK
276 fprintf(output, "Provides: %s\n", provides.c_str());
277}
278static bool WriteScenarioLimitedDependency(FileFd &output,
279 pkgCache::VerIterator const &Ver,
5caf35a7
DK
280 std::vector<bool> const &pkgset,
281 bool const OnlyCritical)
ef00bd7a 282{
a6f8c07f 283 std::array<std::string, _count(DepMap)> dependencies;
ef00bd7a
DK
284 bool orGroup = false;
285 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
286 {
287 if (Dep.IsImplicit() == true)
288 continue;
5caf35a7
DK
289 if (OnlyCritical && Dep.IsCritical() == false)
290 continue;
ef00bd7a
DK
291 if (orGroup == false)
292 {
6dcae298 293 if (pkgset[Dep.TargetPkg()->ID] == false)
ef00bd7a
DK
294 continue;
295 if (dependencies[Dep->Type].empty() == false)
296 dependencies[Dep->Type].append(", ");
297 }
6dcae298 298 else if (pkgset[Dep.TargetPkg()->ID] == false)
ef00bd7a
DK
299 {
300 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
301 continue;
302 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
303 orGroup = false;
304 continue;
305 }
306 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
307 if (Dep->Version != 0)
308 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
309 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
310 {
311 dependencies[Dep->Type].append(" | ");
312 orGroup = true;
313 }
314 else
315 orGroup = false;
316 }
4e92b116 317 bool Okay = output.Failed() == false;
a6f8c07f 318 for (size_t i = 1; i < dependencies.size(); ++i)
ef00bd7a 319 if (dependencies[i].empty() == false)
4e92b116 320 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
ef00bd7a
DK
321 string provides;
322 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
323 {
324 if (Prv.IsMultiArchImplicit() == true)
325 continue;
6dcae298 326 if (pkgset[Prv.ParentPkg()->ID] == false)
ef00bd7a
DK
327 continue;
328 if (provides.empty() == false)
329 provides.append(", ");
330 provides.append(Prv.Name());
331 if (Prv->ProvideVersion != 0)
332 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
333 }
334 if (provides.empty() == false)
4e92b116
DK
335 WriteOkay(Okay, output, "\nProvides: ", provides);
336 return WriteOkay(Okay, output, "\n");
d4f626ff
DK
337}
338 /*}}}*/
33190fe3
DK
339static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
340{
341 /* versions which aren't current and aren't available in
342 any "online" source file are bad, expect if they are the choosen
343 candidate: The exception is for build-dep implementation as it creates
344 such pseudo (package) versions and removes them later on again.
345 We filter out versions at all so packages in 'rc' state only available
346 in dpkg/status aren't passed to solvers as they can't be installed. */
347 if (Pkg->CurrentVer != 0)
348 return false;
349 if (Cache.GetCandidateVersion(Pkg) == Ver)
350 return false;
351 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
352 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
353 return false;
354 return true;
355}
356 /*}}}*/
5caf35a7
DK
357static bool WriteScenarioEDSPVersion(pkgDepCache &Cache, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
358 pkgCache::VerIterator const &Ver)
359{
360 bool Okay = WriteOkay(output, "\nSource: ", Ver.SourcePkgName(),
361 "\nSource-Version: ", Ver.SourceVerStr());
362 if (PrioMap[Ver->Priority] != nullptr)
363 WriteOkay(Okay, output, "\nPriority: ", PrioMap[Ver->Priority]);
364 if (Ver->Section != 0)
365 WriteOkay(Okay, output, "\nSection: ", Ver.Section());
366 if (Pkg.CurrentVer() == Ver)
367 WriteOkay(Okay, output, "\nInstalled: yes");
368 if (Pkg->SelectedState == pkgCache::State::Hold ||
369 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
370 WriteOkay(Okay, output, "\nHold: yes");
371 std::set<string> Releases;
372 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
373 pkgCache::PkgFileIterator File = I.File();
374 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
375 string Release = File.RelStr();
376 if (!Release.empty())
377 Releases.insert(Release);
378 }
379 }
380 if (!Releases.empty()) {
381 WriteOkay(Okay, output, "\nAPT-Release:");
382 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
383 WriteOkay(Okay, output, "\n ", *R);
384 }
385 WriteOkay(Okay, output, "\nAPT-Pin: ", Cache.GetPolicy().GetPriority(Ver));
386 if (Cache.GetCandidateVersion(Pkg) == Ver)
387 WriteOkay(Okay, output, "\nAPT-Candidate: yes");
388 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
389 WriteOkay(Okay, output, "\nAPT-Automatic: yes");
390 return Okay;
391}
392 /*}}}*/
c8a4ce6c
DK
393// EDSP::WriteScenario - to the given file descriptor /*{{{*/
394bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
395{
396 if (Progress != NULL)
397 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
398 unsigned long p = 0;
399 std::vector<std::string> archs = APT::Configuration::getArchitectures();
400 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
401 {
402 std::string const arch = Pkg.Arch();
403 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
404 continue;
405 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
406 {
33190fe3
DK
407 if (SkipUnavailableVersions(Cache, Pkg, Ver))
408 continue;
c8a4ce6c
DK
409 WriteScenarioVersion(Cache, output, Pkg, Ver);
410 WriteScenarioDependency(output, Ver);
411 fprintf(output, "\n");
412 if (Progress != NULL && p % 100 == 0)
413 Progress->Progress(p);
414 }
415 }
416 return true;
ef00bd7a
DK
417}
418bool EDSP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress *Progress)
419{
420 if (Progress != NULL)
421 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
422 unsigned long p = 0;
4e92b116 423 bool Okay = output.Failed() == false;
ef00bd7a 424 std::vector<std::string> archs = APT::Configuration::getArchitectures();
4e92b116 425 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
ef00bd7a
DK
426 {
427 std::string const arch = Pkg.Arch();
428 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
429 continue;
4e92b116 430 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver, ++p)
ef00bd7a
DK
431 {
432 if (SkipUnavailableVersions(Cache, Pkg, Ver))
433 continue;
5caf35a7
DK
434 Okay &= WriteScenarioVersion(output, Pkg, Ver);
435 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
436 Okay &= WriteScenarioDependency(output, Ver, false);
4e92b116 437 WriteOkay(Okay, output, "\n");
ef00bd7a
DK
438 if (Progress != NULL && p % 100 == 0)
439 Progress->Progress(p);
440 }
441 }
442 return true;
c8a4ce6c
DK
443}
444 /*}}}*/
445// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
446bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
447 APT::PackageSet const &pkgset,
448 OpProgress *Progress)
449{
450 if (Progress != NULL)
451 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
452 unsigned long p = 0;
453 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
454 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
455 {
33190fe3
DK
456 if (SkipUnavailableVersions(Cache, Pkg, Ver))
457 continue;
c8a4ce6c
DK
458 WriteScenarioVersion(Cache, output, Pkg, Ver);
459 WriteScenarioLimitedDependency(output, Ver, pkgset);
460 fprintf(output, "\n");
461 if (Progress != NULL && p % 100 == 0)
462 Progress->Progress(p);
463 }
464 if (Progress != NULL)
465 Progress->Done();
466 return true;
ef00bd7a
DK
467}
468bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FileFd &output,
6dcae298 469 std::vector<bool> const &pkgset,
ef00bd7a
DK
470 OpProgress *Progress)
471{
472 if (Progress != NULL)
473 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
474 unsigned long p = 0;
4e92b116 475 bool Okay = output.Failed() == false;
6dcae298
DK
476 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
477 {
478 if (pkgset[Pkg->ID] == false)
479 continue;
4e92b116 480 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver)
ef00bd7a
DK
481 {
482 if (SkipUnavailableVersions(Cache, Pkg, Ver))
483 continue;
5caf35a7
DK
484 Okay &= WriteScenarioVersion(output, Pkg, Ver);
485 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
486 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, false);
4e92b116 487 WriteOkay(Okay, output, "\n");
ef00bd7a
DK
488 if (Progress != NULL && p % 100 == 0)
489 Progress->Progress(p);
490 }
6dcae298 491 }
ef00bd7a
DK
492 if (Progress != NULL)
493 Progress->Done();
494 return Okay;
c8a4ce6c
DK
495}
496 /*}}}*/
c3b85126 497// EDSP::WriteRequest - to the given file descriptor /*{{{*/
93794bc9 498bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
b57c0e35
DK
499 bool const DistUpgrade, bool const AutoRemove,
500 OpProgress *Progress)
6d38011b 501{
b57c0e35
DK
502 if (Progress != NULL)
503 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
504 unsigned long p = 0;
93794bc9 505 string del, inst;
b57c0e35 506 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
6d38011b 507 {
b57c0e35
DK
508 if (Progress != NULL && p % 100 == 0)
509 Progress->Progress(p);
6d38011b 510 string* req;
036eb012
DK
511 pkgDepCache::StateCache &P = Cache[Pkg];
512 if (P.Delete() == true)
6d38011b 513 req = &del;
036eb012
DK
514 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
515 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
6d38011b 516 req = &inst;
6d38011b
DK
517 else
518 continue;
6d5bd614 519 req->append(" ").append(Pkg.FullName());
6d38011b 520 }
25252738 521 fprintf(output, "Request: EDSP 0.5\n");
caa32793
SZ
522
523 const char *arch = _config->Find("APT::Architecture").c_str();
524 std::vector<string> archs = APT::Configuration::getArchitectures();
525 fprintf(output, "Architecture: %s\n", arch);
526 fprintf(output, "Architectures:");
527 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
528 fprintf(output, " %s", a->c_str());
529 fprintf(output, "\n");
530
6d38011b 531 if (del.empty() == false)
6d5bd614 532 fprintf(output, "Remove: %s\n", del.c_str()+1);
6d38011b 533 if (inst.empty() == false)
6d5bd614 534 fprintf(output, "Install: %s\n", inst.c_str()+1);
93794bc9
DK
535 if (Upgrade == true)
536 fprintf(output, "Upgrade: yes\n");
537 if (DistUpgrade == true)
538 fprintf(output, "Dist-Upgrade: yes\n");
539 if (AutoRemove == true)
540 fprintf(output, "Autoremove: yes\n");
eb1000f6
DK
541 auto const solver = _config->Find("APT::Solver", "internal");
542 fprintf(output, "Solver: %s\n", solver.c_str());
543 auto const solverconf = std::string("APT::Solver::") + solver + "::";
544 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
93794bc9 545 fprintf(output, "Strict-Pinning: no\n");
eb1000f6
DK
546 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
547 if (solverpref.empty() == false)
548 fprintf(output, "Preferences: %s\n", solverpref.c_str());
6d5bd614 549 fprintf(output, "\n");
e3674d91 550 return true;
ef00bd7a 551}
43c71fad
DK
552bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output,
553 unsigned int const flags,
ef00bd7a
DK
554 OpProgress *Progress)
555{
556 if (Progress != NULL)
557 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
558 unsigned long p = 0;
559 string del, inst;
560 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
561 {
562 if (Progress != NULL && p % 100 == 0)
563 Progress->Progress(p);
564 string* req;
565 pkgDepCache::StateCache &P = Cache[Pkg];
566 if (P.Delete() == true)
567 req = &del;
568 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
569 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
570 req = &inst;
571 else
572 continue;
573 req->append(" ").append(Pkg.FullName());
574 }
4e92b116 575 bool Okay = WriteOkay(output, "Request: EDSP 0.5\n");
ef00bd7a
DK
576
577 const char *arch = _config->Find("APT::Architecture").c_str();
578 std::vector<string> archs = APT::Configuration::getArchitectures();
4e92b116
DK
579 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
580 "Architectures:");
ef00bd7a 581 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
4e92b116
DK
582 WriteOkay(Okay, output, " ", *a);
583 WriteOkay(Okay, output, "\n");
ef00bd7a
DK
584
585 if (del.empty() == false)
4e92b116 586 WriteOkay(Okay, output, "Remove:", del, "\n");
ef00bd7a 587 if (inst.empty() == false)
4e92b116 588 WriteOkay(Okay, output, "Install:", inst, "\n");
43c71fad 589 if (flags & Request::AUTOREMOVE)
4e92b116 590 WriteOkay(Okay, output, "Autoremove: yes\n");
43c71fad
DK
591 if (flags & Request::UPGRADE_ALL)
592 {
593 WriteOkay(Okay, output, "Upgrade-All: yes\n");
594 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
595 WriteOkay(Okay, output, "Upgrade: yes\n");
596 else
597 WriteOkay(Okay, output, "Dist-Upgrade: yes\n");
598 }
599 if (flags & Request::FORBID_NEW_INSTALL)
600 WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
601 if (flags & Request::FORBID_REMOVE)
602 WriteOkay(Okay, output, "Forbid-Remove: yes\n");
385d9f2f
DK
603 auto const solver = _config->Find("APT::Solver", "internal");
604 WriteOkay(Okay, output, "Solver: ", solver, "\n");
ef00bd7a 605 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
4e92b116 606 WriteOkay(Okay, output, "Strict-Pinning: no\n");
ef00bd7a 607 string solverpref("APT::Solver::");
385d9f2f 608 solverpref.append(solver).append("::Preferences");
ef00bd7a 609 if (_config->Exists(solverpref) == true)
4e92b116
DK
610 WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
611 return WriteOkay(Okay, output, "\n");
e3674d91
DK
612}
613 /*}}}*/
2029276f 614// EDSP::ReadResponse - from the given file descriptor /*{{{*/
b57c0e35 615bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
2a33cb16
DK
616 /* We build an map id to mmap offset here
617 In theory we could use the offset as ID, but then VersionCount
618 couldn't be used to create other versionmappings anymore and it
619 would be too easy for a (buggy) solver to segfault APT… */
620 unsigned long long const VersionCount = Cache.Head().VersionCount;
621 unsigned long VerIdx[VersionCount];
76d4aab0 622 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
2a33cb16
DK
623 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
624 VerIdx[V->ID] = V.Index();
76d4aab0
DK
625 Cache[P].Marked = true;
626 Cache[P].Garbage = false;
627 }
2a33cb16 628
c80a49f5 629 FileFd in;
2202a8a0 630 in.OpenDescriptor(input, FileFd::ReadOnly, true);
288a76d2 631 pkgTagFile response(&in, 100);
c80a49f5
DK
632 pkgTagSection section;
633
90e7fba4 634 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
2029276f
DK
635 while (response.Step(section) == true) {
636 std::string type;
637 if (section.Exists("Install") == true)
638 type = "Install";
639 else if (section.Exists("Remove") == true)
640 type = "Remove";
e876223c 641 else if (section.Exists("Progress") == true) {
b57c0e35 642 if (Progress != NULL) {
c6660a4b 643 string msg = section.FindS("Message");
b57c0e35 644 if (msg.empty() == true)
c6660a4b
DK
645 msg = _("Prepare for receiving solution");
646 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
b57c0e35 647 }
e876223c 648 continue;
ebfeeaed 649 } else if (section.Exists("Error") == true) {
27c69dd0
DK
650 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
651 if (msg.empty() == true) {
652 msg = _("External solver failed without a proper error message");
a1e68c33 653 _error->Error("%s", msg.c_str());
27c69dd0
DK
654 } else
655 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
656 if (Progress != NULL)
657 Progress->Done();
ebfeeaed
DK
658 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
659 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
27c69dd0
DK
660 std::cerr << msg << std::endl << std::endl;
661 return false;
76d4aab0
DK
662 } else if (section.Exists("Autoremove") == true)
663 type = "Autoremove";
f74d99c6
DK
664 else {
665 char const *Start, *End;
666 section.GetSection(Start, End);
667 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
2029276f 668 continue;
f74d99c6 669 }
29099cb6 670
2a33cb16
DK
671 size_t const id = section.FindULL(type.c_str(), VersionCount);
672 if (id == VersionCount) {
69a78835
DK
673 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
674 continue;
2a33cb16
DK
675 } else if (id > Cache.Head().VersionCount) {
676 _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type.c_str()).c_str(), type.c_str());
677 continue;
69a78835 678 }
2029276f 679
2a33cb16 680 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
90e7fba4
DK
681 auto const Pkg = Ver.ParentPkg();
682 if (type == "Autoremove") {
683 Cache[Pkg].Marked = false;
684 Cache[Pkg].Garbage = true;
685 } else if (seenOnce.emplace(Pkg->ID).second == false) {
686 _error->Warning("Ignoring %s stanza received for package %s which already had a previous stanza effecting it!", type.c_str(), Pkg.FullName(false).c_str());
687 } else if (type == "Install") {
688 if (Pkg.CurrentVer() == Ver) {
689 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
690 Ver.VerStr(), Pkg.FullName(false).c_str());
691 } else {
692 Cache.SetCandidateVersion(Ver);
693 Cache.MarkInstall(Pkg, false, 0, false);
694 }
695 } else if (type == "Remove") {
696 if (Pkg->CurrentVer == 0)
697 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
698 Ver.VerStr(), Pkg.FullName(false).c_str());
699 else if (Pkg.CurrentVer() != Ver)
700 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
701 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
702 else
703 Cache.MarkDelete(Ver.ParentPkg(), false);
76d4aab0 704 }
2029276f
DK
705 }
706 return true;
707}
708 /*}}}*/
c8a4ce6c 709// ReadLine - first line from the given file descriptor /*{{{*/
6d5bd614
DK
710// ---------------------------------------------------------------------
711/* Little helper method to read a complete line into a string. Similar to
712 fgets but we need to use the low-level read() here as otherwise the
713 listparser will be confused later on as mixing of fgets and read isn't
2029276f 714 a supported action according to the manpages and results are undefined */
c8a4ce6c 715static bool ReadLine(int const input, std::string &line) {
6d5bd614
DK
716 char one;
717 ssize_t data = 0;
718 line.erase();
719 line.reserve(100);
720 while ((data = read(input, &one, sizeof(one))) != -1) {
721 if (data != 1)
722 continue;
723 if (one == '\n')
724 return true;
725 if (one == '\r')
726 continue;
727 if (line.empty() == true && isblank(one) != 0)
728 continue;
729 line += one;
730 }
731 return false;
732}
733 /*}}}*/
c8a4ce6c 734// StringToBool - convert yes/no to bool /*{{{*/
40795fca
DK
735// ---------------------------------------------------------------------
736/* we are not as lazy as we are in the global StringToBool as we really
0b661a5c
DK
737 only accept yes/no here */
738static bool localStringToBool(std::string answer, bool const defValue) {
739 std::transform(answer.begin(), answer.end(), answer.begin(), ::tolower);
740 if (answer == "yes")
40795fca 741 return true;
0b661a5c 742 else if (answer == "no")
40795fca
DK
743 return false;
744 else
0b661a5c 745 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer.c_str());
40795fca
DK
746 return defValue;
747}
748 /*}}}*/
0b661a5c 749static bool LineStartsWithAndStrip(std::string &line, APT::StringView const with)/*{{{*/
43c71fad 750{
0b661a5c 751 if (line.compare(0, with.size(), with.data()) != 0)
43c71fad 752 return false;
0b661a5c
DK
753 line = APT::String::Strip(line.substr(with.length()));
754 return true;
755}
756 /*}}}*/
757static bool ReadFlag(unsigned int &flags, std::string &line, APT::StringView const name, unsigned int const setflag)/*{{{*/
758{
759 if (LineStartsWithAndStrip(line, name) == false)
760 return false;
761 if (localStringToBool(line, false))
43c71fad
DK
762 flags |= setflag;
763 else
764 flags &= ~setflag;
765 return true;
766}
5caf35a7
DK
767 /*}}}*/
768// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
6d5bd614 769bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
43c71fad 770 std::list<std::string> &remove, unsigned int &flags)
6d5bd614 771{
40795fca
DK
772 install.clear();
773 remove.clear();
43c71fad 774 flags = 0;
6d5bd614
DK
775 std::string line;
776 while (ReadLine(input, line) == true)
777 {
778 // Skip empty lines before request
779 if (line.empty() == true)
780 continue;
781 // The first Tag must be a request, so search for it
0b661a5c 782 if (LineStartsWithAndStrip(line, "Request:"))
6d5bd614
DK
783 continue;
784
785 while (ReadLine(input, line) == true)
786 {
787 // empty lines are the end of the request
788 if (line.empty() == true)
789 return true;
790
791 std::list<std::string> *request = NULL;
0b661a5c 792 if (LineStartsWithAndStrip(line, "Install:"))
6d5bd614 793 request = &install;
0b661a5c 794 else if (LineStartsWithAndStrip(line, "Remove:"))
6d5bd614 795 request = &remove;
43c71fad
DK
796 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
797 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
798 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
799 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
800 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
801 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
802 ;
0b661a5c
DK
803 else if (LineStartsWithAndStrip(line, "Architecture:"))
804 _config->Set("APT::Architecture", line);
805 else if (LineStartsWithAndStrip(line, "Architectures:"))
806 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
807 else if (LineStartsWithAndStrip(line, "Solver:"))
eb1000f6 808 ; // purely informational line
40795fca
DK
809 else
810 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
811
6d5bd614
DK
812 if (request == NULL)
813 continue;
0b661a5c
DK
814 auto const pkgs = VectorizeString(line, ' ');
815 std::move(pkgs.begin(), pkgs.end(), std::back_inserter(*request));
6d5bd614
DK
816 }
817 }
818 return false;
43c71fad
DK
819}
820bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
821 std::list<std::string> &remove, bool &upgrade,
822 bool &distUpgrade, bool &autoRemove)
823{
824 unsigned int flags;
825 auto const ret = ReadRequest(input, install, remove, flags);
826 autoRemove = (flags & Request::AUTOREMOVE);
827 if (flags & Request::UPGRADE_ALL)
828 {
829 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
830 {
831 upgrade = true;
832 distUpgrade = false;
833 } else {
834 upgrade = false;
835 distUpgrade = false;
836 }
837 }
838 else
839 {
840 upgrade = false;
841 distUpgrade = false;
842 }
843 return ret;
6d5bd614
DK
844}
845 /*}}}*/
846// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
c3b85126 847bool EDSP::ApplyRequest(std::list<std::string> const &install,
29099cb6
DK
848 std::list<std::string> const &remove,
849 pkgDepCache &Cache)
6d5bd614
DK
850{
851 for (std::list<std::string>::const_iterator i = install.begin();
d4f626ff
DK
852 i != install.end(); ++i) {
853 pkgCache::PkgIterator P = Cache.FindPkg(*i);
854 if (P.end() == true)
855 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
856 else
857 Cache.MarkInstall(P, false);
858 }
6d5bd614
DK
859
860 for (std::list<std::string>::const_iterator i = remove.begin();
d4f626ff
DK
861 i != remove.end(); ++i) {
862 pkgCache::PkgIterator P = Cache.FindPkg(*i);
863 if (P.end() == true)
864 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
865 else
866 Cache.MarkDelete(P);
867 }
6d5bd614
DK
868 return true;
869}
870 /*}}}*/
71608330 871// EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
c3b85126 872bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
e3674d91 873{
6d5bd614 874 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
e3674d91
DK
875 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
876 {
877 if (Cache[Pkg].Delete() == true)
d4f626ff 878 {
307d9eb2 879 fprintf(output, "Remove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
d4f626ff
DK
880 if (Debug == true)
881 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
882 }
e3674d91 883 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
d4f626ff 884 {
294a8020 885 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
307d9eb2 886 fprintf(output, "Install: %d\n", _system->GetVersionMapping(CandVer->ID));
d4f626ff 887 if (Debug == true)
294a8020 888 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
d4f626ff 889 }
76d4aab0
DK
890 else if (Cache[Pkg].Garbage == true)
891 {
307d9eb2 892 fprintf(output, "Autoremove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
76d4aab0
DK
893 if (Debug == true)
894 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
76d4aab0 895 }
e3674d91
DK
896 else
897 continue;
e3674d91
DK
898 fprintf(output, "\n");
899 }
900
6d38011b 901 return true;
ef00bd7a 902}
71608330 903bool EDSP::WriteSolutionStanza(FileFd &output, char const * const Type, pkgCache::VerIterator const &Ver)
ef00bd7a 904{
4e92b116 905 bool Okay = output.Failed() == false;
71608330
DK
906 WriteOkay(Okay, output, Type, ": ", _system->GetVersionMapping(Ver->ID));
907 if (_config->FindB("Debug::EDSP::WriteSolution", false) == true)
908 WriteOkay(Okay, output, "\nPackage: ", Ver.ParentPkg().FullName(), "\nVersion: ", Ver.VerStr());
909 return WriteOkay(Okay, output, "\n\n");
6d38011b
DK
910}
911 /*}}}*/
e876223c
DK
912// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
913bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
3d17b9ff
DK
914 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
915 fprintf(output, "Percentage: %d\n", percent);
916 fprintf(output, "Message: %s\n\n", message);
917 fflush(output);
e876223c 918 return true;
ef00bd7a
DK
919}
920bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
4e92b116
DK
921 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n",
922 "Percentage: ", percent, "\n",
923 "Message: ", message, "\n\n") && output.Flush();
e876223c
DK
924}
925 /*}}}*/
ebfeeaed
DK
926// EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
927bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
928 fprintf(output, "Error: %s\n", uuid);
929 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
930 return true;
ef00bd7a
DK
931}
932bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
4e92b116
DK
933 return WriteOkay(output, "Error: ", uuid, "\n",
934 "Message: ", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n "),
935 "\n\n");
ebfeeaed
DK
936}
937 /*}}}*/
385d9f2f
DK
938static std::string findExecutable(std::vector<std::string> const &dirs, char const * const binary) {/*{{{*/
939 for (auto && dir : dirs) {
940 std::string const file = flCombine(dir, binary);
941 if (RealFileExists(file) == true)
942 return file;
943 }
944 return "";
945}
946 /*}}}*/
5caf35a7 947static pid_t ExecuteExternal(char const* const type, char const * const binary, char const * const configdir, int * const solver_in, int * const solver_out) {/*{{{*/
385d9f2f
DK
948 auto const solverDirs = _config->FindVector(configdir);
949 auto const file = findExecutable(solverDirs, binary);
950 std::string dumper;
951 {
952 dumper = findExecutable(solverDirs, "apt-dump-solver");
953 if (dumper.empty())
954 dumper = findExecutable(solverDirs, "dump");
741b7da9 955 }
ac5fbff8 956
741b7da9 957 if (file.empty() == true)
5681b3fc 958 {
5caf35a7 959 _error->Error("Can't call external %s '%s' as it is not in a configured directory!", type, binary);
5681b3fc
DK
960 return 0;
961 }
741b7da9
DK
962 int external[4] = {-1, -1, -1, -1};
963 if (pipe(external) != 0 || pipe(external + 2) != 0)
5681b3fc
DK
964 {
965 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
966 return 0;
967 }
741b7da9
DK
968 for (int i = 0; i < 4; ++i)
969 SetCloseExec(external[i], true);
ac5fbff8 970
741b7da9
DK
971 pid_t Solver = ExecFork();
972 if (Solver == 0) {
973 dup2(external[0], STDIN_FILENO);
974 dup2(external[3], STDOUT_FILENO);
385d9f2f
DK
975 auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
976 auto const dumpdir = flNotFile(dumpfile);
007d8b48
DK
977 auto const runasuser = _config->Find(std::string("APT::") + type + "::" + binary + "::RunAsUser",
978 _config->Find(std::string("APT::") + type + "::RunAsUser",
979 _config->Find("APT::Sandbox::User")));
385d9f2f
DK
980 if (dumper.empty() || dumpfile.empty() || dumper == file || CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false)
981 {
007d8b48
DK
982 _config->Set("APT::Sandbox::User", runasuser);
983 DropPrivileges();
385d9f2f
DK
984 char const * const calling[] = { file.c_str(), nullptr };
985 execv(calling[0], const_cast<char**>(calling));
986 }
987 else
988 {
007d8b48 989 char const * const calling[] = { dumper.c_str(), "--user", runasuser.c_str(), dumpfile.c_str(), file.c_str(), nullptr };
385d9f2f
DK
990 execv(calling[0], const_cast<char**>(calling));
991 }
5caf35a7 992 std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
741b7da9
DK
993 _exit(100);
994 }
995 close(external[0]);
996 close(external[3]);
ac5fbff8 997
741b7da9 998 if (WaitFd(external[1], true, 5) == false)
5681b3fc 999 {
5caf35a7 1000 _error->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type);
5681b3fc
DK
1001 return 0;
1002 }
ac5fbff8 1003
741b7da9
DK
1004 *solver_in = external[1];
1005 *solver_out = external[2];
5681b3fc 1006 return Solver;
5caf35a7
DK
1007}
1008 /*}}}*/
1009// EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1010pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
1011 return ExecuteExternal("solver", solver, "Dir::Bin::Solvers", solver_in, solver_out);
5681b3fc
DK
1012}
1013bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
1014 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
1015 return false;
1016 return true;
741b7da9
DK
1017}
1018 /*}}}*/
1019// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1020bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
43c71fad 1021 unsigned int const flags, OpProgress *Progress) {
3a487cc0
DK
1022 if (strcmp(solver, "internal") == 0)
1023 {
1024 auto const dumpfile = _config->FindFile("Dir::Log::Solver");
1025 if (dumpfile.empty())
1026 return false;
1027 auto const dumpdir = flNotFile(dumpfile);
1028 FileFd output;
1029 if (CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false ||
1030 output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false)
1031 return _error->WarningE("EDSP::Resolve", _("Could not open file '%s'"), dumpfile.c_str());
1032 bool Okay = EDSP::WriteRequest(Cache, output, flags, nullptr);
1033 return Okay && EDSP::WriteScenario(Cache, output, nullptr);
1034 }
741b7da9 1035 int solver_in, solver_out;
5681b3fc
DK
1036 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
1037 if (solver_pid == 0)
741b7da9
DK
1038 return false;
1039
ef00bd7a
DK
1040 FileFd output;
1041 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1042 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
b57c0e35 1043
4e92b116 1044 bool Okay = output.Failed() == false;
b57c0e35
DK
1045 if (Progress != NULL)
1046 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
43c71fad 1047 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
b57c0e35
DK
1048 if (Progress != NULL)
1049 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
4e92b116 1050 Okay &= EDSP::WriteScenario(Cache, output, Progress);
ef00bd7a 1051 output.Close();
741b7da9 1052
b57c0e35
DK
1053 if (Progress != NULL)
1054 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
4e92b116 1055 if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
27c69dd0 1056 return false;
741b7da9 1057
5681b3fc 1058 return ExecWait(solver_pid, solver);
43c71fad
DK
1059}
1060bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1061 bool const upgrade, bool const distUpgrade,
1062 bool const autoRemove, OpProgress *Progress) {
1063 unsigned int flags = 0;
1064 if (autoRemove)
1065 flags |= Request::AUTOREMOVE;
1066 if (upgrade)
1067 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1068 if (distUpgrade)
1069 flags |= Request::UPGRADE_ALL;
1070 return ResolveExternal(solver, Cache, flags, Progress);
ac5fbff8
DK
1071}
1072 /*}}}*/
7b197262 1073
f74d99c6 1074bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM, /*{{{*/
7b197262
DK
1075 unsigned int const flags, OpProgress * const Progress)
1076{
b4f91d4d
DK
1077 if (strcmp(solver, "internal") == 0)
1078 {
1079 auto const dumpfile = _config->FindFile("Dir::Log::Planer");
1080 if (dumpfile.empty())
1081 return false;
1082 auto const dumpdir = flNotFile(dumpfile);
1083 FileFd output;
1084 if (CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false ||
1085 output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false)
1086 return _error->WarningE("EIPP::OrderInstall", _("Could not open file '%s'"), dumpfile.c_str());
1087 bool Okay = EIPP::WriteRequest(PM->Cache, output, flags, nullptr);
1088 return Okay && EIPP::WriteScenario(PM->Cache, output, nullptr);
1089 }
1090
7b197262
DK
1091 int solver_in, solver_out;
1092 pid_t const solver_pid = ExecuteExternal("planer", solver, "Dir::Bin::Planers", &solver_in, &solver_out);
1093 if (solver_pid == 0)
1094 return false;
1095
1096 FileFd output;
1097 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
b4f91d4d 1098 return _error->Errno("EIPP::OrderInstall", "Opening planer %s stdin on fd %d for writing failed", solver, solver_in);
7b197262
DK
1099
1100 bool Okay = output.Failed() == false;
1101 if (Progress != NULL)
1102 Progress->OverallProgress(0, 100, 5, _("Execute external planer"));
f74d99c6 1103 Okay &= EIPP::WriteRequest(PM->Cache, output, flags, Progress);
7b197262
DK
1104 if (Progress != NULL)
1105 Progress->OverallProgress(5, 100, 20, _("Execute external planer"));
f74d99c6 1106 Okay &= EIPP::WriteScenario(PM->Cache, output, Progress);
7b197262
DK
1107 output.Close();
1108
1109 if (Progress != NULL)
1110 Progress->OverallProgress(25, 100, 75, _("Execute external planer"));
4cdc3bf0
DK
1111
1112 // we don't tell the external planers about boring things
1113 for (auto Pkg = PM->Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1114 {
1115 if (Pkg->CurrentState == pkgCache::State::ConfigFiles && PM->Cache[Pkg].Purge() == true)
1116 PM->Remove(Pkg, true);
1117 }
1118
f74d99c6 1119 if (Okay && EIPP::ReadResponse(solver_out, PM, Progress) == false)
7b197262
DK
1120 return false;
1121
1122 return ExecWait(solver_pid, solver);
1123}
1124 /*}}}*/
1125bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output, /*{{{*/
1126 unsigned int const flags,
1127 OpProgress * const Progress)
1128{
7b197262
DK
1129 if (Progress != NULL)
1130 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to planer"));
1131 unsigned long p = 0;
4cdc3bf0 1132 string del, inst, reinst;
7b197262
DK
1133 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
1134 {
1135 if (Progress != NULL && p % 100 == 0)
1136 Progress->Progress(p);
1137 string* req;
1138 pkgDepCache::StateCache &P = Cache[Pkg];
4cdc3bf0
DK
1139 if (P.Purge() == true && Pkg->CurrentState == pkgCache::State::ConfigFiles)
1140 continue;
7b197262
DK
1141 if (P.Delete() == true)
1142 req = &del;
f74d99c6 1143 else if (P.NewInstall() == true || P.Upgrade() == true || P.Downgrade() == true)
7b197262
DK
1144 req = &inst;
1145 else if (P.ReInstall() == true)
1146 req = &reinst;
1147 else
1148 continue;
1149 req->append(" ").append(Pkg.FullName());
1150 }
1151 bool Okay = WriteOkay(output, "Request: EIPP 0.1\n");
1152
1153 const char *arch = _config->Find("APT::Architecture").c_str();
1154 std::vector<string> archs = APT::Configuration::getArchitectures();
1155 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
1156 "Architectures:");
1157 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
1158 WriteOkay(Okay, output, " ", *a);
1159 WriteOkay(Okay, output, "\n");
1160
7b197262
DK
1161 if (del.empty() == false)
1162 WriteOkay(Okay, output, "Remove:", del, "\n");
1163 if (inst.empty() == false)
1164 WriteOkay(Okay, output, "Install:", inst, "\n");
1165 if (reinst.empty() == false)
1166 WriteOkay(Okay, output, "ReInstall:", reinst, "\n");
1167 WriteOkay(Okay, output, "Planer: ", _config->Find("APT::Planer", "internal"), "\n");
a21aca10
DK
1168 if ((flags & Request::IMMEDIATE_CONFIGURATION_ALL) != 0)
1169 WriteOkay(Okay, output, "Immediate-Configuration: yes\n");
1170 else if ((flags & Request::NO_IMMEDIATE_CONFIGURATION) != 0)
1171 WriteOkay(Okay, output, "Immediate-Configuration: no\n");
8d1cb6da
DK
1172 else if ((flags & Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0)
1173 WriteOkay(Okay, output, "Allow-Temporary-Remove-of-Essentials: yes\n");
7b197262
DK
1174 return WriteOkay(Okay, output, "\n");
1175}
1176 /*}}}*/
dae19747 1177static bool WriteScenarioEIPPVersion(pkgDepCache &, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
7b197262
DK
1178 pkgCache::VerIterator const &Ver)
1179{
1180 bool Okay = true;
1181 if (Pkg.CurrentVer() == Ver)
1182 switch (Pkg->CurrentState)
1183 {
1184 case pkgCache::State::NotInstalled: WriteOkay(Okay, output, "\nStatus: not-installed"); break;
1185 case pkgCache::State::ConfigFiles: WriteOkay(Okay, output, "\nStatus: config-files"); break;
1186 case pkgCache::State::HalfInstalled: WriteOkay(Okay, output, "\nStatus: half-installed"); break;
1187 case pkgCache::State::UnPacked: WriteOkay(Okay, output, "\nStatus: unpacked"); break;
1188 case pkgCache::State::HalfConfigured: WriteOkay(Okay, output, "\nStatus: half-configured"); break;
1189 case pkgCache::State::TriggersAwaited: WriteOkay(Okay, output, "\nStatus: triggers-awaited"); break;
1190 case pkgCache::State::TriggersPending: WriteOkay(Okay, output, "\nStatus: triggers-pending"); break;
1191 case pkgCache::State::Installed: WriteOkay(Okay, output, "\nStatus: installed"); break;
1192 }
7b197262
DK
1193 return Okay;
1194}
1195 /*}}}*/
1196// EIPP::WriteScenario - to the given file descriptor /*{{{*/
dae19747
DK
1197template<typename forVersion> void forAllInterestingVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, forVersion const &func)
1198{
1199 if (Pkg->CurrentState == pkgCache::State::NotInstalled)
1200 {
1201 auto P = Cache[Pkg];
1202 if (P.Install() == false)
1203 return;
1204 func(Pkg, P.InstVerIter(Cache));
1205 }
1206 else
1207 {
1208 if (Pkg->CurrentVer != 0)
1209 func(Pkg, Pkg.CurrentVer());
1210 auto P = Cache[Pkg];
1211 auto const V = P.InstVerIter(Cache);
1212 if (P.Delete() == false && Pkg.CurrentVer() != V)
1213 func(Pkg, V);
1214 }
1215}
1216
7b197262
DK
1217bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const Progress)
1218{
1219 if (Progress != NULL)
dae19747 1220 Progress->SubProgress(Cache.Head().PackageCount, _("Send scenario to planer"));
7b197262
DK
1221 unsigned long p = 0;
1222 bool Okay = output.Failed() == false;
1223 std::vector<std::string> archs = APT::Configuration::getArchitectures();
dae19747
DK
1224 std::vector<bool> pkgset(Cache.Head().PackageCount, false);
1225 auto const MarkVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) {
1226 pkgset[Pkg->ID] = true;
1227 for (auto D = Ver.DependsList(); D.end() == false; ++D)
7b197262 1228 {
dae19747
DK
1229 if (D.IsCritical() == false)
1230 continue;
1231 auto const P = D.TargetPkg();
1232 for (auto Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
1233 {
1234 auto const V = Prv.OwnerVer();
1235 auto const PV = V.ParentPkg();
1236 if (V == PV.CurrentVer() || V == Cache[PV].InstVerIter(Cache))
1237 pkgset[PV->ID] = true;
1238 }
1239 pkgset[P->ID] = true;
14bed2c0
DK
1240 if (strcmp(P.Arch(), "any") == 0)
1241 {
1242 APT::StringView const pkgname(P.Name());
1243 auto const idxColon = pkgname.find(':');
1244 if (idxColon != APT::StringView::npos)
1245 {
1246 pkgCache::PkgIterator PA;
1247 if (pkgname.substr(idxColon + 1) == "any")
1248 {
1249 auto const GA = Cache.FindGrp(pkgname.substr(0, idxColon).to_string());
1250 for (auto PA = GA.PackageList(); PA.end() == false; PA = GA.NextPkg(PA))
1251 {
1252 pkgset[PA->ID] = true;
1253 }
1254 }
1255 else
1256 {
1257 auto const PA = Cache.FindPkg(pkgname.to_string());
1258 if (PA.end() == false)
1259 pkgset[PA->ID] = true;
1260 }
1261 }
1262 }
1263 else
1264 {
1265 auto const PA = Cache.FindPkg(P.FullName(false), "any");
1266 if (PA.end() == false)
1267 pkgset[PA->ID] = true;
1268 }
7b197262 1269 }
dae19747
DK
1270 };
1271 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1272 forAllInterestingVersions(Cache, Pkg, MarkVersion);
1273 auto const WriteVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) {
1274 Okay &= WriteScenarioVersion(output, Pkg, Ver);
1275 Okay &= WriteScenarioEIPPVersion(Cache, output, Pkg, Ver);
1276 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, true);
1277 WriteOkay(Okay, output, "\n");
1278 if (Progress != NULL && p % 100 == 0)
1279 Progress->Progress(p);
1280 };
1281 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
1282 {
1283 if (pkgset[Pkg->ID] == false || Pkg->VersionList == 0)
1284 continue;
1285 forAllInterestingVersions(Cache, Pkg, WriteVersion);
7b197262
DK
1286 }
1287 return true;
1288}
1289 /*}}}*/
1290// EIPP::ReadResponse - from the given file descriptor /*{{{*/
f74d99c6 1291bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, OpProgress *Progress) {
7b197262
DK
1292 /* We build an map id to mmap offset here
1293 In theory we could use the offset as ID, but then VersionCount
1294 couldn't be used to create other versionmappings anymore and it
1295 would be too easy for a (buggy) solver to segfault APT… */
f74d99c6 1296 unsigned long long const VersionCount = PM->Cache.Head().VersionCount;
7b197262 1297 unsigned long VerIdx[VersionCount];
f74d99c6 1298 for (pkgCache::PkgIterator P = PM->Cache.PkgBegin(); P.end() == false; ++P) {
7b197262
DK
1299 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
1300 VerIdx[V->ID] = V.Index();
1301 }
7b197262
DK
1302
1303 FileFd in;
1304 in.OpenDescriptor(input, FileFd::ReadOnly);
1305 pkgTagFile response(&in, 100);
1306 pkgTagSection section;
1307
7b197262 1308 while (response.Step(section) == true) {
f74d99c6 1309 char const * type = nullptr;
7b197262
DK
1310 if (section.Exists("Progress") == true) {
1311 if (Progress != NULL) {
1312 string msg = section.FindS("Message");
1313 if (msg.empty() == true)
1314 msg = _("Prepare for receiving solution");
1315 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
1316 }
1317 continue;
1318 } else if (section.Exists("Error") == true) {
1319 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
1320 if (msg.empty() == true) {
1321 msg = _("External planer failed without a proper error message");
1322 _error->Error("%s", msg.c_str());
1323 } else
1324 _error->Error("External planer failed with: %s", msg.substr(0,msg.find('\n')).c_str());
1325 if (Progress != NULL)
1326 Progress->Done();
1327 std::cerr << "The planer encountered an error of type: " << section.FindS("Error") << std::endl;
1328 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
1329 std::cerr << msg << std::endl << std::endl;
1330 return false;
262fdd8b
DK
1331 } else if (section.Exists("Unpack") == true)
1332 type = "Unpack";
f74d99c6
DK
1333 else if (section.Exists("Configure") == true)
1334 type = "Configure";
1335 else if (section.Exists("Remove") == true)
1336 type = "Remove";
1337 else {
1338 char const *Start, *End;
1339 section.GetSection(Start, End);
1340 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
1341 continue;
1342 }
1343
1344 if (type == nullptr)
1345 continue;
1346 size_t const id = section.FindULL(type, VersionCount);
1347 if (id == VersionCount) {
1348 _error->Warning("Unable to parse %s request with id value '%s'!", type, section.FindS(type).c_str());
1349 continue;
1350 } else if (id > PM->Cache.Head().VersionCount) {
1351 _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type).c_str(), type);
1352 continue;
1353 }
1354
1355 pkgCache::VerIterator Ver(PM->Cache.GetCache(), PM->Cache.GetCache().VerP + VerIdx[id]);
1356 auto const Pkg = Ver.ParentPkg();
262fdd8b 1357 if (strcmp(type, "Unpack") == 0)
f74d99c6
DK
1358 PM->Install(Pkg, PM->FileNames[Pkg->ID]);
1359 else if (strcmp(type, "Configure") == 0)
1360 PM->Configure(Pkg);
1361 else if (strcmp(type, "Remove") == 0)
1362 PM->Remove(Pkg, PM->Cache[Pkg].Purge());
1363 }
1364 return true;
1365}
1366 /*}}}*/
1367bool EIPP::ReadRequest(int const input, std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
1368 unsigned int &flags)
1369{
1370 actions.clear();
1371 flags = 0;
1372 std::string line;
1373 while (ReadLine(input, line) == true)
1374 {
1375 // Skip empty lines before request
1376 if (line.empty() == true)
1377 continue;
1378 // The first Tag must be a request, so search for it
1379 if (line.compare(0, 8, "Request:") != 0)
1380 continue;
1381
1382 while (ReadLine(input, line) == true)
1383 {
1384 // empty lines are the end of the request
1385 if (line.empty() == true)
1386 return true;
1387
1388 PKG_ACTION pkgact = PKG_ACTION::NOOP;
1389 if (LineStartsWithAndStrip(line, "Install:"))
1390 pkgact = PKG_ACTION::INSTALL;
1391 else if (LineStartsWithAndStrip(line, "ReInstall:"))
1392 pkgact = PKG_ACTION::REINSTALL;
1393 else if (LineStartsWithAndStrip(line, "Remove:"))
1394 pkgact = PKG_ACTION::REMOVE;
1395 else if (LineStartsWithAndStrip(line, "Architecture:"))
1396 _config->Set("APT::Architecture", line);
1397 else if (LineStartsWithAndStrip(line, "Architectures:"))
1398 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
1399 else if (LineStartsWithAndStrip(line, "Planer:"))
1400 ; // purely informational line
a21aca10
DK
1401 else if (LineStartsWithAndStrip(line, "Immediate-Configuration:"))
1402 {
1403 if (localStringToBool(line, true))
1404 flags |= Request::IMMEDIATE_CONFIGURATION_ALL;
1405 else
1406 flags |= Request::NO_IMMEDIATE_CONFIGURATION;
1407 }
8d1cb6da
DK
1408 else if (ReadFlag(flags, line, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS))
1409 ;
f74d99c6
DK
1410 else
1411 _error->Warning("Unknown line in EIPP Request stanza: %s", line.c_str());
1412
1413 if (pkgact == PKG_ACTION::NOOP)
1414 continue;
1415 for (auto && p: VectorizeString(line, ' '))
1416 actions.emplace_back(std::move(p), pkgact);
1417 }
1418 }
1419 return false;
1420
1421
1422 return false;
1423}
1424 /*}}}*/
1425bool EIPP::ApplyRequest(std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
1426 pkgDepCache &Cache)
1427{
1428 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1429 {
1430 short versions = 0;
1431 for (auto Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
1432 {
1433 ++versions;
1434 if (Pkg.CurrentVer() == Ver)
1435 continue;
1436 Cache.SetCandidateVersion(Ver);
1437 }
1438 if (unlikely(versions > 2))
1439 _error->Warning("Package %s has %d versions, but should have at most 2!", Pkg.FullName().c_str(), versions);
1440 }
1441 for (auto && a: actions)
1442 {
1443 pkgCache::PkgIterator P = Cache.FindPkg(a.first);
1444 if (P.end() == true)
1445 {
1446 _error->Warning("Package %s is not known, so can't be acted on", a.first.c_str());
1447 continue;
1448 }
1449 switch (a.second)
1450 {
1451 case PKG_ACTION::NOOP:
1452 _error->Warning("Package %s has NOOP as action?!?", a.first.c_str());
1453 break;
1454 case PKG_ACTION::INSTALL:
1455 Cache.MarkInstall(P, false);
1456 break;
1457 case PKG_ACTION::REINSTALL:
1458 Cache.MarkInstall(P, false);
1459 Cache.SetReInstall(P, true);
1460 break;
1461 case PKG_ACTION::REMOVE:
1462 Cache.MarkDelete(P);
1463 break;
7b197262
DK
1464 }
1465 }
1466 return true;
1467}
1468 /*}}}*/