]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
791aac72f416364f149d133074e4fbf1747e93fb
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 // Ignore implicit dependencies for multiarch here
122 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
124 if (orGroup
== false)
125 dependencies
[Dep
->Type
].append(", ");
126 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
127 if (Dep
->Version
!= 0)
128 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
129 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
131 dependencies
[Dep
->Type
].append(" | ");
137 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
138 if (dependencies
[i
].empty() == false)
139 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
141 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
143 // Ignore implicit provides for multiarch here
144 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
146 provides
.append(", ").append(Prv
.Name());
148 if (provides
.empty() == false)
149 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
152 // EDSP::WriteScenarioLimitedDependency /*{{{*/
153 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
154 pkgCache::PkgIterator
const &Pkg
,
155 pkgCache::VerIterator
const &Ver
,
156 APT::PackageSet
const &pkgset
)
158 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
159 bool orGroup
= false;
160 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
162 // Ignore implicit dependencies for multiarch here
163 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
165 if (orGroup
== false)
167 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
169 dependencies
[Dep
->Type
].append(", ");
171 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
173 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
175 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
179 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
180 if (Dep
->Version
!= 0)
181 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
182 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
184 dependencies
[Dep
->Type
].append(" | ");
190 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
191 if (dependencies
[i
].empty() == false)
192 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
194 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
196 // Ignore implicit provides for multiarch here
197 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
199 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
201 provides
.append(", ").append(Prv
.Name());
203 if (provides
.empty() == false)
204 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
207 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
208 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
209 bool const DistUpgrade
, bool const AutoRemove
,
210 OpProgress
*Progress
)
212 if (Progress
!= NULL
)
213 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
216 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
218 if (Progress
!= NULL
&& p
% 100 == 0)
219 Progress
->Progress(p
);
221 if (Cache
[Pkg
].Delete() == true)
223 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
227 req
->append(" ").append(Pkg
.FullName());
229 fprintf(output
, "Request: EDSP 0.4\n");
230 if (del
.empty() == false)
231 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
232 if (inst
.empty() == false)
233 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
235 fprintf(output
, "Upgrade: yes\n");
236 if (DistUpgrade
== true)
237 fprintf(output
, "Dist-Upgrade: yes\n");
238 if (AutoRemove
== true)
239 fprintf(output
, "Autoremove: yes\n");
240 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
241 fprintf(output
, "Strict-Pinning: no\n");
242 string
solverpref("APT::Solver::");
243 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
244 if (_config
->Exists(solverpref
) == true)
245 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
246 fprintf(output
, "\n");
251 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
252 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
253 /* We build an map id to mmap offset here
254 In theory we could use the offset as ID, but then VersionCount
255 couldn't be used to create other versionmappings anymore and it
256 would be too easy for a (buggy) solver to segfault APTā¦ */
257 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
258 unsigned long VerIdx
[VersionCount
];
259 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
260 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
261 VerIdx
[V
->ID
] = V
.Index();
262 Cache
[P
].Marked
= true;
263 Cache
[P
].Garbage
= false;
267 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
268 pkgTagFile
response(&in
, 100);
269 pkgTagSection section
;
271 while (response
.Step(section
) == true) {
273 if (section
.Exists("Install") == true)
275 else if (section
.Exists("Remove") == true)
277 else if (section
.Exists("Progress") == true) {
278 if (Progress
!= NULL
) {
279 string msg
= section
.FindS("Message");
280 if (msg
.empty() == true)
281 msg
= _("Prepare for receiving solution");
282 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
285 } else if (section
.Exists("Error") == true) {
286 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
287 if (msg
.empty() == true) {
288 msg
= _("External solver failed without a proper error message");
289 _error
->Error("%s", msg
.c_str());
291 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
292 if (Progress
!= NULL
)
294 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
295 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
296 std::cerr
<< msg
<< std::endl
<< std::endl
;
298 } else if (section
.Exists("Autoremove") == true)
303 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
304 if (id
== VersionCount
) {
305 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
307 } else if (id
> Cache
.Head().VersionCount
) {
308 _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());
312 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
313 Cache
.SetCandidateVersion(Ver
);
314 if (type
== "Install")
315 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
316 else if (type
== "Remove")
317 Cache
.MarkDelete(Ver
.ParentPkg(), false);
318 else if (type
== "Autoremove") {
319 Cache
[Ver
.ParentPkg()].Marked
= false;
320 Cache
[Ver
.ParentPkg()].Garbage
= true;
326 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
327 // ---------------------------------------------------------------------
328 /* Little helper method to read a complete line into a string. Similar to
329 fgets but we need to use the low-level read() here as otherwise the
330 listparser will be confused later on as mixing of fgets and read isn't
331 a supported action according to the manpages and results are undefined */
332 bool EDSP::ReadLine(int const input
, std::string
&line
) {
337 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
344 if (line
.empty() == true && isblank(one
) != 0)
351 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
352 // ---------------------------------------------------------------------
353 /* we are not as lazy as we are in the global StringToBool as we really
354 only accept yes/no here - but we will ignore leading spaces */
355 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
356 for (; isspace(*answer
) != 0; ++answer
);
357 if (strncasecmp(answer
, "yes", 3) == 0)
359 else if (strncasecmp(answer
, "no", 2) == 0)
362 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
366 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
367 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
368 std::list
<std::string
> &remove
, bool &upgrade
,
369 bool &distUpgrade
, bool &autoRemove
)
377 while (ReadLine(input
, line
) == true)
379 // Skip empty lines before request
380 if (line
.empty() == true)
382 // The first Tag must be a request, so search for it
383 if (line
.compare(0, 8, "Request:") != 0)
386 while (ReadLine(input
, line
) == true)
388 // empty lines are the end of the request
389 if (line
.empty() == true)
392 std::list
<std::string
> *request
= NULL
;
393 if (line
.compare(0, 8, "Install:") == 0)
398 else if (line
.compare(0, 7, "Remove:") == 0)
403 else if (line
.compare(0, 8, "Upgrade:") == 0)
404 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
405 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
406 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
407 else if (line
.compare(0, 11, "Autoremove:") == 0)
408 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
410 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
414 size_t end
= line
.length();
416 size_t begin
= line
.rfind(' ');
417 if (begin
== std::string::npos
)
419 request
->push_back(line
.substr(0, end
));
422 else if (begin
< end
)
423 request
->push_back(line
.substr(begin
+ 1, end
));
425 end
= line
.find_last_not_of(' ');
426 } while (end
!= std::string::npos
);
432 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
433 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
434 std::list
<std::string
> const &remove
,
437 for (std::list
<std::string
>::const_iterator i
= install
.begin();
438 i
!= install
.end(); ++i
) {
439 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
441 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
443 Cache
.MarkInstall(P
, false);
446 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
447 i
!= remove
.end(); ++i
) {
448 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
450 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
457 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
458 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
460 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
461 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
463 if (Cache
[Pkg
].Delete() == true)
465 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
467 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
469 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
471 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
473 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
475 else if (Cache
[Pkg
].Garbage
== true)
477 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
479 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
480 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
484 fprintf(output
, "\n");
490 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
491 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
492 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
493 fprintf(output
, "Percentage: %d\n", percent
);
494 fprintf(output
, "Message: %s\n\n", message
);
499 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
500 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
501 fprintf(output
, "Error: %s\n", uuid
);
502 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
506 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
507 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
508 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
510 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
511 dir
!= solverDirs
.end(); ++dir
) {
512 file
= flCombine(*dir
, solver
);
513 if (RealFileExists(file
.c_str()) == true)
518 if (file
.empty() == true)
519 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
520 int external
[4] = {-1, -1, -1, -1};
521 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
522 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
523 for (int i
= 0; i
< 4; ++i
)
524 SetCloseExec(external
[i
], true);
526 pid_t Solver
= ExecFork();
528 dup2(external
[0], STDIN_FILENO
);
529 dup2(external
[3], STDOUT_FILENO
);
530 const char* calling
[2] = { file
.c_str(), 0 };
531 execv(calling
[0], (char**) calling
);
532 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
538 if (WaitFd(external
[1], true, 5) == false)
539 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
541 *solver_in
= external
[1];
542 *solver_out
= external
[2];
546 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
547 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
548 bool const upgrade
, bool const distUpgrade
,
549 bool const autoRemove
, OpProgress
*Progress
) {
550 int solver_in
, solver_out
;
551 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
554 FILE* output
= fdopen(solver_in
, "w");
556 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
558 if (Progress
!= NULL
)
559 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
560 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
561 if (Progress
!= NULL
)
562 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
563 EDSP::WriteScenario(Cache
, output
, Progress
);
566 if (Progress
!= NULL
)
567 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
568 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)