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