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