]>
git.saurik.com Git - apt.git/blob - apt-pkg/edsp.cc
44f7dbfd68bb9cb3f8ef53a7e7ae09407cfeb543
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/configuration.h>
13 #include <apt-pkg/version.h>
14 #include <apt-pkg/policy.h>
15 #include <apt-pkg/tagfile.h>
23 // we could use pkgCache::DepType and ::Priority, but these would be localized stringsā¦
24 const char * const EDSP::PrioMap
[] = {0, "important", "required", "standard",
26 const char * const EDSP::DepMap
[] = {"", "Depends", "Pre-Depends", "Suggests",
27 "Recommends" , "Conflicts", "Replaces",
28 "Obsoletes", "Breaks", "Enhances"};
30 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
31 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
34 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
36 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
37 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
39 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
40 WriteScenarioDependency(Cache
, output
, Pkg
, Ver
);
41 fprintf(output
, "\n");
42 if (Progress
!= NULL
&& p
% 100 == 0)
43 Progress
->Progress(p
);
48 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
49 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
50 APT::PackageSet
const &pkgset
,
54 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
56 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
57 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
59 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
60 WriteScenarioLimitedDependency(Cache
, output
, Pkg
, Ver
, pkgset
);
61 fprintf(output
, "\n");
62 if (Progress
!= NULL
&& p
% 100 == 0)
63 Progress
->Progress(p
);
70 // EDSP::WriteScenarioVersion /*{{{*/
71 void EDSP::WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
72 pkgCache::VerIterator
const &Ver
)
74 fprintf(output
, "Package: %s\n", Pkg
.Name());
75 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
76 fprintf(output
, "Version: %s\n", Ver
.VerStr());
77 if (Pkg
.CurrentVer() == Ver
)
78 fprintf(output
, "Installed: yes\n");
79 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
80 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
81 fprintf(output
, "Hold: yes\n");
82 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
83 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
84 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
85 fprintf(output
, "Essential: yes\n");
86 fprintf(output
, "Section: %s\n", Ver
.Section());
87 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
88 fprintf(output
, "Multi-Arch: allowed\n");
89 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
90 fprintf(output
, "Multi-Arch: foreign\n");
91 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
92 fprintf(output
, "Multi-Arch: same\n");
93 signed short Pin
= std::numeric_limits
<signed short>::min();
94 for (pkgCache::VerFileIterator File
= Ver
.FileList(); File
.end() == false; ++File
) {
95 signed short const p
= Cache
.GetPolicy().GetPriority(File
.File());
99 fprintf(output
, "APT-Pin: %d\n", Pin
);
100 if (Cache
.GetCandidateVer(Pkg
) == Ver
)
101 fprintf(output
, "APT-Candidate: yes\n");
102 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
103 fprintf(output
, "APT-Automatic: yes\n");
106 // EDSP::WriteScenarioDependency /*{{{*/
107 void EDSP::WriteScenarioDependency(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
108 pkgCache::VerIterator
const &Ver
)
110 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
111 bool orGroup
= false;
112 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
114 // Ignore implicit dependencies for multiarch here
115 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
117 if (orGroup
== false)
118 dependencies
[Dep
->Type
].append(", ");
119 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
120 if (Dep
->Version
!= 0)
121 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
122 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
124 dependencies
[Dep
->Type
].append(" | ");
130 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
131 if (dependencies
[i
].empty() == false)
132 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
134 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
136 // Ignore implicit provides for multiarch here
137 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
139 provides
.append(", ").append(Prv
.Name());
141 if (provides
.empty() == false)
142 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
145 // EDSP::WriteScenarioLimitedDependency /*{{{*/
146 void EDSP::WriteScenarioLimitedDependency(pkgDepCache
&Cache
, FILE* output
,
147 pkgCache::PkgIterator
const &Pkg
,
148 pkgCache::VerIterator
const &Ver
,
149 APT::PackageSet
const &pkgset
)
151 std::string dependencies
[pkgCache::Dep::Enhances
+ 1];
152 bool orGroup
= false;
153 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
155 // Ignore implicit dependencies for multiarch here
156 if (strcmp(Pkg
.Arch(), Dep
.TargetPkg().Arch()) != 0)
158 if (orGroup
== false)
160 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
162 dependencies
[Dep
->Type
].append(", ");
164 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
166 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
168 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
172 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
173 if (Dep
->Version
!= 0)
174 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
175 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
177 dependencies
[Dep
->Type
].append(" | ");
183 for (int i
= 1; i
< pkgCache::Dep::Enhances
+ 1; ++i
)
184 if (dependencies
[i
].empty() == false)
185 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
187 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
189 // Ignore implicit provides for multiarch here
190 if (strcmp(Pkg
.Arch(), Prv
.ParentPkg().Arch()) != 0 || strcmp(Pkg
.Name(),Prv
.Name()) == 0)
192 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
194 provides
.append(", ").append(Prv
.Name());
196 if (provides
.empty() == false)
197 fprintf(output
, "Provides: %s\n", provides
.c_str()+2);
200 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
201 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
202 bool const DistUpgrade
, bool const AutoRemove
,
203 OpProgress
*Progress
)
205 if (Progress
!= NULL
)
206 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
209 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
211 if (Progress
!= NULL
&& p
% 100 == 0)
212 Progress
->Progress(p
);
214 if (Cache
[Pkg
].Delete() == true)
216 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
220 req
->append(" ").append(Pkg
.FullName());
222 fprintf(output
, "Request: EDSP 0.4\n");
223 if (del
.empty() == false)
224 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
225 if (inst
.empty() == false)
226 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
228 fprintf(output
, "Upgrade: yes\n");
229 if (DistUpgrade
== true)
230 fprintf(output
, "Dist-Upgrade: yes\n");
231 if (AutoRemove
== true)
232 fprintf(output
, "Autoremove: yes\n");
233 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
234 fprintf(output
, "Strict-Pinning: no\n");
235 string
solverpref("APT::Solver::");
236 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
237 if (_config
->Exists(solverpref
) == true)
238 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
239 fprintf(output
, "\n");
244 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
245 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
246 /* We build an map id to mmap offset here
247 In theory we could use the offset as ID, but then VersionCount
248 couldn't be used to create other versionmappings anymore and it
249 would be too easy for a (buggy) solver to segfault APTā¦ */
250 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
251 unsigned long VerIdx
[VersionCount
];
252 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
253 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
254 VerIdx
[V
->ID
] = V
.Index();
255 Cache
[P
].Marked
= true;
256 Cache
[P
].Garbage
= false;
260 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
261 pkgTagFile
response(&in
, 100);
262 pkgTagSection section
;
264 while (response
.Step(section
) == true) {
266 if (section
.Exists("Install") == true)
268 else if (section
.Exists("Remove") == true)
270 else if (section
.Exists("Progress") == true) {
271 if (Progress
!= NULL
) {
272 string msg
= section
.FindS("Message");
273 if (msg
.empty() == true)
274 msg
= _("Prepare for receiving solution");
275 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
278 } else if (section
.Exists("Error") == true) {
279 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
280 if (msg
.empty() == true) {
281 msg
= _("External solver failed without a proper error message");
282 _error
->Error(msg
.c_str());
284 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
285 if (Progress
!= NULL
)
287 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
288 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
289 std::cerr
<< msg
<< std::endl
<< std::endl
;
291 } else if (section
.Exists("Autoremove") == true)
296 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
297 if (id
== VersionCount
) {
298 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
300 } else if (id
> Cache
.Head().VersionCount
) {
301 _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());
305 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
306 Cache
.SetCandidateVersion(Ver
);
307 if (type
== "Install")
308 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
309 else if (type
== "Remove")
310 Cache
.MarkDelete(Ver
.ParentPkg(), false);
311 else if (type
== "Autoremove") {
312 Cache
[Ver
.ParentPkg()].Marked
= false;
313 Cache
[Ver
.ParentPkg()].Garbage
= true;
319 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
320 // ---------------------------------------------------------------------
321 /* Little helper method to read a complete line into a string. Similar to
322 fgets but we need to use the low-level read() here as otherwise the
323 listparser will be confused later on as mixing of fgets and read isn't
324 a supported action according to the manpages and results are undefined */
325 bool EDSP::ReadLine(int const input
, std::string
&line
) {
330 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
337 if (line
.empty() == true && isblank(one
) != 0)
344 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
345 // ---------------------------------------------------------------------
346 /* we are not as lazy as we are in the global StringToBool as we really
347 only accept yes/no here - but we will ignore leading spaces */
348 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
349 for (; isspace(*answer
) != 0; ++answer
);
350 if (strncasecmp(answer
, "yes", 3) == 0)
352 else if (strncasecmp(answer
, "no", 2) == 0)
355 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
359 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
360 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
361 std::list
<std::string
> &remove
, bool &upgrade
,
362 bool &distUpgrade
, bool &autoRemove
)
370 while (ReadLine(input
, line
) == true)
372 // Skip empty lines before request
373 if (line
.empty() == true)
375 // The first Tag must be a request, so search for it
376 if (line
.compare(0, 8, "Request:") != 0)
379 while (ReadLine(input
, line
) == true)
381 // empty lines are the end of the request
382 if (line
.empty() == true)
385 std::list
<std::string
> *request
= NULL
;
386 if (line
.compare(0, 8, "Install:") == 0)
391 else if (line
.compare(0, 7, "Remove:") == 0)
396 else if (line
.compare(0, 8, "Upgrade:") == 0)
397 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
398 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
399 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
400 else if (line
.compare(0, 11, "Autoremove:") == 0)
401 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
403 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
407 size_t end
= line
.length();
409 size_t begin
= line
.rfind(' ');
410 if (begin
== std::string::npos
)
412 request
->push_back(line
.substr(0, end
));
415 else if (begin
< end
)
416 request
->push_back(line
.substr(begin
+ 1, end
));
418 end
= line
.find_last_not_of(' ');
419 } while (end
!= std::string::npos
);
425 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
426 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
427 std::list
<std::string
> const &remove
,
430 for (std::list
<std::string
>::const_iterator i
= install
.begin();
431 i
!= install
.end(); ++i
) {
432 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
434 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
436 Cache
.MarkInstall(P
, false);
439 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
440 i
!= remove
.end(); ++i
) {
441 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
443 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
450 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
451 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
453 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
454 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
456 if (Cache
[Pkg
].Delete() == true)
458 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
460 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
462 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
464 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
466 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
468 else if (Cache
[Pkg
].Garbage
== true)
470 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
472 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
473 fprintf(stderr
, "Autoremove: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
477 fprintf(output
, "\n");
483 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
484 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
485 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
486 fprintf(output
, "Percentage: %d\n", percent
);
487 fprintf(output
, "Message: %s\n\n", message
);
492 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
493 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
494 fprintf(output
, "Error: %s\n", uuid
);
495 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
499 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
500 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
501 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
503 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
504 dir
!= solverDirs
.end(); ++dir
) {
505 file
= flCombine(*dir
, solver
);
506 if (RealFileExists(file
.c_str()) == true)
511 if (file
.empty() == true)
512 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
513 int external
[4] = {-1, -1, -1, -1};
514 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
515 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
516 for (int i
= 0; i
< 4; ++i
)
517 SetCloseExec(external
[i
], true);
519 pid_t Solver
= ExecFork();
521 dup2(external
[0], STDIN_FILENO
);
522 dup2(external
[3], STDOUT_FILENO
);
523 const char* calling
[2] = { file
.c_str(), 0 };
524 execv(calling
[0], (char**) calling
);
525 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
531 if (WaitFd(external
[1], true, 5) == false)
532 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
534 *solver_in
= external
[1];
535 *solver_out
= external
[2];
539 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
540 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
541 bool const upgrade
, bool const distUpgrade
,
542 bool const autoRemove
, OpProgress
*Progress
) {
543 int solver_in
, solver_out
;
544 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
547 FILE* output
= fdopen(solver_in
, "w");
549 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
551 if (Progress
!= NULL
)
552 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
553 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
554 if (Progress
!= NULL
)
555 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
556 EDSP::WriteScenario(Cache
, output
, Progress
);
559 if (Progress
!= NULL
)
560 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
561 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)