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