]>
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>
39 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
40 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
42 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
43 "Recommends" , "Conflicts", "Replaces",
44 "Obsoletes", "Breaks", "Enhances"};
46 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
47 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
50 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
52 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
53 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
55 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
56 WriteScenarioDependency(output
, Ver
);
57 fprintf(output
, "\n");
58 if (Progress
!= NULL
&& p
% 100 == 0)
59 Progress
->Progress(p
);
64 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
65 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
66 APT::PackageSet
const &pkgset
,
70 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
72 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
73 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
75 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
76 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
77 fprintf(output
, "\n");
78 if (Progress
!= NULL
&& p
% 100 == 0)
79 Progress
->Progress(p
);
86 // EDSP::WriteScenarioVersion /*{{{*/
87 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
88 pkgCache::VerIterator
const &Ver
)
90 fprintf(output
, "Package: %s\n", Pkg
.Name());
91 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
92 fprintf(output
, "Version: %s\n", Ver
.VerStr());
93 if (Pkg
.CurrentVer() == Ver
)
94 fprintf(output
, "Installed: yes\n");
95 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
96 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
97 fprintf(output
, "Hold: yes\n");
98 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
99 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
100 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
101 fprintf(output
, "Essential: yes\n");
102 fprintf(output
, "Section: %s\n", Ver
.Section());
103 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
104 fprintf(output
, "Multi-Arch: allowed\n");
105 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
106 fprintf(output
, "Multi-Arch: foreign\n");
107 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
108 fprintf(output
, "Multi-Arch: same\n");
109 signed short Pin
= std::numeric_limits
<signed short>::min();
110 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
111 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
115 fprintf(output
, "APT-Pin: %d\n", Pin
);
116 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
117 fprintf(output
, "APT-Candidate: yes\n");
118 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
119 fprintf(output
, "APT-Automatic: yes\n");
122 // EDSP::WriteScenarioDependency /*{{{*/
123 void EDSP::WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
125 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
126 bool orGroup
= false;
127 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
129 if (Dep
.IsMultiArchImplicit() == true)
131 if (orGroup
== false)
132 dependencies
[Dep
->Type
].append(", ");
133 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
134 if (Dep
->Version
!= 0)
135 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
136 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
138 dependencies
[Dep
->Type
].append(" | ");
144 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
145 if (dependencies
[i
].empty() == false)
146 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
148 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
150 if (Prv
.IsMultiArchImplicit() == true)
152 provides
.append(", ").append(Prv
.Name());
154 if (provides
.empty() == false)
155 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
158 // EDSP::WriteScenarioLimitedDependency /*{{{*/
159 void EDSP::WriteScenarioLimitedDependency(FILE* output
,
160 pkgCache::VerIterator
const &Ver
,
161 APT::PackageSet
const &pkgset
)
163 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
164 bool orGroup
= false;
165 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
167 if (Dep
.IsMultiArchImplicit() == true)
169 if (orGroup
== false)
171 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
173 dependencies
[Dep
->Type
].append(", ");
175 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
177 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
179 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
183 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
184 if (Dep
->Version
!= 0)
185 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
186 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
188 dependencies
[Dep
->Type
].append(" | ");
194 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
195 if (dependencies
[i
].empty() == false)
196 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
198 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
200 if (Prv
.IsMultiArchImplicit() == true)
202 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
204 provides
.append(", ").append(Prv
.Name());
206 if (provides
.empty() == false)
207 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
210 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
211 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
212 bool const DistUpgrade
, bool const AutoRemove
,
213 OpProgress
*Progress
)
215 if (Progress
!= NULL
)
216 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
219 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
221 if (Progress
!= NULL
&& p
% 100 == 0)
222 Progress
->Progress(p
);
224 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
225 if (P
.Delete() == true)
227 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
228 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
232 req
->append(" ").append(Pkg
.FullName());
234 fprintf(output
, "Request: EDSP 0.5\n");
236 const char *arch
= _config
->Find("APT::Architecture").c_str();
237 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
238 fprintf(output
, "Architecture: %s\n", arch
);
239 fprintf(output
, "Architectures:");
240 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
241 fprintf(output
, " %s", a
->c_str());
242 fprintf(output
, "\n");
244 if (del
.empty() == false)
245 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
246 if (inst
.empty() == false)
247 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
249 fprintf(output
, "Upgrade: yes\n");
250 if (DistUpgrade
== true)
251 fprintf(output
, "Dist-Upgrade: yes\n");
252 if (AutoRemove
== true)
253 fprintf(output
, "Autoremove: yes\n");
254 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
255 fprintf(output
, "Strict-Pinning: no\n");
256 string
solverpref("APT::Solver::");
257 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
258 if (_config
->Exists(solverpref
) == true)
259 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
260 fprintf(output
, "\n");
265 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
266 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
267 /* We build an map id to mmap offset here
268 In theory we could use the offset as ID, but then VersionCount
269 couldn't be used to create other versionmappings anymore and it
270 would be too easy for a (buggy) solver to segfault APTā¦ */
271 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
272 unsigned long VerIdx
[VersionCount
];
273 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
274 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
275 VerIdx
[V
->ID
] = V
.Index();
276 Cache
[P
].Marked
= true;
277 Cache
[P
].Garbage
= false;
281 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
282 pkgTagFile
response(&in
, 100);
283 pkgTagSection section
;
285 while (response
.Step(section
) == true) {
287 if (section
.Exists("Install") == true)
289 else if (section
.Exists("Remove") == true)
291 else if (section
.Exists("Progress") == true) {
292 if (Progress
!= NULL
) {
293 string msg
= section
.FindS("Message");
294 if (msg
.empty() == true)
295 msg
= _("Prepare for receiving solution");
296 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
299 } else if (section
.Exists("Error") == true) {
300 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
301 if (msg
.empty() == true) {
302 msg
= _("External solver failed without a proper error message");
303 _error
->Error("%s", msg
.c_str());
305 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
306 if (Progress
!= NULL
)
308 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
309 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
310 std::cerr
<< msg
<< std::endl
<< std::endl
;
312 } else if (section
.Exists("Autoremove") == true)
317 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
318 if (id
== VersionCount
) {
319 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
321 } else if (id
> Cache
.Head().VersionCount
) {
322 _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());
326 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
327 Cache
.SetCandidateVersion(Ver
);
328 if (type
== "Install")
329 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
330 else if (type
== "Remove")
331 Cache
.MarkDelete(Ver
.ParentPkg(), false);
332 else if (type
== "Autoremove") {
333 Cache
[Ver
.ParentPkg()].Marked
= false;
334 Cache
[Ver
.ParentPkg()].Garbage
= true;
340 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
341 // ---------------------------------------------------------------------
342 /* Little helper method to read a complete line into a string. Similar to
343 fgets but we need to use the low-level read() here as otherwise the
344 listparser will be confused later on as mixing of fgets and read isn't
345 a supported action according to the manpages and results are undefined */
346 bool EDSP::ReadLine(int const input
, std::string
&line
) {
351 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
358 if (line
.empty() == true && isblank(one
) != 0)
365 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
366 // ---------------------------------------------------------------------
367 /* we are not as lazy as we are in the global StringToBool as we really
368 only accept yes/no here - but we will ignore leading spaces */
369 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
370 for (; isspace(*answer
) != 0; ++answer
);
371 if (strncasecmp(answer
, "yes", 3) == 0)
373 else if (strncasecmp(answer
, "no", 2) == 0)
376 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
380 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
381 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
382 std::list
<std::string
> &remove
, bool &upgrade
,
383 bool &distUpgrade
, bool &autoRemove
)
391 while (ReadLine(input
, line
) == true)
393 // Skip empty lines before request
394 if (line
.empty() == true)
396 // The first Tag must be a request, so search for it
397 if (line
.compare(0, 8, "Request:") != 0)
400 while (ReadLine(input
, line
) == true)
402 // empty lines are the end of the request
403 if (line
.empty() == true)
406 std::list
<std::string
> *request
= NULL
;
407 if (line
.compare(0, 8, "Install:") == 0)
412 else if (line
.compare(0, 7, "Remove:") == 0)
417 else if (line
.compare(0, 8, "Upgrade:") == 0)
418 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
419 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
420 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
421 else if (line
.compare(0, 11, "Autoremove:") == 0)
422 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
424 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
428 size_t end
= line
.length();
430 size_t begin
= line
.rfind(' ');
431 if (begin
== std::string::npos
)
433 request
->push_back(line
.substr(0, end
));
436 else if (begin
< end
)
437 request
->push_back(line
.substr(begin
+ 1, end
));
439 end
= line
.find_last_not_of(' ');
440 } while (end
!= std::string::npos
);
446 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
447 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
448 std::list
<std::string
> const &remove
,
451 for (std::list
<std::string
>::const_iterator i
= install
.begin();
452 i
!= install
.end(); ++i
) {
453 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
455 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
457 Cache
.MarkInstall(P
, false);
460 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
461 i
!= remove
.end(); ++i
) {
462 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
464 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
471 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
472 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
474 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
475 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
477 if (Cache
[Pkg
].Delete() == true)
479 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
481 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
483 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
485 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
487 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
489 else if (Cache
[Pkg
].Garbage
== true)
491 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
493 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
497 fprintf(output
, "\n");
503 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
504 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
505 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
506 fprintf(output
, "Percentage: %d\n", percent
);
507 fprintf(output
, "Message: %s\n\n", message
);
512 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
513 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
514 fprintf(output
, "Error: %s\n", uuid
);
515 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
519 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
520 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
521 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
523 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
524 dir
!= solverDirs
.end(); ++dir
) {
525 file
= flCombine(*dir
, solver
);
526 if (RealFileExists(file
.c_str()) == true)
531 if (file
.empty() == true)
532 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
533 int external
[4] = {-1, -1, -1, -1};
534 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
535 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
536 for (int i
= 0; i
< 4; ++i
)
537 SetCloseExec(external
[i
], true);
539 pid_t Solver
= ExecFork();
541 dup2(external
[0], STDIN_FILENO
);
542 dup2(external
[3], STDOUT_FILENO
);
543 const char* calling
[2] = { file
.c_str(), 0 };
544 execv(calling
[0], (char**) calling
);
545 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
551 if (WaitFd(external
[1], true, 5) == false)
552 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
554 *solver_in
= external
[1];
555 *solver_out
= external
[2];
559 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
560 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
561 bool const upgrade
, bool const distUpgrade
,
562 bool const autoRemove
, OpProgress
*Progress
) {
563 int solver_in
, solver_out
;
564 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
567 FILE* output
= fdopen(solver_in
, "w");
569 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
571 if (Progress
!= NULL
)
572 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
573 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
574 if (Progress
!= NULL
)
575 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
576 EDSP::WriteScenario(Cache
, output
, Progress
);
579 if (Progress
!= NULL
)
580 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
581 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)