]> git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
0e229e1c099118d6169854eaf69d22129fd5343f
[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 <apt-pkg/edsp.h>
9 #include <apt-pkg/error.h>
10 #include <apt-pkg/configuration.h>
11 #include <apt-pkg/version.h>
12 #include <apt-pkg/policy.h>
13 #include <apt-pkg/tagfile.h>
14
15 #include <apti18n.h>
16 #include <limits>
17
18 #include <stdio.h>
19 /*}}}*/
20
21 // we could use pkgCache::DepType and ::Priority, but these would be localized strings…
22 const char * const EDSP::PrioMap[] = {0, "important", "required", "standard",
23 "optional", "extra"};
24 const char * const EDSP::DepMap[] = {"", "Depends", "Pre-Depends", "Suggests",
25 "Recommends" , "Conflicts", "Replaces",
26 "Obsoletes", "Breaks", "Enhances"};
27
28 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
29 bool EDSP::WriteScenario(pkgDepCache &Cache, FILE* output, OpProgress *Progress)
30 {
31 if (Progress != NULL)
32 Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to solver"));
33 unsigned long p = 0;
34 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
35 for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver, ++p)
36 {
37 WriteScenarioVersion(Cache, output, Pkg, Ver);
38 WriteScenarioDependency(Cache, output, Pkg, Ver);
39 fprintf(output, "\n");
40 if (Progress != NULL && p % 100 == 0)
41 Progress->Progress(p);
42 }
43 return true;
44 }
45 /*}}}*/
46 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
47 bool EDSP::WriteLimitedScenario(pkgDepCache &Cache, FILE* output,
48 APT::PackageSet const &pkgset,
49 OpProgress *Progress)
50 {
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)
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");
60 if (Progress != NULL && p % 100 == 0)
61 Progress->Progress(p);
62 }
63 if (Progress != NULL)
64 Progress->Done();
65 return true;
66 }
67 /*}}}*/
68 // EDSP::WriteScenarioVersion /*{{{*/
69 void 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");
77 if (Pkg->SelectedState == pkgCache::State::Hold ||
78 (Cache[Pkg].Keep() == true && Cache[Pkg].Protect() == true))
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 /*{{{*/
105 void 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 /*{{{*/
144 void 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 /*}}}*/
198 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
199 bool EDSP::WriteRequest(pkgDepCache &Cache, FILE* output, bool const Upgrade,
200 bool const DistUpgrade, bool const AutoRemove,
201 OpProgress *Progress)
202 {
203 if (Progress != NULL)
204 Progress->SubProgress(Cache.Head().PackageCount, _("Send request to solver"));
205 unsigned long p = 0;
206 string del, inst;
207 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
208 {
209 if (Progress != NULL && p % 100 == 0)
210 Progress->Progress(p);
211 string* req;
212 if (Cache[Pkg].Delete() == true)
213 req = &del;
214 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
215 req = &inst;
216 else
217 continue;
218 req->append(" ").append(Pkg.FullName());
219 }
220 fprintf(output, "Request: EDSP 0.4\n");
221 if (del.empty() == false)
222 fprintf(output, "Remove: %s\n", del.c_str()+1);
223 if (inst.empty() == false)
224 fprintf(output, "Install: %s\n", inst.c_str()+1);
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");
235 if (_config->Exists(solverpref) == true)
236 fprintf(output, "Preferences: %s\n", _config->Find(solverpref,"").c_str());
237 fprintf(output, "\n");
238
239 return true;
240 }
241 /*}}}*/
242 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
243 bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
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];
250 for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
251 for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
252 VerIdx[V->ID] = V.Index();
253 Cache[P].Marked = true;
254 Cache[P].Garbage = false;
255 }
256
257 FileFd in;
258 in.OpenDescriptor(input, FileFd::ReadOnly);
259 pkgTagFile response(&in, 100);
260 pkgTagSection section;
261
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";
268 else if (section.Exists("Progress") == true) {
269 if (Progress != NULL) {
270 string msg = section.FindS("Message");
271 if (msg.empty() == true)
272 msg = _("Prepare for receiving solution");
273 Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
274 }
275 continue;
276 } else if (section.Exists("Error") == true) {
277 std::cerr << "The solver encountered an error of type: " << section.FindS("Error") << std::endl;
278 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
279 std::cerr << SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n") << std::endl << std::endl;
280 break;
281 } else if (section.Exists("Autoremove") == true)
282 type = "Autoremove";
283 else
284 continue;
285
286 size_t const id = section.FindULL(type.c_str(), VersionCount);
287 if (id == VersionCount) {
288 _error->Warning("Unable to parse %s request with id value '%s'!", type.c_str(), section.FindS(type.c_str()).c_str());
289 continue;
290 } else if (id > Cache.Head().VersionCount) {
291 _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());
292 continue;
293 }
294
295 pkgCache::VerIterator Ver(Cache.GetCache(), Cache.GetCache().VerP + VerIdx[id]);
296 Cache.SetCandidateVersion(Ver);
297 if (type == "Install")
298 Cache.MarkInstall(Ver.ParentPkg(), false, 0, false);
299 else if (type == "Remove")
300 Cache.MarkDelete(Ver.ParentPkg(), false);
301 else if (type == "Autoremove") {
302 Cache[Ver.ParentPkg()].Marked = false;
303 Cache[Ver.ParentPkg()].Garbage = true;
304 }
305 }
306 return true;
307 }
308 /*}}}*/
309 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
310 // ---------------------------------------------------------------------
311 /* Little helper method to read a complete line into a string. Similar to
312 fgets but we need to use the low-level read() here as otherwise the
313 listparser will be confused later on as mixing of fgets and read isn't
314 a supported action according to the manpages and results are undefined */
315 bool EDSP::ReadLine(int const input, std::string &line) {
316 char one;
317 ssize_t data = 0;
318 line.erase();
319 line.reserve(100);
320 while ((data = read(input, &one, sizeof(one))) != -1) {
321 if (data != 1)
322 continue;
323 if (one == '\n')
324 return true;
325 if (one == '\r')
326 continue;
327 if (line.empty() == true && isblank(one) != 0)
328 continue;
329 line += one;
330 }
331 return false;
332 }
333 /*}}}*/
334 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
335 // ---------------------------------------------------------------------
336 /* we are not as lazy as we are in the global StringToBool as we really
337 only accept yes/no here - but we will ignore leading spaces */
338 bool EDSP::StringToBool(char const *answer, bool const defValue) {
339 for (; isspace(*answer) != 0; ++answer);
340 if (strncasecmp(answer, "yes", 3) == 0)
341 return true;
342 else if (strncasecmp(answer, "no", 2) == 0)
343 return false;
344 else
345 _error->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer);
346 return defValue;
347 }
348 /*}}}*/
349 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
350 bool EDSP::ReadRequest(int const input, std::list<std::string> &install,
351 std::list<std::string> &remove, bool &upgrade,
352 bool &distUpgrade, bool &autoRemove)
353 {
354 install.clear();
355 remove.clear();
356 upgrade = false;
357 distUpgrade = false;
358 autoRemove = false;
359 std::string line;
360 while (ReadLine(input, line) == true)
361 {
362 // Skip empty lines before request
363 if (line.empty() == true)
364 continue;
365 // The first Tag must be a request, so search for it
366 if (line.compare(0, 8, "Request:") != 0)
367 continue;
368
369 while (ReadLine(input, line) == true)
370 {
371 // empty lines are the end of the request
372 if (line.empty() == true)
373 return true;
374
375 std::list<std::string> *request = NULL;
376 if (line.compare(0, 8, "Install:") == 0)
377 {
378 line.erase(0, 8);
379 request = &install;
380 }
381 else if (line.compare(0, 7, "Remove:") == 0)
382 {
383 line.erase(0, 7);
384 request = &remove;
385 }
386 else if (line.compare(0, 8, "Upgrade:") == 0)
387 upgrade = EDSP::StringToBool(line.c_str() + 9, false);
388 else if (line.compare(0, 13, "Dist-Upgrade:") == 0)
389 distUpgrade = EDSP::StringToBool(line.c_str() + 14, false);
390 else if (line.compare(0, 11, "Autoremove:") == 0)
391 autoRemove = EDSP::StringToBool(line.c_str() + 12, false);
392 else
393 _error->Warning("Unknown line in EDSP Request stanza: %s", line.c_str());
394
395 if (request == NULL)
396 continue;
397 size_t end = line.length();
398 do {
399 size_t begin = line.rfind(' ');
400 if (begin == std::string::npos)
401 {
402 request->push_back(line.substr(0, end));
403 break;
404 }
405 else if (begin < end)
406 request->push_back(line.substr(begin + 1, end));
407 line.erase(begin);
408 end = line.find_last_not_of(' ');
409 } while (end != std::string::npos);
410 }
411 }
412 return false;
413 }
414 /*}}}*/
415 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
416 bool EDSP::ApplyRequest(std::list<std::string> const &install,
417 std::list<std::string> const &remove,
418 pkgDepCache &Cache)
419 {
420 for (std::list<std::string>::const_iterator i = install.begin();
421 i != install.end(); ++i) {
422 pkgCache::PkgIterator P = Cache.FindPkg(*i);
423 if (P.end() == true)
424 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
425 else
426 Cache.MarkInstall(P, false);
427 }
428
429 for (std::list<std::string>::const_iterator i = remove.begin();
430 i != remove.end(); ++i) {
431 pkgCache::PkgIterator P = Cache.FindPkg(*i);
432 if (P.end() == true)
433 _error->Warning("Package %s is not known, so can't be installed", i->c_str());
434 else
435 Cache.MarkDelete(P);
436 }
437 return true;
438 }
439 /*}}}*/
440 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
441 bool EDSP::WriteSolution(pkgDepCache &Cache, FILE* output)
442 {
443 bool const Debug = _config->FindB("Debug::EDSP::WriteSolution", false);
444 for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
445 {
446 if (Cache[Pkg].Delete() == true)
447 {
448 fprintf(output, "Remove: %d\n", Pkg.CurrentVer()->ID);
449 if (Debug == true)
450 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
451 }
452 else if (Cache[Pkg].NewInstall() == true || Cache[Pkg].Upgrade() == true)
453 {
454 fprintf(output, "Install: %d\n", Cache.GetCandidateVer(Pkg)->ID);
455 if (Debug == true)
456 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Cache.GetCandidateVer(Pkg).VerStr());
457 }
458 else if (Cache[Pkg].Garbage == true)
459 {
460 fprintf(output, "Autoremove: %d\n", Pkg.CurrentVer()->ID);
461 if (Debug == true)
462 fprintf(output, "Package: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
463 fprintf(stderr, "Autoremove: %s\nVersion: %s\n", Pkg.FullName().c_str(), Pkg.CurrentVer().VerStr());
464 }
465 else
466 continue;
467 fprintf(output, "\n");
468 }
469
470 return true;
471 }
472 /*}}}*/
473 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
474 bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) {
475 fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str());
476 fprintf(output, "Percentage: %d\n", percent);
477 fprintf(output, "Message: %s\n\n", message);
478 fflush(output);
479 return true;
480 }
481 /*}}}*/
482 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
483 bool EDSP::WriteError(char const * const uuid, std::string const &message, FILE* output) {
484 fprintf(output, "Error: %s\n", uuid);
485 fprintf(output, "Message: %s\n\n", SubstVar(SubstVar(message, "\n\n", "\n.\n"), "\n", "\n ").c_str());
486 return true;
487 }
488 /*}}}*/
489 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
490 bool EDSP::ExecuteSolver(const char* const solver, int *solver_in, int *solver_out) {
491 std::vector<std::string> const solverDirs = _config->FindVector("Dir::Bin::Solvers");
492 std::string file;
493 for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
494 dir != solverDirs.end(); ++dir) {
495 file = flCombine(*dir, solver);
496 if (RealFileExists(file.c_str()) == true)
497 break;
498 file.clear();
499 }
500
501 if (file.empty() == true)
502 return _error->Error("Can't call external solver '%s' as it is not in a configured directory!", solver);
503 int external[4] = {-1, -1, -1, -1};
504 if (pipe(external) != 0 || pipe(external + 2) != 0)
505 return _error->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
506 for (int i = 0; i < 4; ++i)
507 SetCloseExec(external[i], true);
508
509 pid_t Solver = ExecFork();
510 if (Solver == 0) {
511 dup2(external[0], STDIN_FILENO);
512 dup2(external[3], STDOUT_FILENO);
513 const char* calling[2] = { file.c_str(), 0 };
514 execv(calling[0], (char**) calling);
515 std::cerr << "Failed to execute solver '" << solver << "'!" << std::endl;
516 _exit(100);
517 }
518 close(external[0]);
519 close(external[3]);
520
521 if (WaitFd(external[1], true, 5) == false)
522 return _error->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
523
524 *solver_in = external[1];
525 *solver_out = external[2];
526 return true;
527 }
528 /*}}}*/
529 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
530 bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
531 bool const upgrade, bool const distUpgrade,
532 bool const autoRemove, OpProgress *Progress) {
533 int solver_in, solver_out;
534 if (EDSP::ExecuteSolver(solver, &solver_in, &solver_out) == false)
535 return false;
536
537 FILE* output = fdopen(solver_in, "w");
538 if (output == NULL)
539 return _error->Errno("Resolve", "fdopen on solver stdin failed");
540
541 if (Progress != NULL)
542 Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
543 EDSP::WriteRequest(Cache, output, upgrade, distUpgrade, autoRemove, Progress);
544 if (Progress != NULL)
545 Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
546 EDSP::WriteScenario(Cache, output, Progress);
547 fclose(output);
548
549 if (Progress != NULL)
550 Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
551 if (EDSP::ReadResponse(solver_out, Cache, Progress) == false)
552 return _error->Error("Reading solver response failed");
553
554 return true;
555 }
556 /*}}}*/