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