]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/edsp.cc
edsp: document unique package identifiers
[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
21#include <ctype.h>
22#include <stddef.h>
23#include <string.h>
24#include <unistd.h>
25#include <stdio.h>
26#include <iostream>
27#include <limits>
28#include <string>
29
30#include <apti18n.h>
31 /*}}}*/
32
33using std::string;
34
35// we could use pkgCache::DepType and ::Priority, but these would be localized strings…
36const char * const PrioMap[] = {0, "important", "required", "standard",
37 "optional", "extra"};
38const char * const DepMap[] = {"", "Depends", "Pre-Depends", "Suggests",
39 "Recommends" , "Conflicts", "Replaces",
40 "Obsoletes", "Breaks", "Enhances"};
41
42
43// WriteScenarioVersion /*{{{*/
44static void WriteScenarioVersion(pkgDepCache &Cache, FILE* output, pkgCache::PkgIterator const &Pkg,
45 pkgCache::VerIterator const &Ver)
46{
47 fprintf(output, "Package: %s\n", Pkg.Name());
48 fprintf(output, "Source: %s\n", Ver.SourcePkgName());
49 fprintf(output, "Architecture: %s\n", Ver.Arch());
50 fprintf(output, "Version: %s\n", Ver.VerStr());
51 fprintf(output, "Source-Version: %s\n", Ver.SourceVerStr());
52 if (Pkg.CurrentVer() == Ver)
53 fprintf(output, "Installed: yes\n");
54 if (Pkg->SelectedState == pkgCache::State::Hold ||
55 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
56 fprintf(output, "Hold: yes\n");
57 fprintf(output, "APT-ID: %d\n", Ver->ID);
58 fprintf(output, "Priority: %s\n", PrioMap[Ver->Priority]);
59 if ((Pkg->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
60 fprintf(output, "Essential: yes\n");
61 fprintf(output, "Section: %s\n", Ver.Section());
62 if ((Ver->MultiArch & pkgCache::Version::Allowed) == pkgCache::Version::Allowed)
63 fprintf(output, "Multi-Arch: allowed\n");
64 else if ((Ver->MultiArch & pkgCache::Version::Foreign) == pkgCache::Version::Foreign)
65 fprintf(output, "Multi-Arch: foreign\n");
66 else if ((Ver->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
67 fprintf(output, "Multi-Arch: same\n");
68 std::set<string> Releases;
69 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I) {
70 pkgCache::PkgFileIterator File = I.File();
71 if (File.Flagged(pkgCache::Flag::NotSource) == false) {
72 string Release = File.RelStr();
73 if (!Release.empty())
74 Releases.insert(Release);
75 }
76 }
77 if (!Releases.empty()) {
78 fprintf(output, "APT-Release:\n");
79 for (std::set<string>::iterator R = Releases.begin(); R != Releases.end(); ++R)
80 fprintf(output, " %s\n", R->c_str());
81 }
82 fprintf(output, "APT-Pin: %d\n", Cache.GetPolicy().GetPriority(Ver));
83 if (Cache.GetCandidateVersion(Pkg) == Ver)
84 fprintf(output, "APT-Candidate: yes\n");
85 if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
86 fprintf(output, "APT-Automatic: yes\n");
87}
88 /*}}}*/
89// WriteScenarioDependency /*{{{*/
90static void WriteScenarioDependency( FILE* output, pkgCache::VerIterator const &Ver)
91{
92 std::string dependencies[pkgCache::Dep::Enhances + 1];
93 bool orGroup = false;
94 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
95 {
96 if (Dep.IsImplicit() == true)
97 continue;
98 if (orGroup == false)
99 dependencies[Dep->Type].append(", ");
100 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
101 if (Dep->Version != 0)
102 dependencies[Dep->Type].append(" (").append(pkgCache::CompTypeDeb(Dep->CompareOp)).append(" ").append(Dep.TargetVer()).append(")");
103 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
104 {
105 dependencies[Dep->Type].append(" | ");
106 orGroup = true;
107 }
108 else
109 orGroup = false;
110 }
111 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
112 if (dependencies[i].empty() == false)
113 fprintf(output, "%s: %s\n", DepMap[i], dependencies[i].c_str()+2);
114 string provides;
115 for (pkgCache::PrvIterator Prv = Ver.ProvidesList(); Prv.end() == false; ++Prv)
116 {
117 if (Prv.IsMultiArchImplicit() == true)
118 continue;
119 provides.append(", ").append(Prv.Name());
120 if (Prv->ProvideVersion != 0)
121 provides.append(" (= ").append(Prv.ProvideVersion()).append(")");
122 }
123 if (provides.empty() == false)
124 fprintf(output, "Provides: %s\n", provides.c_str()+2);
125}
126 /*}}}*/
127// WriteScenarioLimitedDependency /*{{{*/
128static void WriteScenarioLimitedDependency(FILE* output,
129 pkgCache::VerIterator const &Ver,
130 APT::PackageSet const &pkgset)
131{
132 std::string dependencies[pkgCache::Dep::Enhances + 1];
133 bool orGroup = false;
134 for (pkgCache::DepIterator Dep = Ver.DependsList(); Dep.end() == false; ++Dep)
135 {
136 if (Dep.IsImplicit() == true)
137 continue;
138 if (orGroup == false)
139 {
140 if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
141 continue;
142 dependencies[Dep->Type].append(", ");
143 }
144 else if (pkgset.find(Dep.TargetPkg()) == pkgset.end())
145 {
146 if ((Dep->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or)
147 continue;
148 dependencies[Dep->Type].erase(dependencies[Dep->Type].end()-3, dependencies[Dep->Type].end());
149 orGroup = false;
150 continue;
151 }
152 dependencies[Dep->Type].append(Dep.TargetPkg().Name());
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 }
163 for (int i = 1; i < pkgCache::Dep::Enhances + 1; ++i)
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 {
169 if (Prv.IsMultiArchImplicit() == true)
170 continue;
171 if (pkgset.find(Prv.ParentPkg()) == pkgset.end())
172 continue;
173 provides.append(", ").append(Prv.Name());
174 }
175 if (provides.empty() == false)
176 fprintf(output, "Provides: %s\n", provides.c_str()+2);
177}
178 /*}}}*/
179static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
180{
181 /* versions which aren't current and aren't available in
182 any "online" source file are bad, expect if they are the choosen
183 candidate: The exception is for build-dep implementation as it creates
184 such pseudo (package) versions and removes them later on again.
185 We filter out versions at all so packages in 'rc' state only available
186 in dpkg/status aren't passed to solvers as they can't be installed. */
187 if (Pkg->CurrentVer != 0)
188 return false;
189 if (Cache.GetCandidateVersion(Pkg) == Ver)
190 return false;
191 for (pkgCache::VerFileIterator I = Ver.FileList(); I.end() == false; ++I)
192 if (I.File().Flagged(pkgCache::Flag::NotSource) == false)
193 return false;
194 return true;
195}
196 /*}}}*/
197// EDSP::WriteScenario - to the given file descriptor /*{{{*/
198bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
199{
200 if (Progress != NULL)
201 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
202 unsigned long p = 0;
203 std::vector<std::string> archs = APT::Configuration::getArchitectures();
204 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
205 {
206 std::string const arch = Pkg.Arch();
207 if (std::find(archs.begin(), archs.end(), arch) == archs.end())
208 continue;
209 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
210 {
211 if (SkipUnavailableVersions(Cache, Pkg, Ver))
212 continue;
213 WriteScenarioVersion(Cache, output, Pkg, Ver);
214 WriteScenarioDependency(output, Ver);
215 fprintf(output, "\n");
216 if (Progress != NULL && p % 100 == 0)
217 Progress->Progress(p);
218 }
219 }
220 return true;
221}
222 /*}}}*/
223// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
224bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
225 APT::PackageSet const &pkgset,
226 OpProgress *Progress)
227{
228 if (Progress != NULL)
229 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
230 unsigned long p = 0;
231 for (APT::PackageSet::const_iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg, ++p)
232 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
233 {
234 if (SkipUnavailableVersions(Cache, Pkg, Ver))
235 continue;
236 WriteScenarioVersion(Cache, output, Pkg, Ver);
237 WriteScenarioLimitedDependency(output, Ver, pkgset);
238 fprintf(output, "\n");
239 if (Progress != NULL && p % 100 == 0)
240 Progress->Progress(p);
241 }
242 if (Progress != NULL)
243 Progress->Done();
244 return true;
245}
246 /*}}}*/
247// EDSP::WriteRequest - to the given file descriptor /*{{{*/
248bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
249 bool const DistUpgrade, bool const AutoRemove,
250 OpProgress *Progress)
251{
252 if (Progress != NULL)
253 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
254 unsigned long p = 0;
255 string del, inst;
256 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
257 {
258 if (Progress != NULL && p % 100 == 0)
259 Progress->Progress(p);
260 string* req;
261 pkgDepCache::StateCache &P = Cache[Pkg];
262 if (P.Delete() == true)
263 req = &del;
264 else if (P.NewInstall() == true || P.Upgrade() == true || P.ReInstall() == true ||
265 (P.Mode == pkgDepCache::ModeKeep && (P.iFlags & pkgDepCache::Protected) == pkgDepCache::Protected))
266 req = &inst;
267 else
268 continue;
269 req->append(" ").append(Pkg.FullName());
270 }
271 fprintf(output, "Request: EDSP 0.5\n");
272
273 const char *arch = _config->Find("APT::Architecture").c_str();
274 std::vector<string> archs = APT::Configuration::getArchitectures();
275 fprintf(output, "Architecture: %s\n", arch);
276 fprintf(output, "Architectures:");
277 for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
278 fprintf(output, " %s", a->c_str());
279 fprintf(output, "\n");
280
281 if (del.empty() == false)
282 fprintf(output, "Remove: %s\n", del.c_str()+1);
283 if (inst.empty() == false)
284 fprintf(output, "Install: %s\n", inst.c_str()+1);
285 if (Upgrade == true)
286 fprintf(output, "Upgrade: yes\n");
287 if (DistUpgrade == true)
288 fprintf(output, "Dist-Upgrade: yes\n");
289 if (AutoRemove == true)
290 fprintf(output, "Autoremove: yes\n");
291 auto const solver = _config->Find("APT::Solver", "internal");
292 fprintf(output, "Solver: %s\n", solver.c_str());
293 auto const solverconf = std::string("APT::Solver::") + solver + "::";
294 if (_config->FindB(solverconf + "Strict-Pinning", _config->FindB("APT::Solver::Strict-Pinning", true)) == false)
295 fprintf(output, "Strict-Pinning: no\n");
296 auto const solverpref = _config->Find(solverconf + "Preferences", _config->Find("APT::Solver::Preferences", ""));
297 if (solverpref.empty() == false)
298 fprintf(output, "Preferences: %s\n", solverpref.c_str());
299 fprintf(output, "\n");
300 return true;
301}
302 /*}}}*/
303// EDSP::ReadResponse - from the given file descriptor /*{{{*/
304bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
305 /* We build an map id to mmap offset here
306 In theory we could use the offset as ID, but then VersionCount
307 couldn't be used to create other versionmappings anymore and it
308 would be too easy for a (buggy) solver to segfault APT… */
309 unsigned long long const VersionCount = Cache.Head().VersionCount;
310 unsigned long VerIdx[VersionCount];
311 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
312 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
313 VerIdx[V->ID] = V.Index();
314 Cache[P].Marked = true;
315 Cache[P].Garbage = false;
316 }
317
318 FileFd in;
319 in.OpenDescriptor(input, FileFd::ReadOnly);
320 pkgTagFile response(&in, 100);
321 pkgTagSection section;
322
323 while (response.Step(section) == true) {
324 std::string type;
325 if (section.Exists("Install") == true)
326 type = "Install";
327 else if (section.Exists("Remove") == true)
328 type = "Remove";
329 else if (section.Exists("Progress") == true) {
330 if (Progress != NULL) {
331 string msg = section.FindS("Message");
332 if (msg.empty() == true)
333 msg = _("Prepare for receiving solution");
334 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
335 }
336 continue;
337 } else if (section.Exists("Error") == true) {
338 std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
339 if (msg.empty() == true) {
340 msg = _("External solver failed without a proper error message");
341 _error->Error("%s", msg.c_str());
342 } else
343 _error->Error("External solver failed with: %s", msg.substr(0,msg.find('\n')).c_str());
344 if (Progress != NULL)
345 Progress->Done();
346 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
347 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
348 std::cerr << msg << std::endl << std::endl;
349 return false;
350 } else if (section.Exists("Autoremove") == true)
351 type = "Autoremove";
352 else
353 continue;
354
355 size_t const id = section.FindULL(type.c_str(), VersionCount);
356 if (id == VersionCount) {
357 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
358 continue;
359 } else if (id > Cache.Head().VersionCount) {
360 _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());
361 continue;
362 }
363
364 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
365 Cache.SetCandidateVersion(Ver);
366 if (type == "Install")
367 {
368 pkgCache::PkgIterator const P = Ver.ParentPkg();
369 if (Cache[P].Mode != pkgDepCache::ModeInstall)
370 Cache.MarkInstall(P, false, 0, false);
371 }
372 else if (type == "Remove")
373 Cache.MarkDelete(Ver.ParentPkg(), false);
374 else if (type == "Autoremove") {
375 Cache[Ver.ParentPkg()].Marked = false;
376 Cache[Ver.ParentPkg()].Garbage = true;
377 }
378 }
379 return true;
380}
381 /*}}}*/
382// ReadLine - first line from the given file descriptor /*{{{*/
383// ---------------------------------------------------------------------
384/* Little helper method to read a complete line into a string. Similar to
385 fgets but we need to use the low-level read() here as otherwise the
386 listparser will be confused later on as mixing of fgets and read isn't
387 a supported action according to the manpages and results are undefined */
388static bool ReadLine(int const input, std::string &line) {
389 char one;
390 ssize_t data = 0;
391 line.erase();
392 line.reserve(100);
393 while ((data = read(input, &one, sizeof(one))) != -1) {
394 if (data != 1)
395 continue;
396 if (one == '\n')
397 return true;
398 if (one == '\r')
399 continue;
400 if (line.empty() == true && isblank(one) != 0)
401 continue;
402 line += one;
403 }
404 return false;
405}
406 /*}}}*/
407// StringToBool - convert yes/no to bool /*{{{*/
408// ---------------------------------------------------------------------
409/* we are not as lazy as we are in the global StringToBool as we really
410 only accept yes/no here - but we will ignore leading spaces */
411static bool StringToBool(char const *answer, bool const defValue) {
412 for (; isspace(*answer) != 0; ++answer);
413 if (strncasecmp(answer, "yes", 3) == 0)
414 return true;
415 else if (strncasecmp(answer, "no", 2) == 0)
416 return false;
417 else
418 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
419 return defValue;
420}
421 /*}}}*/
422// EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
423bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
424 std::list<std::string> &remove, bool &upgrade,
425 bool &distUpgrade, bool &autoRemove)
426{
427 install.clear();
428 remove.clear();
429 upgrade = false;
430 distUpgrade = false;
431 autoRemove = false;
432 std::string line;
433 while (ReadLine(input, line) == true)
434 {
435 // Skip empty lines before request
436 if (line.empty() == true)
437 continue;
438 // The first Tag must be a request, so search for it
439 if (line.compare(0, 8, "Request:") != 0)
440 continue;
441
442 while (ReadLine(input, line) == true)
443 {
444 // empty lines are the end of the request
445 if (line.empty() == true)
446 return true;
447
448 std::list<std::string> *request = NULL;
449 if (line.compare(0, 8, "Install:") == 0)
450 {
451 line.erase(0, 8);
452 request = &install;
453 }
454 else if (line.compare(0, 7, "Remove:") == 0)
455 {
456 line.erase(0, 7);
457 request = &remove;
458 }
459 else if (line.compare(0, 8, "Upgrade:") == 0)
460 upgrade = StringToBool(line.c_str() + 9, false);
461 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
462 distUpgrade = StringToBool(line.c_str() + 14, false);
463 else if (line.compare(0, 11, "Autoremove:") == 0)
464 autoRemove = StringToBool(line.c_str() + 12, false);
465 else if (line.compare(0, 13, "Architecture:") == 0)
466 _config->Set("APT::Architecture", line.c_str() + 14);
467 else if (line.compare(0, 14, "Architectures:") == 0)
468 {
469 std::string const archs = line.c_str() + 15;
470 _config->Set("APT::Architectures", SubstVar(archs, " ", ","));
471 }
472 else if (line.compare(0, 7, "Solver:") == 0)
473 ; // purely informational line
474 else
475 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
476
477 if (request == NULL)
478 continue;
479 size_t end = line.length();
480 do {
481 size_t begin = line.rfind(' ');
482 if (begin == std::string::npos)
483 {
484 request->push_back(line.substr(0, end));
485 break;
486 }
487 else if (begin < end)
488 request->push_back(line.substr(begin + 1, end));
489 line.erase(begin);
490 end = line.find_last_not_of(' ');
491 } while (end != std::string::npos);
492 }
493 }
494 return false;
495}
496 /*}}}*/
497// EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
498bool EDSP::ApplyRequest(std::list<std::string> const &install,
499 std::list<std::string> const &remove,
500 pkgDepCache &Cache)
501{
502 for (std::list<std::string>::const_iterator i = install.begin();
503 i != install.end(); ++i) {
504 pkgCache::PkgIterator P = Cache.FindPkg(*i);
505 if (P.end() == true)
506 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
507 else
508 Cache.MarkInstall(P, false);
509 }
510
511 for (std::list<std::string>::const_iterator i = remove.begin();
512 i != remove.end(); ++i) {
513 pkgCache::PkgIterator P = Cache.FindPkg(*i);
514 if (P.end() == true)
515 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
516 else
517 Cache.MarkDelete(P);
518 }
519 return true;
520}
521 /*}}}*/
522// EDSP::WriteSolution - to the given file descriptor /*{{{*/
523bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
524{
525 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
526 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
527 {
528 if (Cache[Pkg].Delete() == true)
529 {
530 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
531 if (Debug == true)
532 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
533 }
534 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
535 {
536 pkgCache::VerIterator const CandVer = Cache.GetCandidateVersion(Pkg);
537 fprintf(output, "Install: %d\n", CandVer->ID);
538 if (Debug == true)
539 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), CandVer.VerStr());
540 }
541 else if (Cache[Pkg].Garbage == true)
542 {
543 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
544 if (Debug == true)
545 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
546 }
547 else
548 continue;
549 fprintf(output, "\n");
550 }
551
552 return true;
553}
554 /*}}}*/
555// EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
556bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
557 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
558 fprintf(output, "Percentage: %d\n", percent);
559 fprintf(output, "Message: %s\n\n", message);
560 fflush(output);
561 return true;
562}
563 /*}}}*/
564// EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
565bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
566 fprintf(output, "Error: %s\n", uuid);
567 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
568 return true;
569}
570 /*}}}*/
571// EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
572pid_t EDSP::ExecuteSolver(const char* const solver, int * const solver_in, int * const solver_out, bool) {
573 std::vector<std::string> const solverDirs = _config->FindVector("Dir::Bin::Solvers");
574 std::string file;
575 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
576 dir != solverDirs.end(); ++dir) {
577 file = flCombine(*dir, solver);
578 if (RealFileExists(file.c_str()) == true)
579 break;
580 file.clear();
581 }
582
583 if (file.empty() == true)
584 {
585 _error->Error("Can't call external solver '%s' as it is not in a configured directory!", solver);
586 return 0;
587 }
588 int external[4] = {-1, -1, -1, -1};
589 if (pipe(external) != 0 || pipe(external + 2) != 0)
590 {
591 _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
592 return 0;
593 }
594 for (int i = 0; i < 4; ++i)
595 SetCloseExec(external[i], true);
596
597 pid_t Solver = ExecFork();
598 if (Solver == 0) {
599 dup2(external[0], STDIN_FILENO);
600 dup2(external[3], STDOUT_FILENO);
601 const char* calling[2] = { file.c_str(), 0 };
602 execv(calling[0], (char**) calling);
603 std::cerr << "Failed to execute solver '" << solver << "'!" << std::endl;
604 _exit(100);
605 }
606 close(external[0]);
607 close(external[3]);
608
609 if (WaitFd(external[1], true, 5) == false)
610 {
611 _error->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
612 return 0;
613 }
614
615 *solver_in = external[1];
616 *solver_out = external[2];
617 return Solver;
618}
619bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
620 if (ExecuteSolver(solver, solver_in, solver_out, true) == 0)
621 return false;
622 return true;
623}
624 /*}}}*/
625// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
626bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
627 bool const upgrade, bool const distUpgrade,
628 bool const autoRemove, OpProgress *Progress) {
629 int solver_in, solver_out;
630 pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
631 if (solver_pid == 0)
632 return false;
633
634 FILE* output = fdopen(solver_in, "w");
635 if (output == NULL)
636 return _error->Errno("Resolve", "fdopen on solver stdin failed");
637
638 if (Progress != NULL)
639 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
640 EDSP::WriteRequest(Cache, output, upgrade, distUpgrade, autoRemove, Progress);
641 if (Progress != NULL)
642 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
643 EDSP::WriteScenario(Cache, output, Progress);
644 fclose(output);
645
646 if (Progress != NULL)
647 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
648 if (EDSP::ReadResponse(solver_out, Cache, Progress) == false)
649 return false;
650
651 return ExecWait(solver_pid, solver);
652}
653 /*}}}*/