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