]>
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/version.h>
15 #include <apt-pkg/policy.h>
16 #include <apt-pkg/tagfile.h>
17 #include <apt-pkg/fileutl.h>
18 #include <apt-pkg/progress.h>
30 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
31 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
33 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
34 "Recommends" , "Conflicts", "Replaces",
35 "Obsoletes", "Breaks", "Enhances"};
37 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
38 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
41 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
43 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
44 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
46 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
47 WriteScenarioDependency(Cache
, output
, Pkg
, Ver
);
48 fprintf(output
, "\n");
49 if (Progress
!= NULL
&& p
% 100 == 0)
50 Progress
->Progress(p
);
55 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
56 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
57 APT::PackageSet
const &pkgset
,
61 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
63 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
64 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
66 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
67 WriteScenarioLimitedDependency(Cache
, output
, Pkg
, Ver
, pkgset
);
68 fprintf(output
, "\n");
69 if (Progress
!= NULL
&& p
% 100 == 0)
70 Progress
->Progress(p
);
77 // EDSP::WriteScenarioVersion /*{{{*/
78 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
79 pkgCache::VerIterator
const &Ver
)
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");
86 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
87 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
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());
94 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
95 fprintf(output
, "Multi-Arch: allowed\n");
96 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
97 fprintf(output
, "Multi-Arch: foreign\n");
98 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
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());
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");
113 // EDSP::WriteScenarioDependency /*{{{*/
114 void EDSP::WriteScenarioDependency(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
115 pkgCache::VerIterator
const &Ver
)
117 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
118 bool orGroup
= false;
119 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
121 if (Dep
.IsMultiArchImplicit() == true)
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
)
130 dependencies
[Dep
->Type
].append(" | ");
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);
140 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
142 if (Prv
.IsMultiArchImplicit() == true)
144 provides
.append(", ").append(Prv
.Name());
146 if (provides
.empty() == false)
147 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
150 // EDSP::WriteScenarioLimitedDependency /*{{{*/
151 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
152 pkgCache::PkgIterator
const &Pkg
,
153 pkgCache::VerIterator
const &Ver
,
154 APT::PackageSet
const &pkgset
)
156 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
157 bool orGroup
= false;
158 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
160 if (Dep
.IsMultiArchImplicit() == true)
162 if (orGroup
== false)
164 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
166 dependencies
[Dep
->Type
].append(", ");
168 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
170 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
172 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
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
)
181 dependencies
[Dep
->Type
].append(" | ");
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);
191 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
193 if (Prv
.IsMultiArchImplicit() == true)
195 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
197 provides
.append(", ").append(Prv
.Name());
199 if (provides
.empty() == false)
200 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
203 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
204 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
205 bool const DistUpgrade
, bool const AutoRemove
,
206 OpProgress
*Progress
)
208 if (Progress
!= NULL
)
209 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
212 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
214 if (Progress
!= NULL
&& p
% 100 == 0)
215 Progress
->Progress(p
);
217 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
218 if (P
.Delete() == true)
220 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
221 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
225 req
->append(" ").append(Pkg
.FullName());
227 fprintf(output
, "Request: EDSP 0.4\n");
228 if (del
.empty() == false)
229 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
230 if (inst
.empty() == false)
231 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
233 fprintf(output
, "Upgrade: yes\n");
234 if (DistUpgrade
== true)
235 fprintf(output
, "Dist-Upgrade: yes\n");
236 if (AutoRemove
== true)
237 fprintf(output
, "Autoremove: yes\n");
238 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
239 fprintf(output
, "Strict-Pinning: no\n");
240 string
solverpref("APT::Solver::");
241 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
242 if (_config
->Exists(solverpref
) == true)
243 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
244 fprintf(output
, "\n");
249 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
250 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
251 /* We build an map id to mmap offset here
252 In theory we could use the offset as ID, but then VersionCount
253 couldn't be used to create other versionmappings anymore and it
254 would be too easy for a (buggy) solver to segfault APTā¦ */
255 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
256 unsigned long VerIdx
[VersionCount
];
257 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
258 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
259 VerIdx
[V
->ID
] = V
.Index();
260 Cache
[P
].Marked
= true;
261 Cache
[P
].Garbage
= false;
265 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
266 pkgTagFile
response(&in
, 100);
267 pkgTagSection section
;
269 while (response
.Step(section
) == true) {
271 if (section
.Exists("Install") == true)
273 else if (section
.Exists("Remove") == true)
275 else if (section
.Exists("Progress") == true) {
276 if (Progress
!= NULL
) {
277 string msg
= section
.FindS("Message");
278 if (msg
.empty() == true)
279 msg
= _("Prepare for receiving solution");
280 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
283 } else if (section
.Exists("Error") == true) {
284 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
285 if (msg
.empty() == true) {
286 msg
= _("External solver failed without a proper error message");
287 _error
->Error("%s", msg
.c_str());
289 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
290 if (Progress
!= NULL
)
292 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
293 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
294 std::cerr
<< msg
<< std::endl
<< std::endl
;
296 } else if (section
.Exists("Autoremove") == true)
301 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
302 if (id
== VersionCount
) {
303 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
305 } else if (id
> Cache
.Head().VersionCount
) {
306 _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());
310 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
311 Cache
.SetCandidateVersion(Ver
);
312 if (type
== "Install")
313 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
314 else if (type
== "Remove")
315 Cache
.MarkDelete(Ver
.ParentPkg(), false);
316 else if (type
== "Autoremove") {
317 Cache
[Ver
.ParentPkg()].Marked
= false;
318 Cache
[Ver
.ParentPkg()].Garbage
= true;
324 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
325 // ---------------------------------------------------------------------
326 /* Little helper method to read a complete line into a string. Similar to
327 fgets but we need to use the low-level read() here as otherwise the
328 listparser will be confused later on as mixing of fgets and read isn't
329 a supported action according to the manpages and results are undefined */
330 bool EDSP::ReadLine(int const input
, std::string
&line
) {
335 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
342 if (line
.empty() == true && isblank(one
) != 0)
349 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
350 // ---------------------------------------------------------------------
351 /* we are not as lazy as we are in the global StringToBool as we really
352 only accept yes/no here - but we will ignore leading spaces */
353 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
354 for (; isspace(*answer
) != 0; ++answer
);
355 if (strncasecmp(answer
, "yes", 3) == 0)
357 else if (strncasecmp(answer
, "no", 2) == 0)
360 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
364 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
365 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
366 std::list
<std::string
> &remove
, bool &upgrade
,
367 bool &distUpgrade
, bool &autoRemove
)
375 while (ReadLine(input
, line
) == true)
377 // Skip empty lines before request
378 if (line
.empty() == true)
380 // The first Tag must be a request, so search for it
381 if (line
.compare(0, 8, "Request:") != 0)
384 while (ReadLine(input
, line
) == true)
386 // empty lines are the end of the request
387 if (line
.empty() == true)
390 std::list
<std::string
> *request
= NULL
;
391 if (line
.compare(0, 8, "Install:") == 0)
396 else if (line
.compare(0, 7, "Remove:") == 0)
401 else if (line
.compare(0, 8, "Upgrade:") == 0)
402 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
403 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
404 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
405 else if (line
.compare(0, 11, "Autoremove:") == 0)
406 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
408 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
412 size_t end
= line
.length();
414 size_t begin
= line
.rfind(' ');
415 if (begin
== std::string::npos
)
417 request
->push_back(line
.substr(0, end
));
420 else if (begin
< end
)
421 request
->push_back(line
.substr(begin
+ 1, end
));
423 end
= line
.find_last_not_of(' ');
424 } while (end
!= std::string::npos
);
430 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
431 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
432 std::list
<std::string
> const &remove
,
435 for (std::list
<std::string
>::const_iterator i
= install
.begin();
436 i
!= install
.end(); ++i
) {
437 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
439 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
441 Cache
.MarkInstall(P
, false);
444 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
445 i
!= remove
.end(); ++i
) {
446 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
448 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
455 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
456 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
458 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
459 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
461 if (Cache
[Pkg
].Delete() == true)
463 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
465 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
467 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
469 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
471 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
473 else if (Cache
[Pkg
].Garbage
== true)
475 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
477 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
478 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
482 fprintf(output
, "\n");
488 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
489 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
490 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
491 fprintf(output
, "Percentage: %d\n", percent
);
492 fprintf(output
, "Message: %s\n\n", message
);
497 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
498 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
499 fprintf(output
, "Error: %s\n", uuid
);
500 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
504 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
505 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
506 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
508 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
509 dir
!= solverDirs
.end(); ++dir
) {
510 file
= flCombine(*dir
, solver
);
511 if (RealFileExists(file
.c_str()) == true)
516 if (file
.empty() == true)
517 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
518 int external
[4] = {-1, -1, -1, -1};
519 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
520 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
521 for (int i
= 0; i
< 4; ++i
)
522 SetCloseExec(external
[i
], true);
524 pid_t Solver
= ExecFork();
526 dup2(external
[0], STDIN_FILENO
);
527 dup2(external
[3], STDOUT_FILENO
);
528 const char* calling
[2] = { file
.c_str(), 0 };
529 execv(calling
[0], (char**) calling
);
530 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
536 if (WaitFd(external
[1], true, 5) == false)
537 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
539 *solver_in
= external
[1];
540 *solver_out
= external
[2];
544 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
545 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
546 bool const upgrade
, bool const distUpgrade
,
547 bool const autoRemove
, OpProgress
*Progress
) {
548 int solver_in
, solver_out
;
549 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
552 FILE* output
= fdopen(solver_in
, "w");
554 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
556 if (Progress
!= NULL
)
557 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
558 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
559 if (Progress
!= NULL
)
560 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
561 EDSP::WriteScenario(Cache
, output
, Progress
);
564 if (Progress
!= NULL
)
565 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
566 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)