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