]>
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>
40 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
41 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
43 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
44 "Recommends" , "Conflicts", "Replaces",
45 "Obsoletes", "Breaks", "Enhances"};
47 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
48 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
51 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
53 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
54 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
56 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
57 WriteScenarioDependency(output
, Ver
);
58 fprintf(output
, "\n");
59 if (Progress
!= NULL
&& p
% 100 == 0)
60 Progress
->Progress(p
);
65 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
66 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
67 APT::PackageSet
const &pkgset
,
71 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
73 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
74 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
76 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
77 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
78 fprintf(output
, "\n");
79 if (Progress
!= NULL
&& p
% 100 == 0)
80 Progress
->Progress(p
);
87 // EDSP::WriteScenarioVersion /*{{{*/
88 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
89 pkgCache::VerIterator
const &Ver
)
91 pkgRecords
Recs(Cache
);
92 pkgRecords::Parser
&rec
= Recs
.Lookup(Ver
.FileList());
93 string srcpkg
= rec
.SourcePkg().empty() ? Pkg
.Name() : rec
.SourcePkg();
95 fprintf(output
, "Package: %s\n", Pkg
.Name());
96 fprintf(output
, "Source: %s\n", srcpkg
.c_str());
97 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
98 fprintf(output
, "Version: %s\n", Ver
.VerStr());
99 if (Pkg
.CurrentVer() == Ver
)
100 fprintf(output
, "Installed: yes\n");
101 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
102 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
103 fprintf(output
, "Hold: yes\n");
104 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
105 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
106 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
107 fprintf(output
, "Essential: yes\n");
108 fprintf(output
, "Section: %s\n", Ver
.Section());
109 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
110 fprintf(output
, "Multi-Arch: allowed\n");
111 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
112 fprintf(output
, "Multi-Arch: foreign\n");
113 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
114 fprintf(output
, "Multi-Arch: same\n");
115 signed short Pin
= std::numeric_limits
<signed short>::min();
116 std::set
<string
> Releases
;
117 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
118 pkgCache::PkgFileIterator File
= I
.File();
119 signed short const p
= Cache
.GetPolicy().GetPriority(File
);
122 if ((File
->Flags
& pkgCache::Flag::NotSource
) != pkgCache::Flag::NotSource
) {
123 string Release
= File
.RelStr();
124 if (!Release
.empty())
125 Releases
.insert(Release
);
128 if (!Releases
.empty()) {
129 fprintf(output
, "APT-Release:\n");
130 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
131 fprintf(output
, " %s\n", R
->c_str());
133 fprintf(output
, "APT-Pin: %d\n", Pin
);
134 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
135 fprintf(output
, "APT-Candidate: yes\n");
136 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
137 fprintf(output
, "APT-Automatic: yes\n");
140 // EDSP::WriteScenarioDependency /*{{{*/
141 void EDSP::WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
143 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
144 bool orGroup
= false;
145 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
147 if (Dep
.IsMultiArchImplicit() == true)
149 if (orGroup
== false)
150 dependencies
[Dep
->Type
].append(", ");
151 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
152 if (Dep
->Version
!= 0)
153 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
154 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
156 dependencies
[Dep
->Type
].append(" | ");
162 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
163 if (dependencies
[i
].empty() == false)
164 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
166 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
168 if (Prv
.IsMultiArchImplicit() == true)
170 provides
.append(", ").append(Prv
.Name());
172 if (provides
.empty() == false)
173 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
176 // EDSP::WriteScenarioLimitedDependency /*{{{*/
177 void EDSP::WriteScenarioLimitedDependency(FILE* output
,
178 pkgCache::VerIterator
const &Ver
,
179 APT::PackageSet
const &pkgset
)
181 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
182 bool orGroup
= false;
183 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
185 if (Dep
.IsMultiArchImplicit() == true)
187 if (orGroup
== false)
189 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
191 dependencies
[Dep
->Type
].append(", ");
193 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
195 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
197 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
201 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
202 if (Dep
->Version
!= 0)
203 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
204 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
206 dependencies
[Dep
->Type
].append(" | ");
212 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
213 if (dependencies
[i
].empty() == false)
214 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
216 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
218 if (Prv
.IsMultiArchImplicit() == true)
220 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
222 provides
.append(", ").append(Prv
.Name());
224 if (provides
.empty() == false)
225 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
228 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
229 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
230 bool const DistUpgrade
, bool const AutoRemove
,
231 OpProgress
*Progress
)
233 if (Progress
!= NULL
)
234 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
237 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
239 if (Progress
!= NULL
&& p
% 100 == 0)
240 Progress
->Progress(p
);
242 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
243 if (P
.Delete() == true)
245 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
246 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
250 req
->append(" ").append(Pkg
.FullName());
252 fprintf(output
, "Request: EDSP 0.5\n");
254 const char *arch
= _config
->Find("APT::Architecture").c_str();
255 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
256 fprintf(output
, "Architecture: %s\n", arch
);
257 fprintf(output
, "Architectures:");
258 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
259 fprintf(output
, " %s", a
->c_str());
260 fprintf(output
, "\n");
262 if (del
.empty() == false)
263 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
264 if (inst
.empty() == false)
265 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
267 fprintf(output
, "Upgrade: yes\n");
268 if (DistUpgrade
== true)
269 fprintf(output
, "Dist-Upgrade: yes\n");
270 if (AutoRemove
== true)
271 fprintf(output
, "Autoremove: yes\n");
272 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
273 fprintf(output
, "Strict-Pinning: no\n");
274 string
solverpref("APT::Solver::");
275 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
276 if (_config
->Exists(solverpref
) == true)
277 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
278 fprintf(output
, "\n");
283 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
284 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
285 /* We build an map id to mmap offset here
286 In theory we could use the offset as ID, but then VersionCount
287 couldn't be used to create other versionmappings anymore and it
288 would be too easy for a (buggy) solver to segfault APTā¦ */
289 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
290 unsigned long VerIdx
[VersionCount
];
291 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
292 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
293 VerIdx
[V
->ID
] = V
.Index();
294 Cache
[P
].Marked
= true;
295 Cache
[P
].Garbage
= false;
299 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
300 pkgTagFile
response(&in
, 100);
301 pkgTagSection section
;
303 while (response
.Step(section
) == true) {
305 if (section
.Exists("Install") == true)
307 else if (section
.Exists("Remove") == true)
309 else if (section
.Exists("Progress") == true) {
310 if (Progress
!= NULL
) {
311 string msg
= section
.FindS("Message");
312 if (msg
.empty() == true)
313 msg
= _("Prepare for receiving solution");
314 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
317 } else if (section
.Exists("Error") == true) {
318 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
319 if (msg
.empty() == true) {
320 msg
= _("External solver failed without a proper error message");
321 _error
->Error("%s", msg
.c_str());
323 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
324 if (Progress
!= NULL
)
326 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
327 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
328 std::cerr
<< msg
<< std::endl
<< std::endl
;
330 } else if (section
.Exists("Autoremove") == true)
335 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
336 if (id
== VersionCount
) {
337 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
339 } else if (id
> Cache
.Head().VersionCount
) {
340 _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());
344 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
345 Cache
.SetCandidateVersion(Ver
);
346 if (type
== "Install")
347 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
348 else if (type
== "Remove")
349 Cache
.MarkDelete(Ver
.ParentPkg(), false);
350 else if (type
== "Autoremove") {
351 Cache
[Ver
.ParentPkg()].Marked
= false;
352 Cache
[Ver
.ParentPkg()].Garbage
= true;
358 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
359 // ---------------------------------------------------------------------
360 /* Little helper method to read a complete line into a string. Similar to
361 fgets but we need to use the low-level read() here as otherwise the
362 listparser will be confused later on as mixing of fgets and read isn't
363 a supported action according to the manpages and results are undefined */
364 bool EDSP::ReadLine(int const input
, std::string
&line
) {
369 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
376 if (line
.empty() == true && isblank(one
) != 0)
383 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
384 // ---------------------------------------------------------------------
385 /* we are not as lazy as we are in the global StringToBool as we really
386 only accept yes/no here - but we will ignore leading spaces */
387 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
388 for (; isspace(*answer
) != 0; ++answer
);
389 if (strncasecmp(answer
, "yes", 3) == 0)
391 else if (strncasecmp(answer
, "no", 2) == 0)
394 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
398 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
399 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
400 std::list
<std::string
> &remove
, bool &upgrade
,
401 bool &distUpgrade
, bool &autoRemove
)
409 while (ReadLine(input
, line
) == true)
411 // Skip empty lines before request
412 if (line
.empty() == true)
414 // The first Tag must be a request, so search for it
415 if (line
.compare(0, 8, "Request:") != 0)
418 while (ReadLine(input
, line
) == true)
420 // empty lines are the end of the request
421 if (line
.empty() == true)
424 std::list
<std::string
> *request
= NULL
;
425 if (line
.compare(0, 8, "Install:") == 0)
430 else if (line
.compare(0, 7, "Remove:") == 0)
435 else if (line
.compare(0, 8, "Upgrade:") == 0)
436 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
437 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
438 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
439 else if (line
.compare(0, 11, "Autoremove:") == 0)
440 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
442 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
446 size_t end
= line
.length();
448 size_t begin
= line
.rfind(' ');
449 if (begin
== std::string::npos
)
451 request
->push_back(line
.substr(0, end
));
454 else if (begin
< end
)
455 request
->push_back(line
.substr(begin
+ 1, end
));
457 end
= line
.find_last_not_of(' ');
458 } while (end
!= std::string::npos
);
464 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
465 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
466 std::list
<std::string
> const &remove
,
469 for (std::list
<std::string
>::const_iterator i
= install
.begin();
470 i
!= install
.end(); ++i
) {
471 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
473 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
475 Cache
.MarkInstall(P
, false);
478 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
479 i
!= remove
.end(); ++i
) {
480 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
482 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
489 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
490 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
492 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
493 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
495 if (Cache
[Pkg
].Delete() == true)
497 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
499 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
501 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
503 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
505 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
507 else if (Cache
[Pkg
].Garbage
== true)
509 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
511 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
515 fprintf(output
, "\n");
521 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
522 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
523 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
524 fprintf(output
, "Percentage: %d\n", percent
);
525 fprintf(output
, "Message: %s\n\n", message
);
530 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
531 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
532 fprintf(output
, "Error: %s\n", uuid
);
533 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
537 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
538 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
539 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
541 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
542 dir
!= solverDirs
.end(); ++dir
) {
543 file
= flCombine(*dir
, solver
);
544 if (RealFileExists(file
.c_str()) == true)
549 if (file
.empty() == true)
550 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
551 int external
[4] = {-1, -1, -1, -1};
552 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
553 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
554 for (int i
= 0; i
< 4; ++i
)
555 SetCloseExec(external
[i
], true);
557 pid_t Solver
= ExecFork();
559 dup2(external
[0], STDIN_FILENO
);
560 dup2(external
[3], STDOUT_FILENO
);
561 const char* calling
[2] = { file
.c_str(), 0 };
562 execv(calling
[0], (char**) calling
);
563 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
569 if (WaitFd(external
[1], true, 5) == false)
570 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
572 *solver_in
= external
[1];
573 *solver_out
= external
[2];
577 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
578 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
579 bool const upgrade
, bool const distUpgrade
,
580 bool const autoRemove
, OpProgress
*Progress
) {
581 int solver_in
, solver_out
;
582 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
585 FILE* output
= fdopen(solver_in
, "w");
587 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
589 if (Progress
!= NULL
)
590 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
591 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
592 if (Progress
!= NULL
)
593 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
594 EDSP::WriteScenario(Cache
, output
, Progress
);
597 if (Progress
!= NULL
)
598 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
599 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)