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