]>
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 EDSP::PrioMap
[] = {0, "important", "required", "standard",
44 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
45 "Recommends" , "Conflicts", "Replaces",
46 "Obsoletes", "Breaks", "Enhances"};
48 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
49 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
52 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
54 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
55 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
57 std::string
const arch
= Pkg
.Arch();
58 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
60 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
62 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
63 WriteScenarioDependency(output
, Ver
);
64 fprintf(output
, "\n");
65 if (Progress
!= NULL
&& p
% 100 == 0)
66 Progress
->Progress(p
);
72 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
73 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
74 APT::PackageSet
const &pkgset
,
78 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
80 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
81 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
83 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
84 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
85 fprintf(output
, "\n");
86 if (Progress
!= NULL
&& p
% 100 == 0)
87 Progress
->Progress(p
);
94 // EDSP::WriteScenarioVersion /*{{{*/
95 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
96 pkgCache::VerIterator
const &Ver
)
98 pkgRecords
Recs(Cache
);
99 pkgRecords::Parser
&rec
= Recs
.Lookup(Ver
.FileList());
100 string srcpkg
= rec
.SourcePkg().empty() ? Pkg
.Name() : rec
.SourcePkg();
102 fprintf(output
, "Package: %s\n", Pkg
.Name());
103 fprintf(output
, "Source: %s\n", srcpkg
.c_str());
104 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
105 fprintf(output
, "Version: %s\n", Ver
.VerStr());
106 if (Pkg
.CurrentVer() == Ver
)
107 fprintf(output
, "Installed: yes\n");
108 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
109 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
110 fprintf(output
, "Hold: yes\n");
111 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
112 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
113 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
114 fprintf(output
, "Essential: yes\n");
115 fprintf(output
, "Section: %s\n", Ver
.Section());
116 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
117 fprintf(output
, "Multi-Arch: allowed\n");
118 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
119 fprintf(output
, "Multi-Arch: foreign\n");
120 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
121 fprintf(output
, "Multi-Arch: same\n");
122 signed short Pin
= std::numeric_limits
<signed short>::min();
123 std::set
<string
> Releases
;
124 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
125 pkgCache::PkgFileIterator File
= I
.File();
126 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
129 if ((File
->Flags
& pkgCache::Flag::NotSource
) != pkgCache::Flag::NotSource
) {
130 string Release
= File
.RelStr();
131 if (!Release
.empty())
132 Releases
.insert(Release
);
135 if (!Releases
.empty()) {
136 fprintf(output
, "APT-Release:\n");
137 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
138 fprintf(output
, " %s\n", R
->c_str());
140 fprintf(output
, "APT-Pin: %d\n", Pin
);
141 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
142 fprintf(output
, "APT-Candidate: yes\n");
143 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
144 fprintf(output
, "APT-Automatic: yes\n");
147 // EDSP::WriteScenarioDependency /*{{{*/
148 void EDSP::WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
150 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
151 bool orGroup
= false;
152 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
154 if (Dep
.IsMultiArchImplicit() == true)
156 if (orGroup
== false)
157 dependencies
[Dep
->Type
].append(", ");
158 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
159 if (Dep
->Version
!= 0)
160 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
161 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
163 dependencies
[Dep
->Type
].append(" | ");
169 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
170 if (dependencies
[i
].empty() == false)
171 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
173 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
175 if (Prv
.IsMultiArchImplicit() == true)
177 provides
.append(", ").append(Prv
.Name());
179 if (provides
.empty() == false)
180 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
183 // EDSP::WriteScenarioLimitedDependency /*{{{*/
184 void EDSP::WriteScenarioLimitedDependency(FILE* output
,
185 pkgCache::VerIterator
const &Ver
,
186 APT::PackageSet
const &pkgset
)
188 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
189 bool orGroup
= false;
190 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
192 if (Dep
.IsMultiArchImplicit() == true)
194 if (orGroup
== false)
196 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
198 dependencies
[Dep
->Type
].append(", ");
200 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
202 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
204 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
208 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
209 if (Dep
->Version
!= 0)
210 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
211 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
213 dependencies
[Dep
->Type
].append(" | ");
219 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
220 if (dependencies
[i
].empty() == false)
221 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
223 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
225 if (Prv
.IsMultiArchImplicit() == true)
227 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
229 provides
.append(", ").append(Prv
.Name());
231 if (provides
.empty() == false)
232 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
235 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
236 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
237 bool const DistUpgrade
, bool const AutoRemove
,
238 OpProgress
*Progress
)
240 if (Progress
!= NULL
)
241 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
244 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
246 if (Progress
!= NULL
&& p
% 100 == 0)
247 Progress
->Progress(p
);
249 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
250 if (P
.Delete() == true)
252 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
253 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
257 req
->append(" ").append(Pkg
.FullName());
259 fprintf(output
, "Request: EDSP 0.5\n");
261 const char *arch
= _config
->Find("APT::Architecture").c_str();
262 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
263 fprintf(output
, "Architecture: %s\n", arch
);
264 fprintf(output
, "Architectures:");
265 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
266 fprintf(output
, " %s", a
->c_str());
267 fprintf(output
, "\n");
269 if (del
.empty() == false)
270 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
271 if (inst
.empty() == false)
272 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
274 fprintf(output
, "Upgrade: yes\n");
275 if (DistUpgrade
== true)
276 fprintf(output
, "Dist-Upgrade: yes\n");
277 if (AutoRemove
== true)
278 fprintf(output
, "Autoremove: yes\n");
279 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
280 fprintf(output
, "Strict-Pinning: no\n");
281 string
solverpref("APT::Solver::");
282 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
283 if (_config
->Exists(solverpref
) == true)
284 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
285 fprintf(output
, "\n");
290 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
291 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
292 /* We build an map id to mmap offset here
293 In theory we could use the offset as ID, but then VersionCount
294 couldn't be used to create other versionmappings anymore and it
295 would be too easy for a (buggy) solver to segfault APTā¦ */
296 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
297 unsigned long VerIdx
[VersionCount
];
298 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
299 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
300 VerIdx
[V
->ID
] = V
.Index();
301 Cache
[P
].Marked
= true;
302 Cache
[P
].Garbage
= false;
306 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
307 pkgTagFile
response(&in
, 100);
308 pkgTagSection section
;
310 while (response
.Step(section
) == true) {
312 if (section
.Exists("Install") == true)
314 else if (section
.Exists("Remove") == true)
316 else if (section
.Exists("Progress") == true) {
317 if (Progress
!= NULL
) {
318 string msg
= section
.FindS("Message");
319 if (msg
.empty() == true)
320 msg
= _("Prepare for receiving solution");
321 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
324 } else if (section
.Exists("Error") == true) {
325 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
326 if (msg
.empty() == true) {
327 msg
= _("External solver failed without a proper error message");
328 _error
->Error("%s", msg
.c_str());
330 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
331 if (Progress
!= NULL
)
333 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
334 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
335 std::cerr
<< msg
<< std::endl
<< std::endl
;
337 } else if (section
.Exists("Autoremove") == true)
342 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
343 if (id
== VersionCount
) {
344 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
346 } else if (id
> Cache
.Head().VersionCount
) {
347 _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());
351 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
352 Cache
.SetCandidateVersion(Ver
);
353 if (type
== "Install")
354 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
355 else if (type
== "Remove")
356 Cache
.MarkDelete(Ver
.ParentPkg(), false);
357 else if (type
== "Autoremove") {
358 Cache
[Ver
.ParentPkg()].Marked
= false;
359 Cache
[Ver
.ParentPkg()].Garbage
= true;
365 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
366 // ---------------------------------------------------------------------
367 /* Little helper method to read a complete line into a string. Similar to
368 fgets but we need to use the low-level read() here as otherwise the
369 listparser will be confused later on as mixing of fgets and read isn't
370 a supported action according to the manpages and results are undefined */
371 bool EDSP::ReadLine(int const input
, std::string
&line
) {
376 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
383 if (line
.empty() == true && isblank(one
) != 0)
390 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
391 // ---------------------------------------------------------------------
392 /* we are not as lazy as we are in the global StringToBool as we really
393 only accept yes/no here - but we will ignore leading spaces */
394 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
395 for (; isspace(*answer
) != 0; ++answer
);
396 if (strncasecmp(answer
, "yes", 3) == 0)
398 else if (strncasecmp(answer
, "no", 2) == 0)
401 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
405 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
406 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
407 std::list
<std::string
> &remove
, bool &upgrade
,
408 bool &distUpgrade
, bool &autoRemove
)
416 while (ReadLine(input
, line
) == true)
418 // Skip empty lines before request
419 if (line
.empty() == true)
421 // The first Tag must be a request, so search for it
422 if (line
.compare(0, 8, "Request:") != 0)
425 while (ReadLine(input
, line
) == true)
427 // empty lines are the end of the request
428 if (line
.empty() == true)
431 std::list
<std::string
> *request
= NULL
;
432 if (line
.compare(0, 8, "Install:") == 0)
437 else if (line
.compare(0, 7, "Remove:") == 0)
442 else if (line
.compare(0, 8, "Upgrade:") == 0)
443 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
444 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
445 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
446 else if (line
.compare(0, 11, "Autoremove:") == 0)
447 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
448 else if (line
.compare(0, 13, "Architecture:") == 0)
449 _config
->Set("APT::Architecture", line
.c_str() + 14);
450 else if (line
.compare(0, 14, "Architectures:") == 0)
452 std::string
const archs
= line
.c_str() + 15;
453 _config
->Set("APT::Architectures", SubstVar(archs
, " ", ","));
456 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
460 size_t end
= line
.length();
462 size_t begin
= line
.rfind(' ');
463 if (begin
== std::string::npos
)
465 request
->push_back(line
.substr(0, end
));
468 else if (begin
< end
)
469 request
->push_back(line
.substr(begin
+ 1, end
));
471 end
= line
.find_last_not_of(' ');
472 } while (end
!= std::string::npos
);
478 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
479 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
480 std::list
<std::string
> const &remove
,
483 for (std::list
<std::string
>::const_iterator i
= install
.begin();
484 i
!= install
.end(); ++i
) {
485 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
487 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
489 Cache
.MarkInstall(P
, false);
492 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
493 i
!= remove
.end(); ++i
) {
494 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
496 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
503 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
504 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
506 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
507 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
509 if (Cache
[Pkg
].Delete() == true)
511 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
513 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
515 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
517 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
519 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
521 else if (Cache
[Pkg
].Garbage
== true)
523 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
525 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
529 fprintf(output
, "\n");
535 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
536 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
537 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
538 fprintf(output
, "Percentage: %d\n", percent
);
539 fprintf(output
, "Message: %s\n\n", message
);
544 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
545 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
546 fprintf(output
, "Error: %s\n", uuid
);
547 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
551 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
552 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
553 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
555 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
556 dir
!= solverDirs
.end(); ++dir
) {
557 file
= flCombine(*dir
, solver
);
558 if (RealFileExists(file
.c_str()) == true)
563 if (file
.empty() == true)
565 _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
568 int external
[4] = {-1, -1, -1, -1};
569 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
571 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
574 for (int i
= 0; i
< 4; ++i
)
575 SetCloseExec(external
[i
], true);
577 pid_t Solver
= ExecFork();
579 dup2(external
[0], STDIN_FILENO
);
580 dup2(external
[3], STDOUT_FILENO
);
581 const char* calling
[2] = { file
.c_str(), 0 };
582 execv(calling
[0], (char**) calling
);
583 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
589 if (WaitFd(external
[1], true, 5) == false)
591 _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
595 *solver_in
= external
[1];
596 *solver_out
= external
[2];
599 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
600 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
605 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
606 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
607 bool const upgrade
, bool const distUpgrade
,
608 bool const autoRemove
, OpProgress
*Progress
) {
609 int solver_in
, solver_out
;
610 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
614 FILE* output
= fdopen(solver_in
, "w");
616 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
618 if (Progress
!= NULL
)
619 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
620 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
621 if (Progress
!= NULL
)
622 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
623 EDSP::WriteScenario(Cache
, output
, Progress
);
626 if (Progress
!= NULL
)
627 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
628 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
631 return ExecWait(solver_pid
, solver
);