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