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