]>
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.4\n");
235 if (del
.empty() == false)
236 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
237 if (inst
.empty() == false)
238 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
240 fprintf(output
, "Upgrade: yes\n");
241 if (DistUpgrade
== true)
242 fprintf(output
, "Dist-Upgrade: yes\n");
243 if (AutoRemove
== true)
244 fprintf(output
, "Autoremove: yes\n");
245 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
246 fprintf(output
, "Strict-Pinning: no\n");
247 string
solverpref("APT::Solver::");
248 solverpref
.append(_config
->Find("APT::Solver", "internal")).append("::Preferences");
249 if (_config
->Exists(solverpref
) == true)
250 fprintf(output
, "Preferences: %s\n", _config
->Find(solverpref
,"").c_str());
251 fprintf(output
, "\n");
256 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
257 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
258 /* We build an map id to mmap offset here
259 In theory we could use the offset as ID, but then VersionCount
260 couldn't be used to create other versionmappings anymore and it
261 would be too easy for a (buggy) solver to segfault APTā¦ */
262 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
263 unsigned long VerIdx
[VersionCount
];
264 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
265 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
266 VerIdx
[V
->ID
] = V
.Index();
267 Cache
[P
].Marked
= true;
268 Cache
[P
].Garbage
= false;
272 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
273 pkgTagFile
response(&in
, 100);
274 pkgTagSection section
;
276 while (response
.Step(section
) == true) {
278 if (section
.Exists("Install") == true)
280 else if (section
.Exists("Remove") == true)
282 else if (section
.Exists("Progress") == true) {
283 if (Progress
!= NULL
) {
284 string msg
= section
.FindS("Message");
285 if (msg
.empty() == true)
286 msg
= _("Prepare for receiving solution");
287 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
290 } else if (section
.Exists("Error") == true) {
291 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
292 if (msg
.empty() == true) {
293 msg
= _("External solver failed without a proper error message");
294 _error
->Error("%s", msg
.c_str());
296 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
297 if (Progress
!= NULL
)
299 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
300 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
301 std::cerr
<< msg
<< std::endl
<< std::endl
;
303 } else if (section
.Exists("Autoremove") == true)
308 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
309 if (id
== VersionCount
) {
310 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
312 } else if (id
> Cache
.Head().VersionCount
) {
313 _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());
317 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
318 Cache
.SetCandidateVersion(Ver
);
319 if (type
== "Install")
320 Cache
.MarkInstall(Ver
.ParentPkg(), false, 0, false);
321 else if (type
== "Remove")
322 Cache
.MarkDelete(Ver
.ParentPkg(), false);
323 else if (type
== "Autoremove") {
324 Cache
[Ver
.ParentPkg()].Marked
= false;
325 Cache
[Ver
.ParentPkg()].Garbage
= true;
331 // EDSP::ReadLine - first line from the given file descriptor /*{{{*/
332 // ---------------------------------------------------------------------
333 /* Little helper method to read a complete line into a string. Similar to
334 fgets but we need to use the low-level read() here as otherwise the
335 listparser will be confused later on as mixing of fgets and read isn't
336 a supported action according to the manpages and results are undefined */
337 bool EDSP::ReadLine(int const input
, std::string
&line
) {
342 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
349 if (line
.empty() == true && isblank(one
) != 0)
356 // EDSP::StringToBool - convert yes/no to bool /*{{{*/
357 // ---------------------------------------------------------------------
358 /* we are not as lazy as we are in the global StringToBool as we really
359 only accept yes/no here - but we will ignore leading spaces */
360 bool EDSP::StringToBool(char const *answer
, bool const defValue
) {
361 for (; isspace(*answer
) != 0; ++answer
);
362 if (strncasecmp(answer
, "yes", 3) == 0)
364 else if (strncasecmp(answer
, "no", 2) == 0)
367 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
);
371 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
372 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
373 std::list
<std::string
> &remove
, bool &upgrade
,
374 bool &distUpgrade
, bool &autoRemove
)
382 while (ReadLine(input
, line
) == true)
384 // Skip empty lines before request
385 if (line
.empty() == true)
387 // The first Tag must be a request, so search for it
388 if (line
.compare(0, 8, "Request:") != 0)
391 while (ReadLine(input
, line
) == true)
393 // empty lines are the end of the request
394 if (line
.empty() == true)
397 std::list
<std::string
> *request
= NULL
;
398 if (line
.compare(0, 8, "Install:") == 0)
403 else if (line
.compare(0, 7, "Remove:") == 0)
408 else if (line
.compare(0, 8, "Upgrade:") == 0)
409 upgrade
= EDSP::StringToBool(line
.c_str() + 9, false);
410 else if (line
.compare(0, 13, "Dist-Upgrade:") == 0)
411 distUpgrade
= EDSP::StringToBool(line
.c_str() + 14, false);
412 else if (line
.compare(0, 11, "Autoremove:") == 0)
413 autoRemove
= EDSP::StringToBool(line
.c_str() + 12, false);
415 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
419 size_t end
= line
.length();
421 size_t begin
= line
.rfind(' ');
422 if (begin
== std::string::npos
)
424 request
->push_back(line
.substr(0, end
));
427 else if (begin
< end
)
428 request
->push_back(line
.substr(begin
+ 1, end
));
430 end
= line
.find_last_not_of(' ');
431 } while (end
!= std::string::npos
);
437 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
438 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
439 std::list
<std::string
> const &remove
,
442 for (std::list
<std::string
>::const_iterator i
= install
.begin();
443 i
!= install
.end(); ++i
) {
444 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
446 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
448 Cache
.MarkInstall(P
, false);
451 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
452 i
!= remove
.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());
462 // EDSP::WriteSolution - to the given file descriptor /*{{{*/
463 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
465 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
466 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
468 if (Cache
[Pkg
].Delete() == true)
470 fprintf(output
, "Remove: %d\n", Pkg
.CurrentVer()->ID
);
472 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
474 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
476 fprintf(output
, "Install: %d\n", Cache
.GetCandidateVer(Pkg
)->ID
);
478 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Cache
.GetCandidateVer(Pkg
).VerStr());
480 else if (Cache
[Pkg
].Garbage
== true)
482 fprintf(output
, "Autoremove: %d\n", Pkg
.CurrentVer()->ID
);
484 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
488 fprintf(output
, "\n");
494 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
495 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
496 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
)).c_str());
497 fprintf(output
, "Percentage: %d\n", percent
);
498 fprintf(output
, "Message: %s\n\n", message
);
503 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
504 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
505 fprintf(output
, "Error: %s\n", uuid
);
506 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
510 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
511 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
512 std::vector
<std::string
> const solverDirs
= _config
->FindVector("Dir::Bin::Solvers");
514 for (std::vector
<std::string
>::const_iterator dir
= solverDirs
.begin();
515 dir
!= solverDirs
.end(); ++dir
) {
516 file
= flCombine(*dir
, solver
);
517 if (RealFileExists(file
.c_str()) == true)
522 if (file
.empty() == true)
523 return _error
->Error("Can't call external solver '%s' as it is not in a configured directory!", solver
);
524 int external
[4] = {-1, -1, -1, -1};
525 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
526 return _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
527 for (int i
= 0; i
< 4; ++i
)
528 SetCloseExec(external
[i
], true);
530 pid_t Solver
= ExecFork();
532 dup2(external
[0], STDIN_FILENO
);
533 dup2(external
[3], STDOUT_FILENO
);
534 const char* calling
[2] = { file
.c_str(), 0 };
535 execv(calling
[0], (char**) calling
);
536 std::cerr
<< "Failed to execute solver '" << solver
<< "'!" << std::endl
;
542 if (WaitFd(external
[1], true, 5) == false)
543 return _error
->Errno("Resolve", "Timed out while Waiting on availability of solver stdin");
545 *solver_in
= external
[1];
546 *solver_out
= external
[2];
550 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
551 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
552 bool const upgrade
, bool const distUpgrade
,
553 bool const autoRemove
, OpProgress
*Progress
) {
554 int solver_in
, solver_out
;
555 if (EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
) == false)
558 FILE* output
= fdopen(solver_in
, "w");
560 return _error
->Errno("Resolve", "fdopen on solver stdin failed");
562 if (Progress
!= NULL
)
563 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
564 EDSP::WriteRequest(Cache
, output
, upgrade
, distUpgrade
, autoRemove
, Progress
);
565 if (Progress
!= NULL
)
566 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
567 EDSP::WriteScenario(Cache
, output
, Progress
);
570 if (Progress
!= NULL
)
571 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
572 if (EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)