]> git.saurik.com Git - apt.git/blame - apt-pkg/edsp.cc
if the FileFd failed already following calls should fail, too
[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) {
27c69dd0
DK
651 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
652 if (msg.empty() == true) {
653 msg = _("External solver failed without a proper error message");
a1e68c33 654 _error->Error("%s", msg.c_str());
27c69dd0
DK
655 } else
656 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
657 if (Progress != NULL)
658 Progress->Done();
ebfeeaed
DK
659 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
660 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
27c69dd0
DK
661 std::cerr << msg << std::endl << std::endl;
662 return false;
76d4aab0
DK
663 } else if (section.Exists("Autoremove") == true)
664 type = "Autoremove";
f74d99c6
DK
665 else {
666 char const *Start, *End;
667 section.GetSection(Start, End);
668 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
2029276f 669 continue;
f74d99c6 670 }
29099cb6 671
2a33cb16
DK
672 size_t const id = section.FindULL(type.c_str(), VersionCount);
673 if (id == VersionCount) {
69a78835
DK
674 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
675 continue;
2a33cb16
DK
676 } else if (id > Cache.Head().VersionCount) {
677 _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());
678 continue;
69a78835 679 }
2029276f 680
2a33cb16 681 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
90e7fba4
DK
682 auto const Pkg = Ver.ParentPkg();
683 if (type == "Autoremove") {
684 Cache[Pkg].Marked = false;
685 Cache[Pkg].Garbage = true;
686 } else if (seenOnce.emplace(Pkg->ID).second == false) {
687 _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());
688 } else if (type == "Install") {
689 if (Pkg.CurrentVer() == Ver) {
690 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
691 Ver.VerStr(), Pkg.FullName(false).c_str());
692 } else {
693 Cache.SetCandidateVersion(Ver);
694 Cache.MarkInstall(Pkg, false, 0, false);
695 }
696 } else if (type == "Remove") {
697 if (Pkg->CurrentVer == 0)
698 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
699 Ver.VerStr(), Pkg.FullName(false).c_str());
700 else if (Pkg.CurrentVer() != Ver)
701 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
702 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
703 else
704 Cache.MarkDelete(Ver.ParentPkg(), false);
76d4aab0 705 }
2029276f
DK
706 }
707 return true;
708}
709 /*}}}*/
c8a4ce6c 710// ReadLine - first line from the given file descriptor /*{{{*/
6d5bd614
DK
711// ---------------------------------------------------------------------
712/* Little helper method to read a complete line into a string. Similar to
713 fgets but we need to use the low-level read() here as otherwise the
714 listparser will be confused later on as mixing of fgets and read isn't
2029276f 715 a supported action according to the manpages and results are undefined */
c8a4ce6c 716static bool ReadLine(int const input, std::string &line) {
6d5bd614
DK
717 char one;
718 ssize_t data = 0;
719 line.erase();
720 line.reserve(100);
721 while ((data = read(input, &one, sizeof(one))) != -1) {
722 if (data != 1)
723 continue;
724 if (one == '\n')
725 return true;
726 if (one == '\r')
727 continue;
728 if (line.empty() == true && isblank(one) != 0)
729 continue;
730 line += one;
731 }
732 return false;
733}
734 /*}}}*/
c8a4ce6c 735// StringToBool - convert yes/no to bool /*{{{*/
40795fca
DK
736// ---------------------------------------------------------------------
737/* we are not as lazy as we are in the global StringToBool as we really
0b661a5c
DK
738 only accept yes/no here */
739static bool localStringToBool(std::string answer, bool const defValue) {
740 std::transform(answer.begin(), answer.end(), answer.begin(), ::tolower);
741 if (answer == "yes")
40795fca 742 return true;
0b661a5c 743 else if (answer == "no")
40795fca
DK
744 return false;
745 else
0b661a5c 746 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer.c_str());
40795fca
DK
747 return defValue;
748}
749 /*}}}*/
0b661a5c 750static bool LineStartsWithAndStrip(std::string &line, APT::StringView const with)/*{{{*/
43c71fad 751{
0b661a5c 752 if (line.compare(0, with.size(), with.data()) != 0)
43c71fad 753 return false;
0b661a5c
DK
754 line = APT::String::Strip(line.substr(with.length()));
755 return true;
756}
757 /*}}}*/
758static bool ReadFlag(unsigned int &flags, std::string &line, APT::StringView const name, unsigned int const setflag)/*{{{*/
759{
760 if (LineStartsWithAndStrip(line, name) == false)
761 return false;
762 if (localStringToBool(line, false))
43c71fad
DK
763 flags |= setflag;
764 else
765 flags &= ~setflag;
766 return true;
767}
5caf35a7
DK
768 /*}}}*/
769// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
6d5bd614 770bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
43c71fad 771 std::list<std::string> &remove, unsigned int &flags)
6d5bd614 772{
40795fca
DK
773 install.clear();
774 remove.clear();
43c71fad 775 flags = 0;
6d5bd614
DK
776 std::string line;
777 while (ReadLine(input, line) == true)
778 {
779 // Skip empty lines before request
780 if (line.empty() == true)
781 continue;
782 // The first Tag must be a request, so search for it
0b661a5c 783 if (LineStartsWithAndStrip(line, "Request:"))
6d5bd614
DK
784 continue;
785
786 while (ReadLine(input, line) == true)
787 {
788 // empty lines are the end of the request
789 if (line.empty() == true)
790 return true;
791
792 std::list<std::string> *request = NULL;
0b661a5c 793 if (LineStartsWithAndStrip(line, "Install:"))
6d5bd614 794 request = &install;
0b661a5c 795 else if (LineStartsWithAndStrip(line, "Remove:"))
6d5bd614 796 request = &remove;
43c71fad
DK
797 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
798 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
799 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
800 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
801 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
802 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
803 ;
0b661a5c
DK
804 else if (LineStartsWithAndStrip(line, "Architecture:"))
805 _config->Set("APT::Architecture", line);
806 else if (LineStartsWithAndStrip(line, "Architectures:"))
807 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
808 else if (LineStartsWithAndStrip(line, "Solver:"))
eb1000f6 809 ; // purely informational line
40795fca
DK
810 else
811 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
812
6d5bd614
DK
813 if (request == NULL)
814 continue;
0b661a5c
DK
815 auto const pkgs = VectorizeString(line, ' ');
816 std::move(pkgs.begin(), pkgs.end(), std::back_inserter(*request));
6d5bd614
DK
817 }
818 }
819 return false;
43c71fad
DK
820}
821bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
822 std::list<std::string> &remove, bool &upgrade,
823 bool &distUpgrade, bool &autoRemove)
824{
825 unsigned int flags;
826 auto const ret = ReadRequest(input, install, remove, flags);
827 autoRemove = (flags & Request::AUTOREMOVE);
828 if (flags & Request::UPGRADE_ALL)
829 {
830 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
831 {
832 upgrade = true;
833 distUpgrade = false;
834 } else {
835 upgrade = false;
836 distUpgrade = false;
837 }
838 }
839 else
840 {
841 upgrade = false;
842 distUpgrade = false;
843 }
844 return ret;
6d5bd614
DK
845}
846 /*}}}*/
847// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
c3b85126 848bool EDSP::ApplyRequest(std::list<std::string> const &install,
29099cb6
DK
849 std::list<std::string> const &remove,
850 pkgDepCache &Cache)
6d5bd614
DK
851{
852 for (std::list<std::string>::const_iterator i = install.begin();
d4f626ff
DK
853 i != install.end(); ++i) {
854 pkgCache::PkgIterator P = Cache.FindPkg(*i);
855 if (P.end() == true)
856 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
857 else
858 Cache.MarkInstall(P, false);
859 }
6d5bd614
DK
860
861 for (std::list<std::string>::const_iterator i = remove.begin();
d4f626ff
DK
862 i != remove.end(); ++i) {
863 pkgCache::PkgIterator P = Cache.FindPkg(*i);
864 if (P.end() == true)
865 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
866 else
867 Cache.MarkDelete(P);
868 }
6d5bd614
DK
869 return true;
870}
871 /*}}}*/
71608330 872// EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
c3b85126 873bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
e3674d91 874{
6d5bd614 875 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
e3674d91
DK
876 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
877 {
878 if (Cache[Pkg].Delete() == true)
d4f626ff 879 {
307d9eb2 880 fprintf(output, "Remove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
d4f626ff
DK
881 if (Debug == true)
882 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
883 }
e3674d91 884 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
d4f626ff 885 {
294a8020 886 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
307d9eb2 887 fprintf(output, "Install: %d\n", _system->GetVersionMapping(CandVer->ID));
d4f626ff 888 if (Debug == true)
294a8020 889 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
d4f626ff 890 }
76d4aab0
DK
891 else if (Cache[Pkg].Garbage == true)
892 {
307d9eb2 893 fprintf(output, "Autoremove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
76d4aab0
DK
894 if (Debug == true)
895 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
76d4aab0 896 }
e3674d91
DK
897 else
898 continue;
e3674d91
DK
899 fprintf(output, "\n");
900 }
901
6d38011b 902 return true;
ef00bd7a 903}
71608330 904bool EDSP::WriteSolutionStanza(FileFd &output, char const * const Type, pkgCache::VerIterator const &Ver)
ef00bd7a 905{
4e92b116 906 bool Okay = output.Failed() == false;
71608330
DK
907 WriteOkay(Okay, output, Type, ": ", _system->GetVersionMapping(Ver->ID));
908 if (_config->FindB("Debug::EDSP::WriteSolution", false) == true)
909 WriteOkay(Okay, output, "\nPackage: ", Ver.ParentPkg().FullName(), "\nVersion: ", Ver.VerStr());
910 return WriteOkay(Okay, output, "\n\n");
6d38011b
DK
911}
912 /*}}}*/
e876223c
DK
913// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
914bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
0b45b6e5 915 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL), true).c_str());
3d17b9ff
DK
916 fprintf(output, "Percentage: %d\n", percent);
917 fprintf(output, "Message: %s\n\n", message);
918 fflush(output);
e876223c 919 return true;
ef00bd7a
DK
920}
921bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
0b45b6e5 922 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL), true), "\n",
4e92b116
DK
923 "Percentage: ", percent, "\n",
924 "Message: ", message, "\n\n") && output.Flush();
e876223c
DK
925}
926 /*}}}*/
ebfeeaed
DK
927// EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
928bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
929 fprintf(output, "Error: %s\n", uuid);
930 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
931 return true;
ef00bd7a
DK
932}
933bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
4e92b116
DK
934 return WriteOkay(output, "Error: ", uuid, "\n",
935 "Message: ", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n "),
936 "\n\n");
ebfeeaed
DK
937}
938 /*}}}*/
385d9f2f
DK
939static std::string findExecutable(std::vector<std::string> const &dirs, char const * const binary) {/*{{{*/
940 for (auto && dir : dirs) {
941 std::string const file = flCombine(dir, binary);
942 if (RealFileExists(file) == true)
943 return file;
944 }
945 return "";
946}
947 /*}}}*/
5caf35a7 948static 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
949 auto const solverDirs = _config->FindVector(configdir);
950 auto const file = findExecutable(solverDirs, binary);
951 std::string dumper;
952 {
953 dumper = findExecutable(solverDirs, "apt-dump-solver");
954 if (dumper.empty())
955 dumper = findExecutable(solverDirs, "dump");
741b7da9 956 }
ac5fbff8 957
741b7da9 958 if (file.empty() == true)
5681b3fc 959 {
5caf35a7 960 _error->Error("Can't call external %s '%s' as it is not in a configured directory!", type, binary);
5681b3fc
DK
961 return 0;
962 }
741b7da9
DK
963 int external[4] = {-1, -1, -1, -1};
964 if (pipe(external) != 0 || pipe(external + 2) != 0)
5681b3fc
DK
965 {
966 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
967 return 0;
968 }
741b7da9
DK
969 for (int i = 0; i < 4; ++i)
970 SetCloseExec(external[i], true);
ac5fbff8 971
741b7da9
DK
972 pid_t Solver = ExecFork();
973 if (Solver == 0) {
974 dup2(external[0], STDIN_FILENO);
975 dup2(external[3], STDOUT_FILENO);
385d9f2f
DK
976 auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
977 auto const dumpdir = flNotFile(dumpfile);
007d8b48
DK
978 auto const runasuser = _config->Find(std::string("APT::") + type + "::" + binary + "::RunAsUser",
979 _config->Find(std::string("APT::") + type + "::RunAsUser",
980 _config->Find("APT::Sandbox::User")));
385d9f2f
DK
981 if (dumper.empty() || dumpfile.empty() || dumper == file || CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false)
982 {
007d8b48
DK
983 _config->Set("APT::Sandbox::User", runasuser);
984 DropPrivileges();
385d9f2f
DK
985 char const * const calling[] = { file.c_str(), nullptr };
986 execv(calling[0], const_cast<char**>(calling));
987 }
988 else
989 {
007d8b48 990 char const * const calling[] = { dumper.c_str(), "--user", runasuser.c_str(), dumpfile.c_str(), file.c_str(), nullptr };
385d9f2f
DK
991 execv(calling[0], const_cast<char**>(calling));
992 }
5caf35a7 993 std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
741b7da9
DK
994 _exit(100);
995 }
996 close(external[0]);
997 close(external[3]);
ac5fbff8 998
741b7da9 999 if (WaitFd(external[1], true, 5) == false)
5681b3fc 1000 {
5caf35a7 1001 _error->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type);
5681b3fc
DK
1002 return 0;
1003 }
ac5fbff8 1004
741b7da9
DK
1005 *solver_in = external[1];
1006 *solver_out = external[2];
5681b3fc 1007 return Solver;
5caf35a7
DK
1008}
1009 /*}}}*/
1010// EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1011pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
1012 return ExecuteExternal("solver", solver, "Dir::Bin::Solvers", solver_in, solver_out);
5681b3fc
DK
1013}
1014bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
1015 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
1016 return false;
1017 return true;
741b7da9
DK
1018}
1019 /*}}}*/
4a97f759
DK
1020static bool CreateDumpFile(char const * const id, char const * const type, FileFd &output)/*{{{*/
1021{
1022 auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
1023 if (dumpfile.empty())
1024 return false;
1025 auto const dumpdir = flNotFile(dumpfile);
1026 _error->PushToStack();
1027 bool errored_out = CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false ||
1028 output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false;
1029 std::vector<std::string> downgrademsgs;
1030 while (_error->empty() == false)
1031 {
1032 std::string msg;
1033 _error->PopMessage(msg);
1034 downgrademsgs.emplace_back(std::move(msg));
1035 }
1036 _error->RevertToStack();
1037 for (auto && msg : downgrademsgs)
1038 _error->Warning("%s", msg.c_str());
1039 if (errored_out)
1040 return _error->WarningE(id, _("Could not open file '%s'"), dumpfile.c_str());
1041 return true;
1042}
1043 /*}}}*/
741b7da9
DK
1044// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1045bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
43c71fad 1046 unsigned int const flags, OpProgress *Progress) {
3a487cc0
DK
1047 if (strcmp(solver, "internal") == 0)
1048 {
3a487cc0 1049 FileFd output;
4a97f759
DK
1050 bool Okay = CreateDumpFile("EDSP::Resolve", "solver", output);
1051 Okay &= EDSP::WriteRequest(Cache, output, flags, nullptr);
3a487cc0
DK
1052 return Okay && EDSP::WriteScenario(Cache, output, nullptr);
1053 }
741b7da9 1054 int solver_in, solver_out;
5681b3fc
DK
1055 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
1056 if (solver_pid == 0)
741b7da9
DK
1057 return false;
1058
ef00bd7a
DK
1059 FileFd output;
1060 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1061 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
b57c0e35 1062
4e92b116 1063 bool Okay = output.Failed() == false;
b57c0e35
DK
1064 if (Progress != NULL)
1065 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
43c71fad 1066 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
b57c0e35
DK
1067 if (Progress != NULL)
1068 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
4e92b116 1069 Okay &= EDSP::WriteScenario(Cache, output, Progress);
ef00bd7a 1070 output.Close();
741b7da9 1071
b57c0e35
DK
1072 if (Progress != NULL)
1073 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
4e92b116 1074 if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
27c69dd0 1075 return false;
741b7da9 1076
5681b3fc 1077 return ExecWait(solver_pid, solver);
43c71fad
DK
1078}
1079bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1080 bool const upgrade, bool const distUpgrade,
1081 bool const autoRemove, OpProgress *Progress) {
1082 unsigned int flags = 0;
1083 if (autoRemove)
1084 flags |= Request::AUTOREMOVE;
1085 if (upgrade)
1086 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1087 if (distUpgrade)
1088 flags |= Request::UPGRADE_ALL;
1089 return ResolveExternal(solver, Cache, flags, Progress);
ac5fbff8
DK
1090}
1091 /*}}}*/
7b197262 1092
f74d99c6 1093bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM, /*{{{*/
7b197262
DK
1094 unsigned int const flags, OpProgress * const Progress)
1095{
b4f91d4d
DK
1096 if (strcmp(solver, "internal") == 0)
1097 {
b4f91d4d 1098 FileFd output;
1b50bba6 1099 _error->PushToStack();
4a97f759
DK
1100 bool Okay = CreateDumpFile("EIPP::OrderInstall", "planner", output);
1101 if (Okay == false && dynamic_cast<pkgSimulate*>(PM) != nullptr)
1b50bba6 1102 {
4a97f759
DK
1103 _error->RevertToStack();
1104 return false;
1b50bba6 1105 }
4a97f759
DK
1106 _error->MergeWithStack();
1107 Okay &= EIPP::WriteRequest(PM->Cache, output, flags, nullptr);
b4f91d4d
DK
1108 return Okay && EIPP::WriteScenario(PM->Cache, output, nullptr);
1109 }
1110
7b197262 1111 int solver_in, solver_out;
8e99b22c 1112 pid_t const solver_pid = ExecuteExternal("planner", solver, "Dir::Bin::Planners", &solver_in, &solver_out);
7b197262
DK
1113 if (solver_pid == 0)
1114 return false;
1115
1116 FileFd output;
1117 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
8e99b22c 1118 return _error->Errno("EIPP::OrderInstall", "Opening planner %s stdin on fd %d for writing failed", solver, solver_in);
7b197262
DK
1119
1120 bool Okay = output.Failed() == false;
1121 if (Progress != NULL)
8e99b22c 1122 Progress->OverallProgress(0, 100, 5, _("Execute external planner"));
f74d99c6 1123 Okay &= EIPP::WriteRequest(PM->Cache, output, flags, Progress);
7b197262 1124 if (Progress != NULL)
8e99b22c 1125 Progress->OverallProgress(5, 100, 20, _("Execute external planner"));
f74d99c6 1126 Okay &= EIPP::WriteScenario(PM->Cache, output, Progress);
7b197262
DK
1127 output.Close();
1128
1129 if (Progress != NULL)
8e99b22c 1130 Progress->OverallProgress(25, 100, 75, _("Execute external planner"));
4cdc3bf0 1131
8e99b22c 1132 // we don't tell the external planners about boring things
4cdc3bf0
DK
1133 for (auto Pkg = PM->Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1134 {
1135 if (Pkg->CurrentState == pkgCache::State::ConfigFiles && PM->Cache[Pkg].Purge() == true)
1136 PM->Remove(Pkg, true);
1137 }
1138
f74d99c6 1139 if (Okay && EIPP::ReadResponse(solver_out, PM, Progress) == false)
7b197262
DK
1140 return false;
1141
1142 return ExecWait(solver_pid, solver);
1143}
1144 /*}}}*/
1145bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output, /*{{{*/
1146 unsigned int const flags,
1147 OpProgress * const Progress)
1148{
7b197262 1149 if (Progress != NULL)
8e99b22c 1150 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to planner"));
7b197262 1151 unsigned long p = 0;
4cdc3bf0 1152 string del, inst, reinst;
7b197262
DK
1153 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
1154 {
1155 if (Progress != NULL && p % 100 == 0)
1156 Progress->Progress(p);
1157 string* req;
1158 pkgDepCache::StateCache &P = Cache[Pkg];
4cdc3bf0
DK
1159 if (P.Purge() == true && Pkg->CurrentState == pkgCache::State::ConfigFiles)
1160 continue;
7b197262
DK
1161 if (P.Delete() == true)
1162 req = &del;
f74d99c6 1163 else if (P.NewInstall() == true || P.Upgrade() == true || P.Downgrade() == true)
7b197262
DK
1164 req = &inst;
1165 else if (P.ReInstall() == true)
1166 req = &reinst;
1167 else
1168 continue;
1169 req->append(" ").append(Pkg.FullName());
1170 }
1171 bool Okay = WriteOkay(output, "Request: EIPP 0.1\n");
1172
1173 const char *arch = _config->Find("APT::Architecture").c_str();
1174 std::vector<string> archs = APT::Configuration::getArchitectures();
1175 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
1176 "Architectures:");
1177 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
1178 WriteOkay(Okay, output, " ", *a);
1179 WriteOkay(Okay, output, "\n");
1180
7b197262
DK
1181 if (del.empty() == false)
1182 WriteOkay(Okay, output, "Remove:", del, "\n");
1183 if (inst.empty() == false)
1184 WriteOkay(Okay, output, "Install:", inst, "\n");
1185 if (reinst.empty() == false)
1186 WriteOkay(Okay, output, "ReInstall:", reinst, "\n");
8e99b22c 1187 WriteOkay(Okay, output, "Planner: ", _config->Find("APT::Planner", "internal"), "\n");
a21aca10
DK
1188 if ((flags & Request::IMMEDIATE_CONFIGURATION_ALL) != 0)
1189 WriteOkay(Okay, output, "Immediate-Configuration: yes\n");
1190 else if ((flags & Request::NO_IMMEDIATE_CONFIGURATION) != 0)
1191 WriteOkay(Okay, output, "Immediate-Configuration: no\n");
8d1cb6da
DK
1192 else if ((flags & Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0)
1193 WriteOkay(Okay, output, "Allow-Temporary-Remove-of-Essentials: yes\n");
7b197262
DK
1194 return WriteOkay(Okay, output, "\n");
1195}
1196 /*}}}*/
dae19747 1197static bool WriteScenarioEIPPVersion(pkgDepCache &, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
7b197262
DK
1198 pkgCache::VerIterator const &Ver)
1199{
1200 bool Okay = true;
1201 if (Pkg.CurrentVer() == Ver)
1202 switch (Pkg->CurrentState)
1203 {
1204 case pkgCache::State::NotInstalled: WriteOkay(Okay, output, "\nStatus: not-installed"); break;
1205 case pkgCache::State::ConfigFiles: WriteOkay(Okay, output, "\nStatus: config-files"); break;
1206 case pkgCache::State::HalfInstalled: WriteOkay(Okay, output, "\nStatus: half-installed"); break;
1207 case pkgCache::State::UnPacked: WriteOkay(Okay, output, "\nStatus: unpacked"); break;
1208 case pkgCache::State::HalfConfigured: WriteOkay(Okay, output, "\nStatus: half-configured"); break;
1209 case pkgCache::State::TriggersAwaited: WriteOkay(Okay, output, "\nStatus: triggers-awaited"); break;
1210 case pkgCache::State::TriggersPending: WriteOkay(Okay, output, "\nStatus: triggers-pending"); break;
1211 case pkgCache::State::Installed: WriteOkay(Okay, output, "\nStatus: installed"); break;
1212 }
7b197262
DK
1213 return Okay;
1214}
1215 /*}}}*/
1216// EIPP::WriteScenario - to the given file descriptor /*{{{*/
dae19747
DK
1217template<typename forVersion> void forAllInterestingVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, forVersion const &func)
1218{
1219 if (Pkg->CurrentState == pkgCache::State::NotInstalled)
1220 {
1221 auto P = Cache[Pkg];
1222 if (P.Install() == false)
1223 return;
1224 func(Pkg, P.InstVerIter(Cache));
1225 }
1226 else
1227 {
1228 if (Pkg->CurrentVer != 0)
1229 func(Pkg, Pkg.CurrentVer());
1230 auto P = Cache[Pkg];
1231 auto const V = P.InstVerIter(Cache);
1232 if (P.Delete() == false && Pkg.CurrentVer() != V)
1233 func(Pkg, V);
1234 }
1235}
1236
7b197262
DK
1237bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const Progress)
1238{
1239 if (Progress != NULL)
8e99b22c 1240 Progress->SubProgress(Cache.Head().PackageCount, _("Send scenario to planner"));
7b197262
DK
1241 unsigned long p = 0;
1242 bool Okay = output.Failed() == false;
1243 std::vector<std::string> archs = APT::Configuration::getArchitectures();
dae19747
DK
1244 std::vector<bool> pkgset(Cache.Head().PackageCount, false);
1245 auto const MarkVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) {
1246 pkgset[Pkg->ID] = true;
1247 for (auto D = Ver.DependsList(); D.end() == false; ++D)
7b197262 1248 {
dae19747
DK
1249 if (D.IsCritical() == false)
1250 continue;
1251 auto const P = D.TargetPkg();
1252 for (auto Prv = P.ProvidesList(); Prv.end() == false; ++Prv)
1253 {
1254 auto const V = Prv.OwnerVer();
1255 auto const PV = V.ParentPkg();
1256 if (V == PV.CurrentVer() || V == Cache[PV].InstVerIter(Cache))
1257 pkgset[PV->ID] = true;
1258 }
1259 pkgset[P->ID] = true;
14bed2c0
DK
1260 if (strcmp(P.Arch(), "any") == 0)
1261 {
1262 APT::StringView const pkgname(P.Name());
1263 auto const idxColon = pkgname.find(':');
1264 if (idxColon != APT::StringView::npos)
1265 {
1266 pkgCache::PkgIterator PA;
1267 if (pkgname.substr(idxColon + 1) == "any")
1268 {
1269 auto const GA = Cache.FindGrp(pkgname.substr(0, idxColon).to_string());
1270 for (auto PA = GA.PackageList(); PA.end() == false; PA = GA.NextPkg(PA))
1271 {
1272 pkgset[PA->ID] = true;
1273 }
1274 }
1275 else
1276 {
1277 auto const PA = Cache.FindPkg(pkgname.to_string());
1278 if (PA.end() == false)
1279 pkgset[PA->ID] = true;
1280 }
1281 }
1282 }
1283 else
1284 {
1285 auto const PA = Cache.FindPkg(P.FullName(false), "any");
1286 if (PA.end() == false)
1287 pkgset[PA->ID] = true;
1288 }
7b197262 1289 }
dae19747
DK
1290 };
1291 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1292 forAllInterestingVersions(Cache, Pkg, MarkVersion);
1293 auto const WriteVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) {
1294 Okay &= WriteScenarioVersion(output, Pkg, Ver);
1295 Okay &= WriteScenarioEIPPVersion(Cache, output, Pkg, Ver);
1296 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, true);
1297 WriteOkay(Okay, output, "\n");
1298 if (Progress != NULL && p % 100 == 0)
1299 Progress->Progress(p);
1300 };
1301 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
1302 {
1303 if (pkgset[Pkg->ID] == false || Pkg->VersionList == 0)
1304 continue;
1305 forAllInterestingVersions(Cache, Pkg, WriteVersion);
7b197262 1306 }
5958c749 1307 return Okay;
7b197262
DK
1308}
1309 /*}}}*/
1310// EIPP::ReadResponse - from the given file descriptor /*{{{*/
f74d99c6 1311bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, OpProgress *Progress) {
7b197262
DK
1312 /* We build an map id to mmap offset here
1313 In theory we could use the offset as ID, but then VersionCount
1314 couldn't be used to create other versionmappings anymore and it
1315 would be too easy for a (buggy) solver to segfault APT… */
f74d99c6 1316 unsigned long long const VersionCount = PM->Cache.Head().VersionCount;
7b197262 1317 unsigned long VerIdx[VersionCount];
f74d99c6 1318 for (pkgCache::PkgIterator P = PM->Cache.PkgBegin(); P.end() == false; ++P) {
7b197262
DK
1319 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
1320 VerIdx[V->ID] = V.Index();
1321 }
7b197262
DK
1322
1323 FileFd in;
1324 in.OpenDescriptor(input, FileFd::ReadOnly);
1325 pkgTagFile response(&in, 100);
1326 pkgTagSection section;
1327
7b197262 1328 while (response.Step(section) == true) {
f74d99c6 1329 char const * type = nullptr;
7b197262
DK
1330 if (section.Exists("Progress") == true) {
1331 if (Progress != NULL) {
1332 string msg = section.FindS("Message");
1333 if (msg.empty() == true)
1334 msg = _("Prepare for receiving solution");
1335 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
1336 }
1337 continue;
1338 } else if (section.Exists("Error") == true) {
1339 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
1340 if (msg.empty() == true) {
8e99b22c 1341 msg = _("External planner failed without a proper error message");
7b197262
DK
1342 _error->Error("%s", msg.c_str());
1343 } else
8e99b22c 1344 _error->Error("External planner failed with: %s", msg.substr(0,msg.find('\n')).c_str());
7b197262
DK
1345 if (Progress != NULL)
1346 Progress->Done();
8e99b22c 1347 std::cerr << "The planner encountered an error of type: " << section.FindS("Error") << std::endl;
7b197262
DK
1348 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
1349 std::cerr << msg << std::endl << std::endl;
1350 return false;
262fdd8b
DK
1351 } else if (section.Exists("Unpack") == true)
1352 type = "Unpack";
f74d99c6
DK
1353 else if (section.Exists("Configure") == true)
1354 type = "Configure";
1355 else if (section.Exists("Remove") == true)
1356 type = "Remove";
1357 else {
1358 char const *Start, *End;
1359 section.GetSection(Start, End);
1360 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
1361 continue;
1362 }
1363
1364 if (type == nullptr)
1365 continue;
1366 size_t const id = section.FindULL(type, VersionCount);
1367 if (id == VersionCount) {
1368 _error->Warning("Unable to parse %s request with id value '%s'!", type, section.FindS(type).c_str());
1369 continue;
1370 } else if (id > PM->Cache.Head().VersionCount) {
1371 _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type).c_str(), type);
1372 continue;
1373 }
1374
1375 pkgCache::VerIterator Ver(PM->Cache.GetCache(), PM->Cache.GetCache().VerP + VerIdx[id]);
1376 auto const Pkg = Ver.ParentPkg();
262fdd8b 1377 if (strcmp(type, "Unpack") == 0)
f74d99c6
DK
1378 PM->Install(Pkg, PM->FileNames[Pkg->ID]);
1379 else if (strcmp(type, "Configure") == 0)
1380 PM->Configure(Pkg);
1381 else if (strcmp(type, "Remove") == 0)
1382 PM->Remove(Pkg, PM->Cache[Pkg].Purge());
1383 }
5958c749 1384 return in.Failed() == false;
f74d99c6
DK
1385}
1386 /*}}}*/
1387bool EIPP::ReadRequest(int const input, std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
1388 unsigned int &flags)
1389{
1390 actions.clear();
1391 flags = 0;
1392 std::string line;
1393 while (ReadLine(input, line) == true)
1394 {
1395 // Skip empty lines before request
1396 if (line.empty() == true)
1397 continue;
1398 // The first Tag must be a request, so search for it
1399 if (line.compare(0, 8, "Request:") != 0)
1400 continue;
1401
1402 while (ReadLine(input, line) == true)
1403 {
1404 // empty lines are the end of the request
1405 if (line.empty() == true)
1406 return true;
1407
1408 PKG_ACTION pkgact = PKG_ACTION::NOOP;
1409 if (LineStartsWithAndStrip(line, "Install:"))
1410 pkgact = PKG_ACTION::INSTALL;
1411 else if (LineStartsWithAndStrip(line, "ReInstall:"))
1412 pkgact = PKG_ACTION::REINSTALL;
1413 else if (LineStartsWithAndStrip(line, "Remove:"))
1414 pkgact = PKG_ACTION::REMOVE;
1415 else if (LineStartsWithAndStrip(line, "Architecture:"))
1416 _config->Set("APT::Architecture", line);
1417 else if (LineStartsWithAndStrip(line, "Architectures:"))
1418 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
8e99b22c 1419 else if (LineStartsWithAndStrip(line, "Planner:"))
f74d99c6 1420 ; // purely informational line
a21aca10
DK
1421 else if (LineStartsWithAndStrip(line, "Immediate-Configuration:"))
1422 {
1423 if (localStringToBool(line, true))
1424 flags |= Request::IMMEDIATE_CONFIGURATION_ALL;
1425 else
1426 flags |= Request::NO_IMMEDIATE_CONFIGURATION;
1427 }
8d1cb6da
DK
1428 else if (ReadFlag(flags, line, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS))
1429 ;
f74d99c6
DK
1430 else
1431 _error->Warning("Unknown line in EIPP Request stanza: %s", line.c_str());
1432
1433 if (pkgact == PKG_ACTION::NOOP)
1434 continue;
1435 for (auto && p: VectorizeString(line, ' '))
1436 actions.emplace_back(std::move(p), pkgact);
1437 }
1438 }
1439 return false;
f74d99c6
DK
1440}
1441 /*}}}*/
1442bool EIPP::ApplyRequest(std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
1443 pkgDepCache &Cache)
1444{
1445 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
1446 {
1447 short versions = 0;
1448 for (auto Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
1449 {
1450 ++versions;
1451 if (Pkg.CurrentVer() == Ver)
1452 continue;
1453 Cache.SetCandidateVersion(Ver);
1454 }
1455 if (unlikely(versions > 2))
1456 _error->Warning("Package %s has %d versions, but should have at most 2!", Pkg.FullName().c_str(), versions);
1457 }
1458 for (auto && a: actions)
1459 {
1460 pkgCache::PkgIterator P = Cache.FindPkg(a.first);
1461 if (P.end() == true)
1462 {
1463 _error->Warning("Package %s is not known, so can't be acted on", a.first.c_str());
1464 continue;
1465 }
1466 switch (a.second)
1467 {
1468 case PKG_ACTION::NOOP:
1469 _error->Warning("Package %s has NOOP as action?!?", a.first.c_str());
1470 break;
1471 case PKG_ACTION::INSTALL:
1472 Cache.MarkInstall(P, false);
1473 break;
1474 case PKG_ACTION::REINSTALL:
1475 Cache.MarkInstall(P, false);
1476 Cache.SetReInstall(P, true);
1477 break;
1478 case PKG_ACTION::REMOVE:
1479 Cache.MarkDelete(P);
1480 break;
7b197262
DK
1481 }
1482 }
1483 return true;
1484}
1485 /*}}}*/