]> git.saurik.com Git - apt.git/blame - apt-pkg/edsp.cc
don't leak EDSP solver output fd
[apt.git] / apt-pkg / edsp.cc
CommitLineData
6d38011b
DK
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
6 /*}}}*/
7// Include Files /*{{{*/
ea542140
DK
8#include <config.h>
9
6d38011b 10#include <apt-pkg/error.h>
472ff00e 11#include <apt-pkg/cacheset.h>
453b82a3
DK
12#include <apt-pkg/depcache.h>
13#include <apt-pkg/pkgcache.h>
14#include <apt-pkg/cacheiterators.h>
ac7f8f79
MV
15#include <apt-pkg/progress.h>
16#include <apt-pkg/fileutl.h>
17#include <apt-pkg/edsp.h>
18#include <apt-pkg/tagfile.h>
19#include <apt-pkg/strutl.h>
0b661a5c 20#include <apt-pkg/string_view.h>
307d9eb2 21#include <apt-pkg/pkgsystem.h>
6d38011b 22
3a487cc0 23#include <sys/stat.h>
453b82a3
DK
24#include <ctype.h>
25#include <stddef.h>
26#include <string.h>
453b82a3 27#include <unistd.h>
6d38011b 28#include <stdio.h>
a6f8c07f
DK
29
30#include <array>
453b82a3 31#include <limits>
472ff00e
DK
32#include <string>
33
ea542140 34#include <apti18n.h>
6d38011b
DK
35 /*}}}*/
36
472ff00e
DK
37using std::string;
38
d4f626ff 39// we could use pkgCache::DepType and ::Priority, but these would be localized stringsā€¦
a6f8c07f
DK
40constexpr char const * const PrioMap[] = {
41 nullptr, "important", "required", "standard",
42 "optional", "extra"
43};
44constexpr char const * const DepMap[] = {
45 nullptr, "Depends", "Pre-Depends", "Suggests",
46 "Recommends" , "Conflicts", "Replaces",
47 "Obsoletes", "Breaks", "Enhances"
48};
d4f626ff 49
4e92b116
DK
50// WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
51static bool WriteOkay_fn(FileFd &) { return true; }
52template<typename... Tail> static bool WriteOkay_fn(FileFd &output, APT::StringView data, Tail... more_data)
53{
54 return likely(output.Write(data.data(), data.length()) && WriteOkay_fn(output, more_data...));
55}
56template<typename... Tail> static bool WriteOkay_fn(FileFd &output, unsigned int data, Tail... more_data)
57{
58 std::string number;
59 strprintf(number, "%d", data);
60 return likely(output.Write(number.data(), number.length()) && WriteOkay_fn(output, more_data...));
61}
62template<typename... Data> static bool WriteOkay(bool &Okay, FileFd &output, Data&&... data)
63{
64 Okay = likely(Okay && WriteOkay_fn(output, std::forward<Data>(data)...));
65 return Okay;
66}
67template<typename... Data> static bool WriteOkay(FileFd &output, Data&&... data)
68{
69 bool Okay = likely(output.Failed() == false);
70 return WriteOkay(Okay, output, std::forward<Data>(data)...);
71}
72 /*}}}*/
c8a4ce6c
DK
73// WriteScenarioVersion /*{{{*/
74static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
d4f626ff
DK
75 pkgCache::VerIterator const &Ver)
76{
77 fprintf(output, "Package: %s\n", Pkg.Name());
a221efc3 78 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
d4f626ff
DK
79 fprintf(output, "Architecture: %s\n", Ver.Arch());
80 fprintf(output, "Version: %s\n", Ver.VerStr());
eed4639e 81 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
d4f626ff
DK
82 if (Pkg.CurrentVer() == Ver)
83 fprintf(output, "Installed: yes\n");
cbc702ea
DK
84 if (Pkg->SelectedState == pkgCache::State::Hold ||
85 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
d4f626ff
DK
86 fprintf(output, "Hold: yes\n");
87 fprintf(output, "APT-ID: %d\n", Ver->ID);
ef00bd7a
DK
88 if (PrioMap[Ver->Priority] != nullptr)
89 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
d4f626ff
DK
90 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
91 fprintf(output, "Essential: yes\n");
ef00bd7a
DK
92 if (Ver->Section != 0)
93 fprintf(output, "Section: %s\n", Ver.Section());
894d672e 94 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
d4f626ff 95 fprintf(output, "Multi-Arch: allowed\n");
894d672e 96 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
d4f626ff 97 fprintf(output, "Multi-Arch: foreign\n");
894d672e 98 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
d4f626ff 99 fprintf(output, "Multi-Arch: same\n");
b5ea5d4a
SZ
100 std::set<string> Releases;
101 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
102 pkgCache::PkgFileIterator File = I.File();
b07aeb1a 103 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
b5ea5d4a
SZ
104 string Release = File.RelStr();
105 if (!Release.empty())
106 Releases.insert(Release);
107 }
108 }
109 if (!Releases.empty()) {
110 fprintf(output, "APT-Release:\n");
111 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
112 fprintf(output, " %s\n", R->c_str());
d4f626ff 113 }
3c7567a5 114 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
294a8020 115 if (Cache.GetCandidateVersion(Pkg) == Ver)
d4f626ff
DK
116 fprintf(output, "APT-Candidate: yes\n");
117 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
118 fprintf(output, "APT-Automatic: yes\n");
ef00bd7a 119}
5caf35a7 120static bool WriteScenarioVersion(FileFd &output, pkgCache::PkgIterator const &Pkg,
ef00bd7a
DK
121 pkgCache::VerIterator const &Ver)
122{
4e92b116 123 bool Okay = WriteOkay(output, "Package: ", Pkg.Name(),
4e92b116 124 "\nArchitecture: ", Ver.Arch(),
5caf35a7 125 "\nVersion: ", Ver.VerStr());
4e92b116 126 WriteOkay(Okay, output, "\nAPT-ID: ", Ver->ID);
ef00bd7a 127 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
4e92b116 128 WriteOkay(Okay, output, "\nEssential: yes");
ef00bd7a 129 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
4e92b116 130 WriteOkay(Okay, output, "\nMulti-Arch: allowed");
ef00bd7a 131 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
4e92b116 132 WriteOkay(Okay, output, "\nMulti-Arch: foreign");
ef00bd7a 133 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
4e92b116 134 WriteOkay(Okay, output, "\nMulti-Arch: same");
ef00bd7a 135 return Okay;
d4f626ff
DK
136}
137 /*}}}*/
c8a4ce6c
DK
138// WriteScenarioDependency /*{{{*/
139static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
d4f626ff 140{
a6f8c07f 141 std::array<std::string, _count(DepMap)> dependencies;
d4f626ff
DK
142 bool orGroup = false;
143 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
144 {
8c7af4d4 145 if (Dep.IsImplicit() == true)
d4f626ff
DK
146 continue;
147 if (orGroup == false)
148 dependencies[Dep->Type].append(", ");
3addaba1 149 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
d4f626ff
DK
150 if (Dep->Version != 0)
151 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
152 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
153 {
154 dependencies[Dep->Type].append(" | ");
155 orGroup = true;
156 }
157 else
158 orGroup = false;
159 }
a6f8c07f 160 for (size_t i = 1; i < dependencies.size(); ++i)
d4f626ff
DK
161 if (dependencies[i].empty() == false)
162 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
163 string provides;
164 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
165 {
d5648746 166 if (Prv.IsMultiArchImplicit() == true)
d4f626ff 167 continue;
ef00bd7a
DK
168 if (provides.empty() == false)
169 provides.append(", ");
170 provides.append(Prv.Name());
2c53226b
DK
171 if (Prv->ProvideVersion != 0)
172 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
d4f626ff
DK
173 }
174 if (provides.empty() == false)
ef00bd7a
DK
175 fprintf(output, "Provides: %s\n", provides.c_str());
176}
5caf35a7 177static bool WriteScenarioDependency(FileFd &output, pkgCache::VerIterator const &Ver, bool const OnlyCritical)
ef00bd7a 178{
a6f8c07f 179 std::array<std::string, _count(DepMap)> dependencies;
ef00bd7a
DK
180 bool orGroup = false;
181 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
182 {
183 if (Dep.IsImplicit() == true)
184 continue;
5caf35a7
DK
185 if (OnlyCritical && Dep.IsCritical() == false)
186 continue;
ef00bd7a
DK
187 if (orGroup == false && dependencies[Dep->Type].empty() == false)
188 dependencies[Dep->Type].append(", ");
189 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
190 if (Dep->Version != 0)
191 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
192 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
193 {
194 dependencies[Dep->Type].append(" | ");
195 orGroup = true;
196 }
197 else
198 orGroup = false;
199 }
4e92b116 200 bool Okay = output.Failed() == false;
a6f8c07f 201 for (size_t i = 1; i < dependencies.size(); ++i)
ef00bd7a 202 if (dependencies[i].empty() == false)
4e92b116 203 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
ef00bd7a
DK
204 string provides;
205 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
206 {
207 if (Prv.IsMultiArchImplicit() == true)
208 continue;
209 if (provides.empty() == false)
210 provides.append(", ");
211 provides.append(Prv.Name());
212 if (Prv->ProvideVersion != 0)
213 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
214 }
215 if (provides.empty() == false)
4e92b116
DK
216 WriteOkay(Okay, output, "\nProvides: ", provides);
217 return WriteOkay(Okay, output, "\n");
d4f626ff
DK
218}
219 /*}}}*/
c8a4ce6c
DK
220// WriteScenarioLimitedDependency /*{{{*/
221static void WriteScenarioLimitedDependency(FILE* output,
d4f626ff
DK
222 pkgCache::VerIterator const &Ver,
223 APT::PackageSet const &pkgset)
224{
a6f8c07f 225 std::array<std::string, _count(DepMap)> dependencies;
d4f626ff
DK
226 bool orGroup = false;
227 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
228 {
8c7af4d4 229 if (Dep.IsImplicit() == true)
d4f626ff
DK
230 continue;
231 if (orGroup == false)
232 {
233 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
234 continue;
ef00bd7a
DK
235 if (dependencies[Dep->Type].empty() == false)
236 dependencies[Dep->Type].append(", ");
d4f626ff
DK
237 }
238 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
239 {
240 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
241 continue;
242 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
243 orGroup = false;
244 continue;
245 }
3addaba1 246 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
d4f626ff
DK
247 if (Dep->Version != 0)
248 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
249 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
250 {
251 dependencies[Dep->Type].append(" | ");
252 orGroup = true;
253 }
254 else
255 orGroup = false;
256 }
a6f8c07f 257 for (size_t i = 1; i < dependencies.size(); ++i)
d4f626ff 258 if (dependencies[i].empty() == false)
ef00bd7a 259 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str());
d4f626ff
DK
260 string provides;
261 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
262 {
d5648746 263 if (Prv.IsMultiArchImplicit() == true)
d4f626ff
DK
264 continue;
265 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
266 continue;
ef00bd7a
DK
267 if (provides.empty() == false)
268 provides.append(", ");
269 provides.append(Prv.Name());
270 if (Prv->ProvideVersion != 0)
271 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
d4f626ff
DK
272 }
273 if (provides.empty() == false)
ef00bd7a
DK
274 fprintf(output, "Provides: %s\n", provides.c_str());
275}
276static bool WriteScenarioLimitedDependency(FileFd &output,
277 pkgCache::VerIterator const &Ver,
5caf35a7
DK
278 std::vector<bool> const &pkgset,
279 bool const OnlyCritical)
ef00bd7a 280{
a6f8c07f 281 std::array<std::string, _count(DepMap)> dependencies;
ef00bd7a
DK
282 bool orGroup = false;
283 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
284 {
285 if (Dep.IsImplicit() == true)
286 continue;
5caf35a7
DK
287 if (OnlyCritical && Dep.IsCritical() == false)
288 continue;
ef00bd7a
DK
289 if (orGroup == false)
290 {
6dcae298 291 if (pkgset[Dep.TargetPkg()->ID] == false)
ef00bd7a
DK
292 continue;
293 if (dependencies[Dep->Type].empty() == false)
294 dependencies[Dep->Type].append(", ");
295 }
6dcae298 296 else if (pkgset[Dep.TargetPkg()->ID] == false)
ef00bd7a
DK
297 {
298 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
299 continue;
300 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
301 orGroup = false;
302 continue;
303 }
304 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
305 if (Dep->Version != 0)
306 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
307 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
308 {
309 dependencies[Dep->Type].append(" | ");
310 orGroup = true;
311 }
312 else
313 orGroup = false;
314 }
4e92b116 315 bool Okay = output.Failed() == false;
a6f8c07f 316 for (size_t i = 1; i < dependencies.size(); ++i)
ef00bd7a 317 if (dependencies[i].empty() == false)
4e92b116 318 WriteOkay(Okay, output, "\n", DepMap[i], ": ", dependencies[i]);
ef00bd7a
DK
319 string provides;
320 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
321 {
322 if (Prv.IsMultiArchImplicit() == true)
323 continue;
6dcae298 324 if (pkgset[Prv.ParentPkg()->ID] == false)
ef00bd7a
DK
325 continue;
326 if (provides.empty() == false)
327 provides.append(", ");
328 provides.append(Prv.Name());
329 if (Prv->ProvideVersion != 0)
330 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
331 }
332 if (provides.empty() == false)
4e92b116
DK
333 WriteOkay(Okay, output, "\nProvides: ", provides);
334 return WriteOkay(Okay, output, "\n");
d4f626ff
DK
335}
336 /*}}}*/
33190fe3
DK
337static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
338{
339 /* versions which aren't current and aren't available in
340 any "online" source file are bad, expect if they are the choosen
341 candidate: The exception is for build-dep implementation as it creates
342 such pseudo (package) versions and removes them later on again.
343 We filter out versions at all so packages in 'rc' state only available
344 in dpkg/status aren't passed to solvers as they can't be installed. */
345 if (Pkg->CurrentVer != 0)
346 return false;
347 if (Cache.GetCandidateVersion(Pkg) == Ver)
348 return false;
349 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
350 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
351 return false;
352 return true;
353}
354 /*}}}*/
5caf35a7
DK
355static bool WriteScenarioEDSPVersion(pkgDepCache &Cache, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
356 pkgCache::VerIterator const &Ver)
357{
358 bool Okay = WriteOkay(output, "\nSource: ", Ver.SourcePkgName(),
359 "\nSource-Version: ", Ver.SourceVerStr());
360 if (PrioMap[Ver->Priority] != nullptr)
361 WriteOkay(Okay, output, "\nPriority: ", PrioMap[Ver->Priority]);
362 if (Ver->Section != 0)
363 WriteOkay(Okay, output, "\nSection: ", Ver.Section());
364 if (Pkg.CurrentVer() == Ver)
365 WriteOkay(Okay, output, "\nInstalled: yes");
366 if (Pkg->SelectedState == pkgCache::State::Hold ||
367 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
368 WriteOkay(Okay, output, "\nHold: yes");
369 std::set<string> Releases;
370 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
371 pkgCache::PkgFileIterator File = I.File();
372 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
373 string Release = File.RelStr();
374 if (!Release.empty())
375 Releases.insert(Release);
376 }
377 }
378 if (!Releases.empty()) {
379 WriteOkay(Okay, output, "\nAPT-Release:");
380 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
381 WriteOkay(Okay, output, "\n ", *R);
382 }
383 WriteOkay(Okay, output, "\nAPT-Pin: ", Cache.GetPolicy().GetPriority(Ver));
384 if (Cache.GetCandidateVersion(Pkg) == Ver)
385 WriteOkay(Okay, output, "\nAPT-Candidate: yes");
386 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
387 WriteOkay(Okay, output, "\nAPT-Automatic: yes");
388 return Okay;
389}
390 /*}}}*/
c8a4ce6c
DK
391// EDSP::WriteScenario - to the given file descriptor /*{{{*/
392bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
393{
394 if (Progress != NULL)
395 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
396 unsigned long p = 0;
397 std::vector<std::string> archs = APT::Configuration::getArchitectures();
398 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
399 {
400 std::string const arch = Pkg.Arch();
401 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
402 continue;
403 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
404 {
33190fe3
DK
405 if (SkipUnavailableVersions(Cache, Pkg, Ver))
406 continue;
c8a4ce6c
DK
407 WriteScenarioVersion(Cache, output, Pkg, Ver);
408 WriteScenarioDependency(output, Ver);
409 fprintf(output, "\n");
410 if (Progress != NULL && p % 100 == 0)
411 Progress->Progress(p);
412 }
413 }
414 return true;
ef00bd7a
DK
415}
416bool EDSP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress *Progress)
417{
418 if (Progress != NULL)
419 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
420 unsigned long p = 0;
4e92b116 421 bool Okay = output.Failed() == false;
ef00bd7a 422 std::vector<std::string> archs = APT::Configuration::getArchitectures();
4e92b116 423 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg)
ef00bd7a
DK
424 {
425 std::string const arch = Pkg.Arch();
426 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
427 continue;
4e92b116 428 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver, ++p)
ef00bd7a
DK
429 {
430 if (SkipUnavailableVersions(Cache, Pkg, Ver))
431 continue;
5caf35a7
DK
432 Okay &= WriteScenarioVersion(output, Pkg, Ver);
433 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
434 Okay &= WriteScenarioDependency(output, Ver, false);
4e92b116 435 WriteOkay(Okay, output, "\n");
ef00bd7a
DK
436 if (Progress != NULL && p % 100 == 0)
437 Progress->Progress(p);
438 }
439 }
440 return true;
c8a4ce6c
DK
441}
442 /*}}}*/
443// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
444bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
445 APT::PackageSet const &pkgset,
446 OpProgress *Progress)
447{
448 if (Progress != NULL)
449 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
450 unsigned long p = 0;
451 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
452 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
453 {
33190fe3
DK
454 if (SkipUnavailableVersions(Cache, Pkg, Ver))
455 continue;
c8a4ce6c
DK
456 WriteScenarioVersion(Cache, output, Pkg, Ver);
457 WriteScenarioLimitedDependency(output, Ver, pkgset);
458 fprintf(output, "\n");
459 if (Progress != NULL && p % 100 == 0)
460 Progress->Progress(p);
461 }
462 if (Progress != NULL)
463 Progress->Done();
464 return true;
ef00bd7a
DK
465}
466bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FileFd &output,
6dcae298 467 std::vector<bool> const &pkgset,
ef00bd7a
DK
468 OpProgress *Progress)
469{
470 if (Progress != NULL)
471 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
472 unsigned long p = 0;
4e92b116 473 bool Okay = output.Failed() == false;
6dcae298
DK
474 for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p)
475 {
476 if (pkgset[Pkg->ID] == false)
477 continue;
4e92b116 478 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver)
ef00bd7a
DK
479 {
480 if (SkipUnavailableVersions(Cache, Pkg, Ver))
481 continue;
5caf35a7
DK
482 Okay &= WriteScenarioVersion(output, Pkg, Ver);
483 Okay &= WriteScenarioEDSPVersion(Cache, output, Pkg, Ver);
484 Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, false);
4e92b116 485 WriteOkay(Okay, output, "\n");
ef00bd7a
DK
486 if (Progress != NULL && p % 100 == 0)
487 Progress->Progress(p);
488 }
6dcae298 489 }
ef00bd7a
DK
490 if (Progress != NULL)
491 Progress->Done();
492 return Okay;
c8a4ce6c
DK
493}
494 /*}}}*/
c3b85126 495// EDSP::WriteRequest - to the given file descriptor /*{{{*/
93794bc9 496bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
b57c0e35
DK
497 bool const DistUpgrade, bool const AutoRemove,
498 OpProgress *Progress)
6d38011b 499{
b57c0e35
DK
500 if (Progress != NULL)
501 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
502 unsigned long p = 0;
93794bc9 503 string del, inst;
b57c0e35 504 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
6d38011b 505 {
b57c0e35
DK
506 if (Progress != NULL && p % 100 == 0)
507 Progress->Progress(p);
6d38011b 508 string* req;
036eb012
DK
509 pkgDepCache::StateCache &P = Cache[Pkg];
510 if (P.Delete() == true)
6d38011b 511 req = &del;
036eb012
DK
512 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
513 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
6d38011b 514 req = &inst;
6d38011b
DK
515 else
516 continue;
6d5bd614 517 req->append(" ").append(Pkg.FullName());
6d38011b 518 }
25252738 519 fprintf(output, "Request: EDSP 0.5\n");
caa32793
SZ
520
521 const char *arch = _config->Find("APT::Architecture").c_str();
522 std::vector<string> archs = APT::Configuration::getArchitectures();
523 fprintf(output, "Architecture: %s\n", arch);
524 fprintf(output, "Architectures:");
525 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
526 fprintf(output, " %s", a->c_str());
527 fprintf(output, "\n");
528
6d38011b 529 if (del.empty() == false)
6d5bd614 530 fprintf(output, "Remove: %s\n", del.c_str()+1);
6d38011b 531 if (inst.empty() == false)
6d5bd614 532 fprintf(output, "Install: %s\n", inst.c_str()+1);
93794bc9
DK
533 if (Upgrade == true)
534 fprintf(output, "Upgrade: yes\n");
535 if (DistUpgrade == true)
536 fprintf(output, "Dist-Upgrade: yes\n");
537 if (AutoRemove == true)
538 fprintf(output, "Autoremove: yes\n");
eb1000f6
DK
539 auto const solver = _config->Find("APT::Solver", "internal");
540 fprintf(output, "Solver: %s\n", solver.c_str());
541 auto const solverconf = std::string("APT::Solver::") + solver + "::";
542 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
93794bc9 543 fprintf(output, "Strict-Pinning: no\n");
eb1000f6
DK
544 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
545 if (solverpref.empty() == false)
546 fprintf(output, "Preferences: %s\n", solverpref.c_str());
6d5bd614 547 fprintf(output, "\n");
e3674d91 548 return true;
ef00bd7a 549}
43c71fad
DK
550bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output,
551 unsigned int const flags,
ef00bd7a
DK
552 OpProgress *Progress)
553{
554 if (Progress != NULL)
555 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
556 unsigned long p = 0;
557 string del, inst;
558 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
559 {
560 if (Progress != NULL && p % 100 == 0)
561 Progress->Progress(p);
562 string* req;
563 pkgDepCache::StateCache &P = Cache[Pkg];
564 if (P.Delete() == true)
565 req = &del;
566 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
567 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
568 req = &inst;
569 else
570 continue;
571 req->append(" ").append(Pkg.FullName());
572 }
4e92b116 573 bool Okay = WriteOkay(output, "Request: EDSP 0.5\n");
ef00bd7a
DK
574
575 const char *arch = _config->Find("APT::Architecture").c_str();
576 std::vector<string> archs = APT::Configuration::getArchitectures();
4e92b116
DK
577 WriteOkay(Okay, output, "Architecture: ", arch, "\n",
578 "Architectures:");
ef00bd7a 579 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
4e92b116
DK
580 WriteOkay(Okay, output, " ", *a);
581 WriteOkay(Okay, output, "\n");
ef00bd7a
DK
582
583 if (del.empty() == false)
4e92b116 584 WriteOkay(Okay, output, "Remove:", del, "\n");
ef00bd7a 585 if (inst.empty() == false)
4e92b116 586 WriteOkay(Okay, output, "Install:", inst, "\n");
43c71fad 587 if (flags & Request::AUTOREMOVE)
4e92b116 588 WriteOkay(Okay, output, "Autoremove: yes\n");
43c71fad
DK
589 if (flags & Request::UPGRADE_ALL)
590 {
591 WriteOkay(Okay, output, "Upgrade-All: yes\n");
592 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
593 WriteOkay(Okay, output, "Upgrade: yes\n");
594 else
595 WriteOkay(Okay, output, "Dist-Upgrade: yes\n");
596 }
597 if (flags & Request::FORBID_NEW_INSTALL)
598 WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
599 if (flags & Request::FORBID_REMOVE)
600 WriteOkay(Okay, output, "Forbid-Remove: yes\n");
385d9f2f
DK
601 auto const solver = _config->Find("APT::Solver", "internal");
602 WriteOkay(Okay, output, "Solver: ", solver, "\n");
ef00bd7a 603 if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
4e92b116 604 WriteOkay(Okay, output, "Strict-Pinning: no\n");
ef00bd7a 605 string solverpref("APT::Solver::");
385d9f2f 606 solverpref.append(solver).append("::Preferences");
ef00bd7a 607 if (_config->Exists(solverpref) == true)
4e92b116
DK
608 WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
609 return WriteOkay(Okay, output, "\n");
e3674d91
DK
610}
611 /*}}}*/
2029276f 612// EDSP::ReadResponse - from the given file descriptor /*{{{*/
b57c0e35 613bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
2a33cb16
DK
614 /* We build an map id to mmap offset here
615 In theory we could use the offset as ID, but then VersionCount
616 couldn't be used to create other versionmappings anymore and it
617 would be too easy for a (buggy) solver to segfault APTā€¦ */
618 unsigned long long const VersionCount = Cache.Head().VersionCount;
619 unsigned long VerIdx[VersionCount];
76d4aab0 620 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
2a33cb16
DK
621 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
622 VerIdx[V->ID] = V.Index();
76d4aab0
DK
623 Cache[P].Marked = true;
624 Cache[P].Garbage = false;
625 }
2a33cb16 626
c80a49f5 627 FileFd in;
2202a8a0 628 in.OpenDescriptor(input, FileFd::ReadOnly, true);
288a76d2 629 pkgTagFile response(&in, 100);
c80a49f5
DK
630 pkgTagSection section;
631
90e7fba4 632 std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
2029276f
DK
633 while (response.Step(section) == true) {
634 std::string type;
635 if (section.Exists("Install") == true)
636 type = "Install";
637 else if (section.Exists("Remove") == true)
638 type = "Remove";
e876223c 639 else if (section.Exists("Progress") == true) {
b57c0e35 640 if (Progress != NULL) {
c6660a4b 641 string msg = section.FindS("Message");
b57c0e35 642 if (msg.empty() == true)
c6660a4b
DK
643 msg = _("Prepare for receiving solution");
644 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
b57c0e35 645 }
e876223c 646 continue;
ebfeeaed 647 } else if (section.Exists("Error") == true) {
27c69dd0
DK
648 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
649 if (msg.empty() == true) {
650 msg = _("External solver failed without a proper error message");
a1e68c33 651 _error->Error("%s", msg.c_str());
27c69dd0
DK
652 } else
653 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
654 if (Progress != NULL)
655 Progress->Done();
ebfeeaed
DK
656 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
657 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
27c69dd0
DK
658 std::cerr << msg << std::endl << std::endl;
659 return false;
76d4aab0
DK
660 } else if (section.Exists("Autoremove") == true)
661 type = "Autoremove";
662 else
2029276f 663 continue;
29099cb6 664
2a33cb16
DK
665 size_t const id = section.FindULL(type.c_str(), VersionCount);
666 if (id == VersionCount) {
69a78835
DK
667 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
668 continue;
2a33cb16
DK
669 } else if (id > Cache.Head().VersionCount) {
670 _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());
671 continue;
69a78835 672 }
2029276f 673
2a33cb16 674 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
90e7fba4
DK
675 auto const Pkg = Ver.ParentPkg();
676 if (type == "Autoremove") {
677 Cache[Pkg].Marked = false;
678 Cache[Pkg].Garbage = true;
679 } else if (seenOnce.emplace(Pkg->ID).second == false) {
680 _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());
681 } else if (type == "Install") {
682 if (Pkg.CurrentVer() == Ver) {
683 _error->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
684 Ver.VerStr(), Pkg.FullName(false).c_str());
685 } else {
686 Cache.SetCandidateVersion(Ver);
687 Cache.MarkInstall(Pkg, false, 0, false);
688 }
689 } else if (type == "Remove") {
690 if (Pkg->CurrentVer == 0)
691 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
692 Ver.VerStr(), Pkg.FullName(false).c_str());
693 else if (Pkg.CurrentVer() != Ver)
694 _error->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
695 Ver.VerStr(), Pkg.FullName(false).c_str(), Pkg.CurrentVer().VerStr());
696 else
697 Cache.MarkDelete(Ver.ParentPkg(), false);
76d4aab0 698 }
2029276f
DK
699 }
700 return true;
701}
702 /*}}}*/
c8a4ce6c 703// ReadLine - first line from the given file descriptor /*{{{*/
6d5bd614
DK
704// ---------------------------------------------------------------------
705/* Little helper method to read a complete line into a string. Similar to
706 fgets but we need to use the low-level read() here as otherwise the
707 listparser will be confused later on as mixing of fgets and read isn't
2029276f 708 a supported action according to the manpages and results are undefined */
c8a4ce6c 709static bool ReadLine(int const input, std::string &line) {
6d5bd614
DK
710 char one;
711 ssize_t data = 0;
712 line.erase();
713 line.reserve(100);
714 while ((data = read(input, &one, sizeof(one))) != -1) {
715 if (data != 1)
716 continue;
717 if (one == '\n')
718 return true;
719 if (one == '\r')
720 continue;
721 if (line.empty() == true && isblank(one) != 0)
722 continue;
723 line += one;
724 }
725 return false;
726}
727 /*}}}*/
c8a4ce6c 728// StringToBool - convert yes/no to bool /*{{{*/
40795fca
DK
729// ---------------------------------------------------------------------
730/* we are not as lazy as we are in the global StringToBool as we really
0b661a5c
DK
731 only accept yes/no here */
732static bool localStringToBool(std::string answer, bool const defValue) {
733 std::transform(answer.begin(), answer.end(), answer.begin(), ::tolower);
734 if (answer == "yes")
40795fca 735 return true;
0b661a5c 736 else if (answer == "no")
40795fca
DK
737 return false;
738 else
0b661a5c 739 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer.c_str());
40795fca
DK
740 return defValue;
741}
742 /*}}}*/
0b661a5c 743static bool LineStartsWithAndStrip(std::string &line, APT::StringView const with)/*{{{*/
43c71fad 744{
0b661a5c 745 if (line.compare(0, with.size(), with.data()) != 0)
43c71fad 746 return false;
0b661a5c
DK
747 line = APT::String::Strip(line.substr(with.length()));
748 return true;
749}
750 /*}}}*/
751static bool ReadFlag(unsigned int &flags, std::string &line, APT::StringView const name, unsigned int const setflag)/*{{{*/
752{
753 if (LineStartsWithAndStrip(line, name) == false)
754 return false;
755 if (localStringToBool(line, false))
43c71fad
DK
756 flags |= setflag;
757 else
758 flags &= ~setflag;
759 return true;
760}
5caf35a7
DK
761 /*}}}*/
762// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
6d5bd614 763bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
43c71fad 764 std::list<std::string> &remove, unsigned int &flags)
6d5bd614 765{
40795fca
DK
766 install.clear();
767 remove.clear();
43c71fad 768 flags = 0;
6d5bd614
DK
769 std::string line;
770 while (ReadLine(input, line) == true)
771 {
772 // Skip empty lines before request
773 if (line.empty() == true)
774 continue;
775 // The first Tag must be a request, so search for it
0b661a5c 776 if (LineStartsWithAndStrip(line, "Request:"))
6d5bd614
DK
777 continue;
778
779 while (ReadLine(input, line) == true)
780 {
781 // empty lines are the end of the request
782 if (line.empty() == true)
783 return true;
784
785 std::list<std::string> *request = NULL;
0b661a5c 786 if (LineStartsWithAndStrip(line, "Install:"))
6d5bd614 787 request = &install;
0b661a5c 788 else if (LineStartsWithAndStrip(line, "Remove:"))
6d5bd614 789 request = &remove;
43c71fad
DK
790 else if (ReadFlag(flags, line, "Upgrade:", (Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL)) ||
791 ReadFlag(flags, line, "Dist-Upgrade:", Request::UPGRADE_ALL) ||
792 ReadFlag(flags, line, "Upgrade-All:", Request::UPGRADE_ALL) ||
793 ReadFlag(flags, line, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL) ||
794 ReadFlag(flags, line, "Forbid-Remove:", Request::FORBID_REMOVE) ||
795 ReadFlag(flags, line, "Autoremove:", Request::AUTOREMOVE))
796 ;
0b661a5c
DK
797 else if (LineStartsWithAndStrip(line, "Architecture:"))
798 _config->Set("APT::Architecture", line);
799 else if (LineStartsWithAndStrip(line, "Architectures:"))
800 _config->Set("APT::Architectures", SubstVar(line, " ", ","));
801 else if (LineStartsWithAndStrip(line, "Solver:"))
eb1000f6 802 ; // purely informational line
40795fca
DK
803 else
804 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
805
6d5bd614
DK
806 if (request == NULL)
807 continue;
0b661a5c
DK
808 auto const pkgs = VectorizeString(line, ' ');
809 std::move(pkgs.begin(), pkgs.end(), std::back_inserter(*request));
6d5bd614
DK
810 }
811 }
812 return false;
43c71fad
DK
813}
814bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
815 std::list<std::string> &remove, bool &upgrade,
816 bool &distUpgrade, bool &autoRemove)
817{
818 unsigned int flags;
819 auto const ret = ReadRequest(input, install, remove, flags);
820 autoRemove = (flags & Request::AUTOREMOVE);
821 if (flags & Request::UPGRADE_ALL)
822 {
823 if (flags & (Request::FORBID_NEW_INSTALL | Request::FORBID_REMOVE))
824 {
825 upgrade = true;
826 distUpgrade = false;
827 } else {
828 upgrade = false;
829 distUpgrade = false;
830 }
831 }
832 else
833 {
834 upgrade = false;
835 distUpgrade = false;
836 }
837 return ret;
6d5bd614
DK
838}
839 /*}}}*/
840// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
c3b85126 841bool EDSP::ApplyRequest(std::list<std::string> const &install,
29099cb6
DK
842 std::list<std::string> const &remove,
843 pkgDepCache &Cache)
6d5bd614
DK
844{
845 for (std::list<std::string>::const_iterator i = install.begin();
d4f626ff
DK
846 i != install.end(); ++i) {
847 pkgCache::PkgIterator P = Cache.FindPkg(*i);
848 if (P.end() == true)
849 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
850 else
851 Cache.MarkInstall(P, false);
852 }
6d5bd614
DK
853
854 for (std::list<std::string>::const_iterator i = remove.begin();
d4f626ff
DK
855 i != remove.end(); ++i) {
856 pkgCache::PkgIterator P = Cache.FindPkg(*i);
857 if (P.end() == true)
858 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
859 else
860 Cache.MarkDelete(P);
861 }
6d5bd614
DK
862 return true;
863}
864 /*}}}*/
71608330 865// EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
c3b85126 866bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
e3674d91 867{
6d5bd614 868 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
e3674d91
DK
869 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
870 {
871 if (Cache[Pkg].Delete() == true)
d4f626ff 872 {
307d9eb2 873 fprintf(output, "Remove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
d4f626ff
DK
874 if (Debug == true)
875 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
876 }
e3674d91 877 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
d4f626ff 878 {
294a8020 879 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
307d9eb2 880 fprintf(output, "Install: %d\n", _system->GetVersionMapping(CandVer->ID));
d4f626ff 881 if (Debug == true)
294a8020 882 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
d4f626ff 883 }
76d4aab0
DK
884 else if (Cache[Pkg].Garbage == true)
885 {
307d9eb2 886 fprintf(output, "Autoremove: %d\n", _system->GetVersionMapping(Pkg.CurrentVer()->ID));
76d4aab0
DK
887 if (Debug == true)
888 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
76d4aab0 889 }
e3674d91
DK
890 else
891 continue;
e3674d91
DK
892 fprintf(output, "\n");
893 }
894
6d38011b 895 return true;
ef00bd7a 896}
71608330 897bool EDSP::WriteSolutionStanza(FileFd &output, char const * const Type, pkgCache::VerIterator const &Ver)
ef00bd7a 898{
4e92b116 899 bool Okay = output.Failed() == false;
71608330
DK
900 WriteOkay(Okay, output, Type, ": ", _system->GetVersionMapping(Ver->ID));
901 if (_config->FindB("Debug::EDSP::WriteSolution", false) == true)
902 WriteOkay(Okay, output, "\nPackage: ", Ver.ParentPkg().FullName(), "\nVersion: ", Ver.VerStr());
903 return WriteOkay(Okay, output, "\n\n");
6d38011b
DK
904}
905 /*}}}*/
e876223c
DK
906// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
907bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
3d17b9ff
DK
908 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
909 fprintf(output, "Percentage: %d\n", percent);
910 fprintf(output, "Message: %s\n\n", message);
911 fflush(output);
e876223c 912 return true;
ef00bd7a
DK
913}
914bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) {
4e92b116
DK
915 return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n",
916 "Percentage: ", percent, "\n",
917 "Message: ", message, "\n\n") && output.Flush();
e876223c
DK
918}
919 /*}}}*/
ebfeeaed
DK
920// EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
921bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
922 fprintf(output, "Error: %s\n", uuid);
923 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
924 return true;
ef00bd7a
DK
925}
926bool EDSP::WriteError(char const * const uuid, std::string const &message, FileFd &output) {
4e92b116
DK
927 return WriteOkay(output, "Error: ", uuid, "\n",
928 "Message: ", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n "),
929 "\n\n");
ebfeeaed
DK
930}
931 /*}}}*/
385d9f2f
DK
932static std::string findExecutable(std::vector<std::string> const &dirs, char const * const binary) {/*{{{*/
933 for (auto && dir : dirs) {
934 std::string const file = flCombine(dir, binary);
935 if (RealFileExists(file) == true)
936 return file;
937 }
938 return "";
939}
940 /*}}}*/
5caf35a7 941static 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
942 auto const solverDirs = _config->FindVector(configdir);
943 auto const file = findExecutable(solverDirs, binary);
944 std::string dumper;
945 {
946 dumper = findExecutable(solverDirs, "apt-dump-solver");
947 if (dumper.empty())
948 dumper = findExecutable(solverDirs, "dump");
741b7da9 949 }
ac5fbff8 950
741b7da9 951 if (file.empty() == true)
5681b3fc 952 {
5caf35a7 953 _error->Error("Can't call external %s '%s' as it is not in a configured directory!", type, binary);
5681b3fc
DK
954 return 0;
955 }
741b7da9
DK
956 int external[4] = {-1, -1, -1, -1};
957 if (pipe(external) != 0 || pipe(external + 2) != 0)
5681b3fc
DK
958 {
959 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
960 return 0;
961 }
741b7da9
DK
962 for (int i = 0; i < 4; ++i)
963 SetCloseExec(external[i], true);
ac5fbff8 964
741b7da9
DK
965 pid_t Solver = ExecFork();
966 if (Solver == 0) {
967 dup2(external[0], STDIN_FILENO);
968 dup2(external[3], STDOUT_FILENO);
385d9f2f
DK
969 auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
970 auto const dumpdir = flNotFile(dumpfile);
007d8b48
DK
971 auto const runasuser = _config->Find(std::string("APT::") + type + "::" + binary + "::RunAsUser",
972 _config->Find(std::string("APT::") + type + "::RunAsUser",
973 _config->Find("APT::Sandbox::User")));
385d9f2f
DK
974 if (dumper.empty() || dumpfile.empty() || dumper == file || CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false)
975 {
007d8b48
DK
976 _config->Set("APT::Sandbox::User", runasuser);
977 DropPrivileges();
385d9f2f
DK
978 char const * const calling[] = { file.c_str(), nullptr };
979 execv(calling[0], const_cast<char**>(calling));
980 }
981 else
982 {
007d8b48 983 char const * const calling[] = { dumper.c_str(), "--user", runasuser.c_str(), dumpfile.c_str(), file.c_str(), nullptr };
385d9f2f
DK
984 execv(calling[0], const_cast<char**>(calling));
985 }
5caf35a7 986 std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
741b7da9
DK
987 _exit(100);
988 }
989 close(external[0]);
990 close(external[3]);
ac5fbff8 991
741b7da9 992 if (WaitFd(external[1], true, 5) == false)
5681b3fc 993 {
5caf35a7 994 _error->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type);
5681b3fc
DK
995 return 0;
996 }
ac5fbff8 997
741b7da9
DK
998 *solver_in = external[1];
999 *solver_out = external[2];
5681b3fc 1000 return Solver;
5caf35a7
DK
1001}
1002 /*}}}*/
1003// EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1004pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
1005 return ExecuteExternal("solver", solver, "Dir::Bin::Solvers", solver_in, solver_out);
5681b3fc
DK
1006}
1007bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
1008 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
1009 return false;
1010 return true;
741b7da9
DK
1011}
1012 /*}}}*/
1013// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1014bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
43c71fad 1015 unsigned int const flags, OpProgress *Progress) {
3a487cc0
DK
1016 if (strcmp(solver, "internal") == 0)
1017 {
1018 auto const dumpfile = _config->FindFile("Dir::Log::Solver");
1019 if (dumpfile.empty())
1020 return false;
1021 auto const dumpdir = flNotFile(dumpfile);
1022 FileFd output;
1023 if (CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false ||
1024 output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false)
1025 return _error->WarningE("EDSP::Resolve", _("Could not open file '%s'"), dumpfile.c_str());
1026 bool Okay = EDSP::WriteRequest(Cache, output, flags, nullptr);
1027 return Okay && EDSP::WriteScenario(Cache, output, nullptr);
1028 }
741b7da9 1029 int solver_in, solver_out;
5681b3fc
DK
1030 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
1031 if (solver_pid == 0)
741b7da9
DK
1032 return false;
1033
ef00bd7a
DK
1034 FileFd output;
1035 if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
1036 return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
b57c0e35 1037
4e92b116 1038 bool Okay = output.Failed() == false;
b57c0e35
DK
1039 if (Progress != NULL)
1040 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
43c71fad 1041 Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
b57c0e35
DK
1042 if (Progress != NULL)
1043 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
4e92b116 1044 Okay &= EDSP::WriteScenario(Cache, output, Progress);
ef00bd7a 1045 output.Close();
741b7da9 1046
b57c0e35
DK
1047 if (Progress != NULL)
1048 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
4e92b116 1049 if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
27c69dd0 1050 return false;
741b7da9 1051
5681b3fc 1052 return ExecWait(solver_pid, solver);
43c71fad
DK
1053}
1054bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
1055 bool const upgrade, bool const distUpgrade,
1056 bool const autoRemove, OpProgress *Progress) {
1057 unsigned int flags = 0;
1058 if (autoRemove)
1059 flags |= Request::AUTOREMOVE;
1060 if (upgrade)
1061 flags |= Request::UPGRADE_ALL | Request::FORBID_REMOVE | Request::FORBID_NEW_INSTALL;
1062 if (distUpgrade)
1063 flags |= Request::UPGRADE_ALL;
1064 return ResolveExternal(solver, Cache, flags, Progress);
ac5fbff8
DK
1065}
1066 /*}}}*/