]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
178791daeb44a553e079bccb5c7c676f44d10864
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 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
117 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
121 fprintf(output
, "APT-Pin: %d\n", Pin
);
122 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
123 fprintf(output
, "APT-Candidate: yes\n");
124 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
125 fprintf(output
, "APT-Automatic: yes\n");
128 // EDSP::WriteScenarioDependency /*{{{*/
129 void EDSP::WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
131 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
132 bool orGroup
= false;
133 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
135 if (Dep
.IsMultiArchImplicit() == true)
137 if (orGroup
== false)
138 dependencies
[Dep
->Type
].append(", ");
139 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
140 if (Dep
->Version
!= 0)
141 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
142 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
144 dependencies
[Dep
->Type
].append(" | ");
150 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
151 if (dependencies
[i
].empty() == false)
152 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
154 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
156 if (Prv
.IsMultiArchImplicit() == true)
158 provides
.append(", ").append(Prv
.Name());
160 if (provides
.empty() == false)
161 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
164 // EDSP::WriteScenarioLimitedDependency /*{{{*/
165 void EDSP::WriteScenarioLimitedDependency(FILE* output
,
166 pkgCache::VerIterator
const &Ver
,
167 APT::PackageSet
const &pkgset
)
169 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
170 bool orGroup
= false;
171 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
173 if (Dep
.IsMultiArchImplicit() == true)
175 if (orGroup
== false)
177 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
179 dependencies
[Dep
->Type
].append(", ");
181 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
183 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
185 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
189 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
190 if (Dep
->Version
!= 0)
191 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
192 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
194 dependencies
[Dep
->Type
].append(" | ");
200 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
201 if (dependencies
[i
].empty() == false)
202 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
204 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
206 if (Prv
.IsMultiArchImplicit() == true)
208 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
210 provides
.append(", ").append(Prv
.Name());
212 if (provides
.empty() == false)
213 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
216 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
217 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
218 bool const DistUpgrade
, bool const AutoRemove
,
219 OpProgress
*Progress
)
221 if (Progress
!= NULL
)
222 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
225 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
227 if (Progress
!= NULL
&& p
% 100 == 0)
228 Progress
->Progress(p
);
230 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
231 if (P
.Delete() == true)
233 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
234 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
238 req
->append(" ").append(Pkg
.FullName());
240 fprintf(output
, "Request: EDSP 0.5\n");
242 const char *arch
= _config
->Find("APT::Architecture").c_str();
243 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
244 fprintf(output
, "Architecture: %s\n", arch
);
245 fprintf(output
, "Architectures:");
246 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
247 fprintf(output
, " %s", a
->c_str());
248 fprintf(output
, "\n");
250 if (del
.empty() == false)
251 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
252 if (inst
.empty() == false)
253 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
255 fprintf(output
, "Upgrade: yes\n");
256 if (DistUpgrade
== true)
257 fprintf(output
, "Dist-Upgrade: yes\n");
258 if (AutoRemove
== true)
259 fprintf(output
, "Autoremove: yes\n");
260 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
261 fprintf(output
, "Strict-Pinning: no\n");
262 string
solverpref("APT::Solver::");
263 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
264 if (_config
->Exists(solverpref
) == true)
265 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
266 fprintf(output
, "\n");
271 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
272 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
273 /* We build an map id to mmap offset here
274 In theory we could use the offset as ID, but then VersionCount
275 couldn't be used to create other versionmappings anymore and it
276 would be too easy for a (buggy) solver to segfault APT⦠*/
277 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
278 unsigned long VerIdx
[VersionCount
];
279 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
280 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
281 VerIdx
[V
->ID
] = V
.Index();
282 Cache
[P
].Marked
= true;
283 Cache
[P
].Garbage
= false;
287 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
288 pkgTagFile
response(&in
, 100);
289 pkgTagSection section
;
291 while (response
.Step(section
) == true) {
293 if (section
.Exists("Install") == true)
295 else if (section
.Exists("Remove") == true)
297 else if (section
.Exists("Progress") == true) {
298 if (Progress
!= NULL
) {
299 string msg
= section
.FindS("Message");
300 if (msg
.empty() == true)
301 msg
= _("Prepare for receiving solution");
302 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
305 } else if (section
.Exists("Error") == true) {
306 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
307 if (msg
.empty() == true) {
308 msg
= _("External solver failed without a proper error message");
309 _error
->Error("%s", msg
.c_str());
311 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
312 if (Progress
!= NULL
)
314 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
315 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
316 std::cerr
<< msg
<< std::endl
<< std::endl
;
318 } else if (section
.Exists("Autoremove") == true)
323 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
324 if (id
== VersionCount
) {
325 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
327 } else if (id
> Cache
.Head().VersionCount
) {
328 _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());
332 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
333 Cache
.SetCandidateVersion(Ver
);
334 if (type
== "Install")
335 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
336 else if (type
== "Remove")
337 Cache
.MarkDelete(Ver
.ParentPkg(), false);
338 else if (type
== "Autoremove") {
339 Cache
[Ver
.ParentPkg()].Marked
= false;
340 Cache
[Ver
.ParentPkg()].Garbage
= true;
346 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
347 // ---------------------------------------------------------------------
348 /* Little helper method to read a complete line into a string. Similar to
349 fgets but we need to use the low-level read() here as otherwise the
350 listparser will be confused later on as mixing of fgets and read isn't
351 a supported action according to the manpages and results are undefined */
352 bool EDSP::ReadLine(int const input
, std::string
&line
) {
357 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
364 if (line
.empty() == true && isblank(one
) != 0)
371 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
372 // ---------------------------------------------------------------------
373 /* we are not as lazy as we are in the global StringToBool as we really
374 only accept yes/no here - but we will ignore leading spaces */
375 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
376 for (; isspace(*answer
) != 0; ++answer
);
377 if (strncasecmp(answer
, "yes", 3) == 0)
379 else if (strncasecmp(answer
, "no", 2) == 0)
382 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
386 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
387 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
388 std::list
<std::string
> &remove
, bool &upgrade
,
389 bool &distUpgrade
, bool &autoRemove
)
397 while (ReadLine(input
, line
) == true)
399 // Skip empty lines before request
400 if (line
.empty() == true)
402 // The first Tag must be a request, so search for it
403 if (line
.compare(0, 8, "Request:") != 0)
406 while (ReadLine(input
, line
) == true)
408 // empty lines are the end of the request
409 if (line
.empty() == true)
412 std::list
<std::string
> *request
= NULL
;
413 if (line
.compare(0, 8, "Install:") == 0)
418 else if (line
.compare(0, 7, "Remove:") == 0)
423 else if (line
.compare(0, 8, "Upgrade:") == 0)
424 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
425 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
426 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
427 else if (line
.compare(0, 11, "Autoremove:") == 0)
428 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
430 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
434 size_t end
= line
.length();
436 size_t begin
= line
.rfind(' ');
437 if (begin
== std::string::npos
)
439 request
->push_back(line
.substr(0, end
));
442 else if (begin
< end
)
443 request
->push_back(line
.substr(begin
+ 1, end
));
445 end
= line
.find_last_not_of(' ');
446 } while (end
!= std::string::npos
);
452 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
453 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
454 std::list
<std::string
> const &remove
,
457 for (std::list
<std::string
>::const_iterator i
= install
.begin();
458 i
!= install
.end(); ++i
) {
459 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
461 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
463 Cache
.MarkInstall(P
, false);
466 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
467 i
!= remove
.end(); ++i
) {
468 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
470 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
477 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
478 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
480 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
481 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
483 if (Cache
[Pkg
].Delete() == true)
485 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
487 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
489 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
491 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
493 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
495 else if (Cache
[Pkg
].Garbage
== true)
497 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
499 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
503 fprintf(output
, "\n");
509 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
510 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
511 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
512 fprintf(output
, "Percentage: %d\n", percent
);
513 fprintf(output
, "Message: %s\n\n", message
);
518 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
519 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
520 fprintf(output
, "Error: %s\n", uuid
);
521 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
525 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
526 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
527 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
529 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
530 dir
!= solverDirs
.end(); ++dir
) {
531 file
= flCombine(*dir
, solver
);
532 if (RealFileExists(file
.c_str()) == true)
537 if (file
.empty() == true)
538 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
539 int external
[4] = {-1, -1, -1, -1};
540 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
541 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
542 for (int i
= 0; i
< 4; ++i
)
543 SetCloseExec(external
[i
], true);
545 pid_t Solver
= ExecFork();
547 dup2(external
[0], STDIN_FILENO
);
548 dup2(external
[3], STDOUT_FILENO
);
549 const char* calling
[2] = { file
.c_str(), 0 };
550 execv(calling
[0], (char**) calling
);
551 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
557 if (WaitFd(external
[1], true, 5) == false)
558 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
560 *solver_in
= external
[1];
561 *solver_out
= external
[2];
565 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
566 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
567 bool const upgrade
, bool const distUpgrade
,
568 bool const autoRemove
, OpProgress
*Progress
) {
569 int solver_in
, solver_out
;
570 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
573 FILE* output
= fdopen(solver_in
, "w");
575 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
577 if (Progress
!= NULL
)
578 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
579 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
580 if (Progress
!= NULL
)
581 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
582 EDSP::WriteScenario(Cache
, output
, Progress
);
585 if (Progress
!= NULL
)
586 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
587 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)