]>
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/error.h>
11 #include <apt-pkg/cacheset.h>
12 #include <apt-pkg/depcache.h>
13 #include <apt-pkg/pkgcache.h>
14 #include <apt-pkg/cacheiterators.h>
15 #include <apt-pkg/progress.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/edsp.h>
18 #include <apt-pkg/tagfile.h>
19 #include <apt-pkg/strutl.h>
35 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
36 const char * const PrioMap
[] = {0, "important", "required", "standard",
38 const char * const DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
39 "Recommends" , "Conflicts", "Replaces",
40 "Obsoletes", "Breaks", "Enhances"};
43 // WriteScenarioVersion /*{{{*/
44 static void WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
45 pkgCache::VerIterator
const &Ver
)
47 fprintf(output
, "Package: %s\n", Pkg
.Name());
48 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
49 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
50 fprintf(output
, "Version: %s\n", Ver
.VerStr());
51 fprintf(output
, "Source-Version: %s\n", Ver
.SourceVerStr());
52 if (Pkg
.CurrentVer() == Ver
)
53 fprintf(output
, "Installed: yes\n");
54 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
55 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
56 fprintf(output
, "Hold: yes\n");
57 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
58 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
59 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
60 fprintf(output
, "Essential: yes\n");
61 fprintf(output
, "Section: %s\n", Ver
.Section());
62 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
63 fprintf(output
, "Multi-Arch: allowed\n");
64 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
65 fprintf(output
, "Multi-Arch: foreign\n");
66 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
67 fprintf(output
, "Multi-Arch: same\n");
68 signed short Pin
= std::numeric_limits
<signed short>::min();
69 std::set
<string
> Releases
;
70 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
71 pkgCache::PkgFileIterator File
= I
.File();
72 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
75 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
76 string Release
= File
.RelStr();
78 Releases
.insert(Release
);
81 if (!Releases
.empty()) {
82 fprintf(output
, "APT-Release:\n");
83 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
84 fprintf(output
, " %s\n", R
->c_str());
86 fprintf(output
, "APT-Pin: %d\n", Pin
);
87 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
88 fprintf(output
, "APT-Candidate: yes\n");
89 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
90 fprintf(output
, "APT-Automatic: yes\n");
93 // WriteScenarioDependency /*{{{*/
94 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
96 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
98 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
100 if (Dep
.IsImplicit() == true)
102 if (orGroup
== false)
103 dependencies
[Dep
->Type
].append(", ");
104 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
105 if (Dep
->Version
!= 0)
106 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
107 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
109 dependencies
[Dep
->Type
].append(" | ");
115 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
116 if (dependencies
[i
].empty() == false)
117 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
119 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
121 if (Prv
.IsMultiArchImplicit() == true)
123 provides
.append(", ").append(Prv
.Name());
124 if (Prv
->ProvideVersion
!= 0)
125 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
127 if (provides
.empty() == false)
128 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
131 // WriteScenarioLimitedDependency /*{{{*/
132 static void WriteScenarioLimitedDependency(FILE* output
,
133 pkgCache::VerIterator
const &Ver
,
134 APT::PackageSet
const &pkgset
)
136 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
137 bool orGroup
= false;
138 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
140 if (Dep
.IsImplicit() == true)
142 if (orGroup
== false)
144 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
146 dependencies
[Dep
->Type
].append(", ");
148 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
150 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
152 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
156 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
157 if (Dep
->Version
!= 0)
158 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
159 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
161 dependencies
[Dep
->Type
].append(" | ");
167 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
168 if (dependencies
[i
].empty() == false)
169 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
171 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
173 if (Prv
.IsMultiArchImplicit() == true)
175 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
177 provides
.append(", ").append(Prv
.Name());
179 if (provides
.empty() == false)
180 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
183 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
184 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
186 if (Progress
!= NULL
)
187 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
189 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
190 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
192 std::string
const arch
= Pkg
.Arch();
193 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
195 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
197 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
198 WriteScenarioDependency(output
, Ver
);
199 fprintf(output
, "\n");
200 if (Progress
!= NULL
&& p
% 100 == 0)
201 Progress
->Progress(p
);
207 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
208 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
209 APT::PackageSet
const &pkgset
,
210 OpProgress
*Progress
)
212 if (Progress
!= NULL
)
213 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
215 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
216 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
218 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
219 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
220 fprintf(output
, "\n");
221 if (Progress
!= NULL
&& p
% 100 == 0)
222 Progress
->Progress(p
);
224 if (Progress
!= NULL
)
229 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
230 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
231 bool const DistUpgrade
, bool const AutoRemove
,
232 OpProgress
*Progress
)
234 if (Progress
!= NULL
)
235 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
238 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
240 if (Progress
!= NULL
&& p
% 100 == 0)
241 Progress
->Progress(p
);
243 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
244 if (P
.Delete() == true)
246 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
247 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
251 req
->append(" ").append(Pkg
.FullName());
253 fprintf(output
, "Request: EDSP 0.5\n");
255 const char *arch
= _config
->Find("APT::Architecture").c_str();
256 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
257 fprintf(output
, "Architecture: %s\n", arch
);
258 fprintf(output
, "Architectures:");
259 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
260 fprintf(output
, " %s", a
->c_str());
261 fprintf(output
, "\n");
263 if (del
.empty() == false)
264 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
265 if (inst
.empty() == false)
266 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
268 fprintf(output
, "Upgrade: yes\n");
269 if (DistUpgrade
== true)
270 fprintf(output
, "Dist-Upgrade: yes\n");
271 if (AutoRemove
== true)
272 fprintf(output
, "Autoremove: yes\n");
273 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
274 fprintf(output
, "Strict-Pinning: no\n");
275 string
solverpref("APT::Solver::");
276 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
277 if (_config
->Exists(solverpref
) == true)
278 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
279 fprintf(output
, "\n");
284 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
285 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
286 /* We build an map id to mmap offset here
287 In theory we could use the offset as ID, but then VersionCount
288 couldn't be used to create other versionmappings anymore and it
289 would be too easy for a (buggy) solver to segfault APTā¦ */
290 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
291 unsigned long VerIdx
[VersionCount
];
292 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
293 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
294 VerIdx
[V
->ID
] = V
.Index();
295 Cache
[P
].Marked
= true;
296 Cache
[P
].Garbage
= false;
300 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
301 pkgTagFile
response(&in
, 100);
302 pkgTagSection section
;
304 while (response
.Step(section
) == true) {
306 if (section
.Exists("Install") == true)
308 else if (section
.Exists("Remove") == true)
310 else if (section
.Exists("Progress") == true) {
311 if (Progress
!= NULL
) {
312 string msg
= section
.FindS("Message");
313 if (msg
.empty() == true)
314 msg
= _("Prepare for receiving solution");
315 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
318 } else if (section
.Exists("Error") == true) {
319 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
320 if (msg
.empty() == true) {
321 msg
= _("External solver failed without a proper error message");
322 _error
->Error("%s", msg
.c_str());
324 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
325 if (Progress
!= NULL
)
327 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
328 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
329 std::cerr
<< msg
<< std::endl
<< std::endl
;
331 } else if (section
.Exists("Autoremove") == true)
336 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
337 if (id
== VersionCount
) {
338 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
340 } else if (id
> Cache
.Head().VersionCount
) {
341 _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());
345 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
346 Cache
.SetCandidateVersion(Ver
);
347 if (type
== "Install")
349 pkgCache::PkgIterator
const P
= Ver
.ParentPkg();
350 if (Cache
[P
].Mode
!= pkgDepCache::ModeInstall
)
351 Cache
.MarkInstall(P
, false, 0, false);
353 else if (type
== "Remove")
354 Cache
.MarkDelete(Ver
.ParentPkg(), false);
355 else if (type
== "Autoremove") {
356 Cache
[Ver
.ParentPkg()].Marked
= false;
357 Cache
[Ver
.ParentPkg()].Garbage
= true;
363 // ReadLine - first line from the given file descriptor /*{{{*/
364 // ---------------------------------------------------------------------
365 /* Little helper method to read a complete line into a string. Similar to
366 fgets but we need to use the low-level read() here as otherwise the
367 listparser will be confused later on as mixing of fgets and read isn't
368 a supported action according to the manpages and results are undefined */
369 static bool ReadLine(int const input
, std::string
&line
) {
374 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
381 if (line
.empty() == true && isblank(one
) != 0)
388 // StringToBool - convert yes/no to bool /*{{{*/
389 // ---------------------------------------------------------------------
390 /* we are not as lazy as we are in the global StringToBool as we really
391 only accept yes/no here - but we will ignore leading spaces */
392 static bool StringToBool(char const *answer
, bool const defValue
) {
393 for (; isspace(*answer
) != 0; ++answer
);
394 if (strncasecmp(answer
, "yes", 3) == 0)
396 else if (strncasecmp(answer
, "no", 2) == 0)
399 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
403 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
404 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
405 std::list
<std::string
> &remove
, bool &upgrade
,
406 bool &distUpgrade
, bool &autoRemove
)
414 while (ReadLine(input
, line
) == true)
416 // Skip empty lines before request
417 if (line
.empty() == true)
419 // The first Tag must be a request, so search for it
420 if (line
.compare(0, 8, "Request:") != 0)
423 while (ReadLine(input
, line
) == true)
425 // empty lines are the end of the request
426 if (line
.empty() == true)
429 std::list
<std::string
> *request
= NULL
;
430 if (line
.compare(0, 8, "Install:") == 0)
435 else if (line
.compare(0, 7, "Remove:") == 0)
440 else if (line
.compare(0, 8, "Upgrade:") == 0)
441 upgrade
= StringToBool(line
.c_str() + 9, false);
442 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
443 distUpgrade
= StringToBool(line
.c_str() + 14, false);
444 else if (line
.compare(0, 11, "Autoremove:") == 0)
445 autoRemove
= StringToBool(line
.c_str() + 12, false);
446 else if (line
.compare(0, 13, "Architecture:") == 0)
447 _config
->Set("APT::Architecture", line
.c_str() + 14);
448 else if (line
.compare(0, 14, "Architectures:") == 0)
450 std::string
const archs
= line
.c_str() + 15;
451 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
454 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
458 size_t end
= line
.length();
460 size_t begin
= line
.rfind(' ');
461 if (begin
== std::string::npos
)
463 request
->push_back(line
.substr(0, end
));
466 else if (begin
< end
)
467 request
->push_back(line
.substr(begin
+ 1, end
));
469 end
= line
.find_last_not_of(' ');
470 } while (end
!= std::string::npos
);
476 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
477 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
478 std::list
<std::string
> const &remove
,
481 for (std::list
<std::string
>::const_iterator i
= install
.begin();
482 i
!= install
.end(); ++i
) {
483 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
485 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
487 Cache
.MarkInstall(P
, false);
490 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
491 i
!= remove
.end(); ++i
) {
492 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
494 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
501 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
502 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
504 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
505 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
507 if (Cache
[Pkg
].Delete() == true)
509 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
511 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
513 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
515 pkgCache::VerIterator
const CandVer
= Cache
.GetCandidateVersion(Pkg
);
516 fprintf(output
, "Install: %d\n", CandVer
->ID
);
518 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), CandVer
.VerStr());
520 else if (Cache
[Pkg
].Garbage
== true)
522 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
524 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
528 fprintf(output
, "\n");
534 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
535 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
536 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
537 fprintf(output
, "Percentage: %d\n", percent
);
538 fprintf(output
, "Message: %s\n\n", message
);
543 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
544 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
545 fprintf(output
, "Error: %s\n", uuid
);
546 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
550 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
551 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
552 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
554 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
555 dir
!= solverDirs
.end(); ++dir
) {
556 file
= flCombine(*dir
, solver
);
557 if (RealFileExists(file
.c_str()) == true)
562 if (file
.empty() == true)
564 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
567 int external
[4] = {-1, -1, -1, -1};
568 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
570 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
573 for (int i
= 0; i
< 4; ++i
)
574 SetCloseExec(external
[i
], true);
576 pid_t Solver
= ExecFork();
578 dup2(external
[0], STDIN_FILENO
);
579 dup2(external
[3], STDOUT_FILENO
);
580 const char* calling
[2] = { file
.c_str(), 0 };
581 execv(calling
[0], (char**) calling
);
582 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
588 if (WaitFd(external
[1], true, 5) == false)
590 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
594 *solver_in
= external
[1];
595 *solver_out
= external
[2];
598 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
599 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
604 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
605 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
606 bool const upgrade
, bool const distUpgrade
,
607 bool const autoRemove
, OpProgress
*Progress
) {
608 int solver_in
, solver_out
;
609 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
613 FILE* output
= fdopen(solver_in
, "w");
615 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
617 if (Progress
!= NULL
)
618 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
619 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
620 if (Progress
!= NULL
)
621 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
622 EDSP::WriteScenario(Cache
, output
, Progress
);
625 if (Progress
!= NULL
)
626 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
627 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
630 return ExecWait(solver_pid
, solver
);