]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
4 Set of methods to help writing and reading everything needed for EDSP
5 ##################################################################### */
7 // Include Files /*{{{*/
10 #include <apt-pkg/edsp.h>
11 #include <apt-pkg/error.h>
12 #include <apt-pkg/cacheset.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/tagfile.h>
15 #include <apt-pkg/fileutl.h>
16 #include <apt-pkg/progress.h>
17 #include <apt-pkg/depcache.h>
18 #include <apt-pkg/pkgcache.h>
19 #include <apt-pkg/cacheiterators.h>
20 #include <apt-pkg/strutl.h>
21 #include <apt-pkg/pkgrecords.h>
41 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
42 const char * const PrioMap
[] = {0, "important", "required", "standard",
44 const char * const DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
45 "Recommends" , "Conflicts", "Replaces",
46 "Obsoletes", "Breaks", "Enhances"};
49 // WriteScenarioVersion /*{{{*/
50 static void WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
51 pkgCache::VerIterator
const &Ver
)
53 fprintf(output
, "Package: %s\n", Pkg
.Name());
54 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
55 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
56 fprintf(output
, "Version: %s\n", Ver
.VerStr());
57 if (Pkg
.CurrentVer() == Ver
)
58 fprintf(output
, "Installed: yes\n");
59 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
60 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
61 fprintf(output
, "Hold: yes\n");
62 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
63 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
64 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
65 fprintf(output
, "Essential: yes\n");
66 fprintf(output
, "Section: %s\n", Ver
.Section());
67 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
68 fprintf(output
, "Multi-Arch: allowed\n");
69 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
70 fprintf(output
, "Multi-Arch: foreign\n");
71 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
72 fprintf(output
, "Multi-Arch: same\n");
73 signed short Pin
= std::numeric_limits
<signed short>::min();
74 std::set
<string
> Releases
;
75 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
76 pkgCache::PkgFileIterator File
= I
.File();
77 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
80 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
81 string Release
= File
.RelStr();
83 Releases
.insert(Release
);
86 if (!Releases
.empty()) {
87 fprintf(output
, "APT-Release:\n");
88 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
89 fprintf(output
, " %s\n", R
->c_str());
91 fprintf(output
, "APT-Pin: %d\n", Pin
);
92 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
93 fprintf(output
, "APT-Candidate: yes\n");
94 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
95 fprintf(output
, "APT-Automatic: yes\n");
98 // WriteScenarioDependency /*{{{*/
99 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
101 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
102 bool orGroup
= false;
103 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
105 if (Dep
.IsImplicit() == true)
107 if (orGroup
== false)
108 dependencies
[Dep
->Type
].append(", ");
109 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().FullName((Dep
->CompareOp
& pkgCache::Dep::ArchSpecific
) != pkgCache::Dep::ArchSpecific
));
110 if (Dep
->Version
!= 0)
111 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
112 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
114 dependencies
[Dep
->Type
].append(" | ");
120 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
121 if (dependencies
[i
].empty() == false)
122 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
124 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
126 if (Prv
.IsMultiArchImplicit() == true)
128 provides
.append(", ").append(Prv
.Name());
130 if (provides
.empty() == false)
131 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
134 // WriteScenarioLimitedDependency /*{{{*/
135 static void WriteScenarioLimitedDependency(FILE* output
,
136 pkgCache::VerIterator
const &Ver
,
137 APT::PackageSet
const &pkgset
)
139 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
140 bool orGroup
= false;
141 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
143 if (Dep
.IsImplicit() == true)
145 if (orGroup
== false)
147 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
149 dependencies
[Dep
->Type
].append(", ");
151 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
153 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
155 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
159 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().FullName((Dep
->CompareOp
& pkgCache::Dep::ArchSpecific
) != pkgCache::Dep::ArchSpecific
));
160 if (Dep
->Version
!= 0)
161 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
162 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
164 dependencies
[Dep
->Type
].append(" | ");
170 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
171 if (dependencies
[i
].empty() == false)
172 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
174 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
176 if (Prv
.IsMultiArchImplicit() == true)
178 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
180 provides
.append(", ").append(Prv
.Name());
182 if (provides
.empty() == false)
183 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
186 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
187 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
189 if (Progress
!= NULL
)
190 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
192 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
193 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
195 std::string
const arch
= Pkg
.Arch();
196 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
198 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
200 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
201 WriteScenarioDependency(output
, Ver
);
202 fprintf(output
, "\n");
203 if (Progress
!= NULL
&& p
% 100 == 0)
204 Progress
->Progress(p
);
210 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
211 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
212 APT::PackageSet
const &pkgset
,
213 OpProgress
*Progress
)
215 if (Progress
!= NULL
)
216 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
218 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
219 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
221 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
222 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
223 fprintf(output
, "\n");
224 if (Progress
!= NULL
&& p
% 100 == 0)
225 Progress
->Progress(p
);
227 if (Progress
!= NULL
)
232 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
233 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
234 bool const DistUpgrade
, bool const AutoRemove
,
235 OpProgress
*Progress
)
237 if (Progress
!= NULL
)
238 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
241 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
243 if (Progress
!= NULL
&& p
% 100 == 0)
244 Progress
->Progress(p
);
246 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
247 if (P
.Delete() == true)
249 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
250 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
254 req
->append(" ").append(Pkg
.FullName());
256 fprintf(output
, "Request: EDSP 0.5\n");
258 const char *arch
= _config
->Find("APT::Architecture").c_str();
259 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
260 fprintf(output
, "Architecture: %s\n", arch
);
261 fprintf(output
, "Architectures:");
262 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
263 fprintf(output
, " %s", a
->c_str());
264 fprintf(output
, "\n");
266 if (del
.empty() == false)
267 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
268 if (inst
.empty() == false)
269 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
271 fprintf(output
, "Upgrade: yes\n");
272 if (DistUpgrade
== true)
273 fprintf(output
, "Dist-Upgrade: yes\n");
274 if (AutoRemove
== true)
275 fprintf(output
, "Autoremove: yes\n");
276 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
277 fprintf(output
, "Strict-Pinning: no\n");
278 string
solverpref("APT::Solver::");
279 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
280 if (_config
->Exists(solverpref
) == true)
281 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
282 fprintf(output
, "\n");
287 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
288 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
289 /* We build an map id to mmap offset here
290 In theory we could use the offset as ID, but then VersionCount
291 couldn't be used to create other versionmappings anymore and it
292 would be too easy for a (buggy) solver to segfault APTā¦ */
293 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
294 unsigned long VerIdx
[VersionCount
];
295 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
296 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
297 VerIdx
[V
->ID
] = V
.Index();
298 Cache
[P
].Marked
= true;
299 Cache
[P
].Garbage
= false;
303 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
304 pkgTagFile
response(&in
, 100);
305 pkgTagSection section
;
307 while (response
.Step(section
) == true) {
309 if (section
.Exists("Install") == true)
311 else if (section
.Exists("Remove") == true)
313 else if (section
.Exists("Progress") == true) {
314 if (Progress
!= NULL
) {
315 string msg
= section
.FindS("Message");
316 if (msg
.empty() == true)
317 msg
= _("Prepare for receiving solution");
318 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
321 } else if (section
.Exists("Error") == true) {
322 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
323 if (msg
.empty() == true) {
324 msg
= _("External solver failed without a proper error message");
325 _error
->Error("%s", msg
.c_str());
327 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
328 if (Progress
!= NULL
)
330 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
331 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
332 std::cerr
<< msg
<< std::endl
<< std::endl
;
334 } else if (section
.Exists("Autoremove") == true)
339 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
340 if (id
== VersionCount
) {
341 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
343 } else if (id
> Cache
.Head().VersionCount
) {
344 _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());
348 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
349 Cache
.SetCandidateVersion(Ver
);
350 if (type
== "Install")
351 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
352 else if (type
== "Remove")
353 Cache
.MarkDelete(Ver
.ParentPkg(), false);
354 else if (type
== "Autoremove") {
355 Cache
[Ver
.ParentPkg()].Marked
= false;
356 Cache
[Ver
.ParentPkg()].Garbage
= true;
362 // ReadLine - first line from the given file descriptor /*{{{*/
363 // ---------------------------------------------------------------------
364 /* Little helper method to read a complete line into a string. Similar to
365 fgets but we need to use the low-level read() here as otherwise the
366 listparser will be confused later on as mixing of fgets and read isn't
367 a supported action according to the manpages and results are undefined */
368 static bool ReadLine(int const input
, std::string
&line
) {
373 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
380 if (line
.empty() == true && isblank(one
) != 0)
387 // StringToBool - convert yes/no to bool /*{{{*/
388 // ---------------------------------------------------------------------
389 /* we are not as lazy as we are in the global StringToBool as we really
390 only accept yes/no here - but we will ignore leading spaces */
391 static bool StringToBool(char const *answer
, bool const defValue
) {
392 for (; isspace(*answer
) != 0; ++answer
);
393 if (strncasecmp(answer
, "yes", 3) == 0)
395 else if (strncasecmp(answer
, "no", 2) == 0)
398 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
402 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
403 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
404 std::list
<std::string
> &remove
, bool &upgrade
,
405 bool &distUpgrade
, bool &autoRemove
)
413 while (ReadLine(input
, line
) == true)
415 // Skip empty lines before request
416 if (line
.empty() == true)
418 // The first Tag must be a request, so search for it
419 if (line
.compare(0, 8, "Request:") != 0)
422 while (ReadLine(input
, line
) == true)
424 // empty lines are the end of the request
425 if (line
.empty() == true)
428 std::list
<std::string
> *request
= NULL
;
429 if (line
.compare(0, 8, "Install:") == 0)
434 else if (line
.compare(0, 7, "Remove:") == 0)
439 else if (line
.compare(0, 8, "Upgrade:") == 0)
440 upgrade
= StringToBool(line
.c_str() + 9, false);
441 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
442 distUpgrade
= StringToBool(line
.c_str() + 14, false);
443 else if (line
.compare(0, 11, "Autoremove:") == 0)
444 autoRemove
= StringToBool(line
.c_str() + 12, false);
445 else if (line
.compare(0, 13, "Architecture:") == 0)
446 _config
->Set("APT::Architecture", line
.c_str() + 14);
447 else if (line
.compare(0, 14, "Architectures:") == 0)
449 std::string
const archs
= line
.c_str() + 15;
450 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
453 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
457 size_t end
= line
.length();
459 size_t begin
= line
.rfind(' ');
460 if (begin
== std::string::npos
)
462 request
->push_back(line
.substr(0, end
));
465 else if (begin
< end
)
466 request
->push_back(line
.substr(begin
+ 1, end
));
468 end
= line
.find_last_not_of(' ');
469 } while (end
!= std::string::npos
);
475 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
476 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
477 std::list
<std::string
> const &remove
,
480 for (std::list
<std::string
>::const_iterator i
= install
.begin();
481 i
!= install
.end(); ++i
) {
482 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
484 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
486 Cache
.MarkInstall(P
, false);
489 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
490 i
!= remove
.end(); ++i
) {
491 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
493 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
500 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
501 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
503 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
504 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
506 if (Cache
[Pkg
].Delete() == true)
508 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
510 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
512 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
514 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
516 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
518 else if (Cache
[Pkg
].Garbage
== true)
520 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
522 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
526 fprintf(output
, "\n");
532 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
533 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
534 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
535 fprintf(output
, "Percentage: %d\n", percent
);
536 fprintf(output
, "Message: %s\n\n", message
);
541 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
542 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
543 fprintf(output
, "Error: %s\n", uuid
);
544 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
548 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
549 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
550 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
552 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
553 dir
!= solverDirs
.end(); ++dir
) {
554 file
= flCombine(*dir
, solver
);
555 if (RealFileExists(file
.c_str()) == true)
560 if (file
.empty() == true)
562 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
565 int external
[4] = {-1, -1, -1, -1};
566 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
568 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
571 for (int i
= 0; i
< 4; ++i
)
572 SetCloseExec(external
[i
], true);
574 pid_t Solver
= ExecFork();
576 dup2(external
[0], STDIN_FILENO
);
577 dup2(external
[3], STDOUT_FILENO
);
578 const char* calling
[2] = { file
.c_str(), 0 };
579 execv(calling
[0], (char**) calling
);
580 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
586 if (WaitFd(external
[1], true, 5) == false)
588 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
592 *solver_in
= external
[1];
593 *solver_out
= external
[2];
596 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
597 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
602 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
603 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
604 bool const upgrade
, bool const distUpgrade
,
605 bool const autoRemove
, OpProgress
*Progress
) {
606 int solver_in
, solver_out
;
607 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
611 FILE* output
= fdopen(solver_in
, "w");
613 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
615 if (Progress
!= NULL
)
616 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
617 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
618 if (Progress
!= NULL
)
619 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
620 EDSP::WriteScenario(Cache
, output
, Progress
);
623 if (Progress
!= NULL
)
624 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
625 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
628 return ExecWait(solver_pid
, solver
);