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