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/algorithms.h>
11 #include <apt-pkg/error.h>
12 #include <apt-pkg/cacheset.h>
13 #include <apt-pkg/depcache.h>
14 #include <apt-pkg/pkgcache.h>
15 #include <apt-pkg/cacheiterators.h>
16 #include <apt-pkg/prettyprinters.h>
17 #include <apt-pkg/packagemanager.h>
18 #include <apt-pkg/progress.h>
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/edsp.h>
21 #include <apt-pkg/tagfile.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/string_view.h>
24 #include <apt-pkg/pkgsystem.h>
42 // we could use pkgCache::DepType and ::Priority, but these would be localized strings…
43 constexpr char const * const PrioMap
[] = {
44 nullptr, "important", "required", "standard",
47 constexpr char const * const DepMap
[] = {
48 nullptr, "Depends", "Pre-Depends", "Suggests",
49 "Recommends" , "Conflicts", "Replaces",
50 "Obsoletes", "Breaks", "Enhances"
53 // WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
54 static bool WriteOkay_fn(FileFd
&) { return true; }
55 template<typename
... Tail
> static bool WriteOkay_fn(FileFd
&output
, APT::StringView data
, Tail
... more_data
)
57 return likely(output
.Write(data
.data(), data
.length()) && WriteOkay_fn(output
, more_data
...));
59 template<typename
... Tail
> static bool WriteOkay_fn(FileFd
&output
, unsigned int data
, Tail
... more_data
)
62 strprintf(number
, "%d", data
);
63 return likely(output
.Write(number
.data(), number
.length()) && WriteOkay_fn(output
, more_data
...));
65 template<typename
... Data
> static bool WriteOkay(bool &Okay
, FileFd
&output
, Data
&&... data
)
67 Okay
= likely(Okay
&& WriteOkay_fn(output
, std::forward
<Data
>(data
)...));
70 template<typename
... Data
> static bool WriteOkay(FileFd
&output
, Data
&&... data
)
72 bool Okay
= likely(output
.Failed() == false);
73 return WriteOkay(Okay
, output
, std::forward
<Data
>(data
)...);
76 // WriteScenarioVersion /*{{{*/
77 static void WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
78 pkgCache::VerIterator
const &Ver
)
80 fprintf(output
, "Package: %s\n", Pkg
.Name());
81 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
82 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
83 fprintf(output
, "Version: %s\n", Ver
.VerStr());
84 fprintf(output
, "Source-Version: %s\n", Ver
.SourceVerStr());
85 if (Pkg
.CurrentVer() == Ver
)
86 fprintf(output
, "Installed: yes\n");
87 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
88 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
89 fprintf(output
, "Hold: yes\n");
90 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
91 if (PrioMap
[Ver
->Priority
] != nullptr)
92 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
93 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
94 fprintf(output
, "Essential: yes\n");
95 if (Ver
->Section
!= 0)
96 fprintf(output
, "Section: %s\n", Ver
.Section());
97 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
98 fprintf(output
, "Multi-Arch: allowed\n");
99 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
100 fprintf(output
, "Multi-Arch: foreign\n");
101 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
102 fprintf(output
, "Multi-Arch: same\n");
103 std::set
<string
> Releases
;
104 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
105 pkgCache::PkgFileIterator File
= I
.File();
106 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
107 string Release
= File
.RelStr();
108 if (!Release
.empty())
109 Releases
.insert(Release
);
112 if (!Releases
.empty()) {
113 fprintf(output
, "APT-Release:\n");
114 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
115 fprintf(output
, " %s\n", R
->c_str());
117 fprintf(output
, "APT-Pin: %d\n", Cache
.GetPolicy().GetPriority(Ver
));
118 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
119 fprintf(output
, "APT-Candidate: yes\n");
120 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
121 fprintf(output
, "APT-Automatic: yes\n");
123 static bool WriteScenarioVersion(FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,
124 pkgCache::VerIterator
const &Ver
)
126 bool Okay
= WriteOkay(output
, "Package: ", Pkg
.Name(),
127 "\nArchitecture: ", Ver
.Arch(),
128 "\nVersion: ", Ver
.VerStr());
129 WriteOkay(Okay
, output
, "\nAPT-ID: ", Ver
->ID
);
130 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
131 WriteOkay(Okay
, output
, "\nEssential: yes");
132 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
133 WriteOkay(Okay
, output
, "\nMulti-Arch: allowed");
134 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
135 WriteOkay(Okay
, output
, "\nMulti-Arch: foreign");
136 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
137 WriteOkay(Okay
, output
, "\nMulti-Arch: same");
141 // WriteScenarioDependency /*{{{*/
142 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
144 std::array
<std::string
, _count(DepMap
)> dependencies
;
145 bool orGroup
= false;
146 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
148 if (Dep
.IsImplicit() == true)
150 if (orGroup
== false)
151 dependencies
[Dep
->Type
].append(", ");
152 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
153 if (Dep
->Version
!= 0)
154 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
155 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
157 dependencies
[Dep
->Type
].append(" | ");
163 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
164 if (dependencies
[i
].empty() == false)
165 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
167 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
169 if (Prv
.IsMultiArchImplicit() == true)
171 if (provides
.empty() == false)
172 provides
.append(", ");
173 provides
.append(Prv
.Name());
174 if (Prv
->ProvideVersion
!= 0)
175 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
177 if (provides
.empty() == false)
178 fprintf(output
, "Provides: %s\n", provides
.c_str());
180 static bool WriteScenarioDependency(FileFd
&output
, pkgCache::VerIterator
const &Ver
, bool const OnlyCritical
)
182 std::array
<std::string
, _count(DepMap
)> dependencies
;
183 bool orGroup
= false;
184 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
186 if (Dep
.IsImplicit() == true)
188 if (OnlyCritical
&& Dep
.IsCritical() == false)
190 if (orGroup
== false && dependencies
[Dep
->Type
].empty() == false)
191 dependencies
[Dep
->Type
].append(", ");
192 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
193 if (Dep
->Version
!= 0)
194 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
195 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
197 dependencies
[Dep
->Type
].append(" | ");
203 bool Okay
= output
.Failed() == false;
204 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
205 if (dependencies
[i
].empty() == false)
206 WriteOkay(Okay
, output
, "\n", DepMap
[i
], ": ", dependencies
[i
]);
208 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
210 if (Prv
.IsMultiArchImplicit() == true)
212 if (provides
.empty() == false)
213 provides
.append(", ");
214 provides
.append(Prv
.Name());
215 if (Prv
->ProvideVersion
!= 0)
216 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
218 if (provides
.empty() == false)
219 WriteOkay(Okay
, output
, "\nProvides: ", provides
);
220 return WriteOkay(Okay
, output
, "\n");
223 // WriteScenarioLimitedDependency /*{{{*/
224 static void WriteScenarioLimitedDependency(FILE* output
,
225 pkgCache::VerIterator
const &Ver
,
226 APT::PackageSet
const &pkgset
)
228 std::array
<std::string
, _count(DepMap
)> dependencies
;
229 bool orGroup
= false;
230 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
232 if (Dep
.IsImplicit() == true)
234 if (orGroup
== false)
236 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
238 if (dependencies
[Dep
->Type
].empty() == false)
239 dependencies
[Dep
->Type
].append(", ");
241 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
243 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
245 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
249 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
250 if (Dep
->Version
!= 0)
251 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
252 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
254 dependencies
[Dep
->Type
].append(" | ");
260 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
261 if (dependencies
[i
].empty() == false)
262 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str());
264 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
266 if (Prv
.IsMultiArchImplicit() == true)
268 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
270 if (provides
.empty() == false)
271 provides
.append(", ");
272 provides
.append(Prv
.Name());
273 if (Prv
->ProvideVersion
!= 0)
274 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
276 if (provides
.empty() == false)
277 fprintf(output
, "Provides: %s\n", provides
.c_str());
279 static bool WriteScenarioLimitedDependency(FileFd
&output
,
280 pkgCache::VerIterator
const &Ver
,
281 std::vector
<bool> const &pkgset
,
282 bool const OnlyCritical
)
284 std::array
<std::string
, _count(DepMap
)> dependencies
;
285 bool orGroup
= false;
286 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
288 if (Dep
.IsImplicit() == true)
290 if (OnlyCritical
&& Dep
.IsCritical() == false)
292 if (orGroup
== false)
294 if (pkgset
[Dep
.TargetPkg()->ID
] == false)
296 if (dependencies
[Dep
->Type
].empty() == false)
297 dependencies
[Dep
->Type
].append(", ");
299 else if (pkgset
[Dep
.TargetPkg()->ID
] == false)
301 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
303 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
307 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
308 if (Dep
->Version
!= 0)
309 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
310 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
312 dependencies
[Dep
->Type
].append(" | ");
318 bool Okay
= output
.Failed() == false;
319 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
320 if (dependencies
[i
].empty() == false)
321 WriteOkay(Okay
, output
, "\n", DepMap
[i
], ": ", dependencies
[i
]);
323 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
325 if (Prv
.IsMultiArchImplicit() == true)
327 if (pkgset
[Prv
.ParentPkg()->ID
] == false)
329 if (provides
.empty() == false)
330 provides
.append(", ");
331 provides
.append(Prv
.Name());
332 if (Prv
->ProvideVersion
!= 0)
333 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
335 if (provides
.empty() == false)
336 WriteOkay(Okay
, output
, "\nProvides: ", provides
);
337 return WriteOkay(Okay
, output
, "\n");
340 static bool SkipUnavailableVersions(pkgDepCache
&Cache
, pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
)/*{{{*/
342 /* versions which aren't current and aren't available in
343 any "online" source file are bad, expect if they are the chosen
344 candidate: The exception is for build-dep implementation as it creates
345 such pseudo (package) versions and removes them later on again.
346 We filter out versions at all so packages in 'rc' state only available
347 in dpkg/status aren't passed to solvers as they can't be installed. */
348 if (Pkg
->CurrentVer
!= 0)
350 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
352 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
)
353 if (I
.File().Flagged(pkgCache::Flag::NotSource
) == false)
358 static bool WriteScenarioEDSPVersion(pkgDepCache
&Cache
, FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,/*{{{*/
359 pkgCache::VerIterator
const &Ver
)
361 bool Okay
= WriteOkay(output
, "\nSource: ", Ver
.SourcePkgName(),
362 "\nSource-Version: ", Ver
.SourceVerStr());
363 if (PrioMap
[Ver
->Priority
] != nullptr)
364 WriteOkay(Okay
, output
, "\nPriority: ", PrioMap
[Ver
->Priority
]);
365 if (Ver
->Section
!= 0)
366 WriteOkay(Okay
, output
, "\nSection: ", Ver
.Section());
367 if (Pkg
.CurrentVer() == Ver
)
368 WriteOkay(Okay
, output
, "\nInstalled: yes");
369 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
370 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
371 WriteOkay(Okay
, output
, "\nHold: yes");
372 std::set
<string
> Releases
;
373 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
374 pkgCache::PkgFileIterator File
= I
.File();
375 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
376 string Release
= File
.RelStr();
377 if (!Release
.empty())
378 Releases
.insert(Release
);
381 if (!Releases
.empty()) {
382 WriteOkay(Okay
, output
, "\nAPT-Release:");
383 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
384 WriteOkay(Okay
, output
, "\n ", *R
);
386 WriteOkay(Okay
, output
, "\nAPT-Pin: ", Cache
.GetPolicy().GetPriority(Ver
));
387 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
388 WriteOkay(Okay
, output
, "\nAPT-Candidate: yes");
389 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
390 WriteOkay(Okay
, output
, "\nAPT-Automatic: yes");
394 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
395 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
397 if (Progress
!= NULL
)
398 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
400 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
401 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
403 std::string
const arch
= Pkg
.Arch();
404 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
406 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
408 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
410 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
411 WriteScenarioDependency(output
, Ver
);
412 fprintf(output
, "\n");
413 if (Progress
!= NULL
&& p
% 100 == 0)
414 Progress
->Progress(p
);
419 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FileFd
&output
, OpProgress
*Progress
)
421 if (Progress
!= NULL
)
422 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
424 bool Okay
= output
.Failed() == false;
425 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
426 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
)
428 std::string
const arch
= Pkg
.Arch();
429 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
431 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false && likely(Okay
); ++Ver
, ++p
)
433 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
435 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
436 Okay
&= WriteScenarioEDSPVersion(Cache
, output
, Pkg
, Ver
);
437 Okay
&= WriteScenarioDependency(output
, Ver
, false);
438 WriteOkay(Okay
, output
, "\n");
439 if (Progress
!= NULL
&& p
% 100 == 0)
440 Progress
->Progress(p
);
446 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
447 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
448 APT::PackageSet
const &pkgset
,
449 OpProgress
*Progress
)
451 if (Progress
!= NULL
)
452 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
454 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
455 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
457 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
459 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
460 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
461 fprintf(output
, "\n");
462 if (Progress
!= NULL
&& p
% 100 == 0)
463 Progress
->Progress(p
);
465 if (Progress
!= NULL
)
469 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FileFd
&output
,
470 std::vector
<bool> const &pkgset
,
471 OpProgress
*Progress
)
473 if (Progress
!= NULL
)
474 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
476 bool Okay
= output
.Failed() == false;
477 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
, ++p
)
479 if (pkgset
[Pkg
->ID
] == false)
481 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false && likely(Okay
); ++Ver
)
483 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
485 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
486 Okay
&= WriteScenarioEDSPVersion(Cache
, output
, Pkg
, Ver
);
487 Okay
&= WriteScenarioLimitedDependency(output
, Ver
, pkgset
, false);
488 WriteOkay(Okay
, output
, "\n");
489 if (Progress
!= NULL
&& p
% 100 == 0)
490 Progress
->Progress(p
);
493 if (Progress
!= NULL
)
498 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
499 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
500 bool const DistUpgrade
, bool const AutoRemove
,
501 OpProgress
*Progress
)
503 if (Progress
!= NULL
)
504 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
507 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
509 if (Progress
!= NULL
&& p
% 100 == 0)
510 Progress
->Progress(p
);
512 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
513 if (P
.Delete() == true)
515 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
516 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
520 req
->append(" ").append(Pkg
.FullName());
522 fprintf(output
, "Request: EDSP 0.5\n");
524 const char *arch
= _config
->Find("APT::Architecture").c_str();
525 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
526 fprintf(output
, "Architecture: %s\n", arch
);
527 fprintf(output
, "Architectures:");
528 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
529 fprintf(output
, " %s", a
->c_str());
530 fprintf(output
, "\n");
532 if (del
.empty() == false)
533 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
534 if (inst
.empty() == false)
535 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
537 fprintf(output
, "Upgrade: yes\n");
538 if (DistUpgrade
== true)
539 fprintf(output
, "Dist-Upgrade: yes\n");
540 if (AutoRemove
== true)
541 fprintf(output
, "Autoremove: yes\n");
542 auto const solver
= _config
->Find("APT::Solver", "internal");
543 fprintf(output
, "Solver: %s\n", solver
.c_str());
544 auto const solverconf
= std::string("APT::Solver::") + solver
+ "::";
545 if (_config
->FindB(solverconf
+ "Strict-Pinning", _config
->FindB("APT::Solver::Strict-Pinning", true)) == false)
546 fprintf(output
, "Strict-Pinning: no\n");
547 auto const solverpref
= _config
->Find(solverconf
+ "Preferences", _config
->Find("APT::Solver::Preferences", ""));
548 if (solverpref
.empty() == false)
549 fprintf(output
, "Preferences: %s\n", solverpref
.c_str());
550 fprintf(output
, "\n");
553 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FileFd
&output
,
554 unsigned int const flags
,
555 OpProgress
*Progress
)
557 if (Progress
!= NULL
)
558 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
561 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
563 if (Progress
!= NULL
&& p
% 100 == 0)
564 Progress
->Progress(p
);
566 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
567 if (P
.Delete() == true)
569 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
570 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
574 req
->append(" ").append(Pkg
.FullName());
576 bool Okay
= WriteOkay(output
, "Request: EDSP 0.5\n");
578 const char *arch
= _config
->Find("APT::Architecture").c_str();
579 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
580 WriteOkay(Okay
, output
, "Architecture: ", arch
, "\n",
582 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
583 WriteOkay(Okay
, output
, " ", *a
);
584 WriteOkay(Okay
, output
, "\n");
586 if (del
.empty() == false)
587 WriteOkay(Okay
, output
, "Remove:", del
, "\n");
588 if (inst
.empty() == false)
589 WriteOkay(Okay
, output
, "Install:", inst
, "\n");
590 if (flags
& Request::AUTOREMOVE
)
591 WriteOkay(Okay
, output
, "Autoremove: yes\n");
592 if (flags
& Request::UPGRADE_ALL
)
594 WriteOkay(Okay
, output
, "Upgrade-All: yes\n");
595 if (flags
& (Request::FORBID_NEW_INSTALL
| Request::FORBID_REMOVE
))
596 WriteOkay(Okay
, output
, "Upgrade: yes\n");
598 WriteOkay(Okay
, output
, "Dist-Upgrade: yes\n");
600 if (flags
& Request::FORBID_NEW_INSTALL
)
601 WriteOkay(Okay
, output
, "Forbid-New-Install: yes\n");
602 if (flags
& Request::FORBID_REMOVE
)
603 WriteOkay(Okay
, output
, "Forbid-Remove: yes\n");
604 auto const solver
= _config
->Find("APT::Solver", "internal");
605 WriteOkay(Okay
, output
, "Solver: ", solver
, "\n");
606 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
607 WriteOkay(Okay
, output
, "Strict-Pinning: no\n");
608 string
solverpref("APT::Solver::");
609 solverpref
.append(solver
).append("::Preferences");
610 if (_config
->Exists(solverpref
) == true)
611 WriteOkay(Okay
, output
, "Preferences: ", _config
->Find(solverpref
,""), "\n");
612 return WriteOkay(Okay
, output
, "\n");
615 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
616 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
617 /* We build an map id to mmap offset here
618 In theory we could use the offset as ID, but then VersionCount
619 couldn't be used to create other versionmappings anymore and it
620 would be too easy for a (buggy) solver to segfault APT… */
621 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
622 unsigned long VerIdx
[VersionCount
];
623 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
624 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
625 VerIdx
[V
->ID
] = V
.Index();
626 Cache
[P
].Marked
= true;
627 Cache
[P
].Garbage
= false;
631 in
.OpenDescriptor(input
, FileFd::ReadOnly
, true);
632 pkgTagFile
response(&in
, 100);
633 pkgTagSection section
;
635 std::set
<decltype(Cache
.PkgBegin()->ID
)> seenOnce
;
636 while (response
.Step(section
) == true) {
638 if (section
.Exists("Install") == true)
640 else if (section
.Exists("Remove") == true)
642 else if (section
.Exists("Progress") == true) {
643 if (Progress
!= NULL
) {
644 string msg
= section
.FindS("Message");
645 if (msg
.empty() == true)
646 msg
= _("Prepare for receiving solution");
647 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
650 } else if (section
.Exists("Error") == true) {
651 if (_error
->PendingError()) {
652 if (Progress
!= nullptr)
655 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
657 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
658 if (msg
.empty() == true) {
659 msg
= _("External solver failed without a proper error message");
660 _error
->Error("%s", msg
.c_str());
662 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
663 if (Progress
!= nullptr)
665 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
666 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
667 std::cerr
<< msg
<< std::endl
<< std::endl
;
669 } else if (section
.Exists("Autoremove") == true)
672 char const *Start
, *End
;
673 section
.GetSection(Start
, End
);
674 _error
->Warning("Encountered an unexpected section with %d fields: %s", section
.Count(), std::string(Start
, End
).c_str());
678 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
679 if (id
== VersionCount
) {
680 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
682 } else if (id
> Cache
.Head().VersionCount
) {
683 _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());
687 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
688 auto const Pkg
= Ver
.ParentPkg();
689 if (type
== "Autoremove") {
690 Cache
[Pkg
].Marked
= false;
691 Cache
[Pkg
].Garbage
= true;
692 } else if (seenOnce
.emplace(Pkg
->ID
).second
== false) {
693 _error
->Warning("Ignoring %s stanza received for package %s which already had a previous stanza effecting it!", type
.c_str(), Pkg
.FullName(false).c_str());
694 } else if (type
== "Install") {
695 if (Pkg
.CurrentVer() == Ver
) {
696 _error
->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
697 Ver
.VerStr(), Pkg
.FullName(false).c_str());
699 Cache
.SetCandidateVersion(Ver
);
700 Cache
.MarkInstall(Pkg
, false, 0, false);
702 } else if (type
== "Remove") {
703 if (Pkg
->CurrentVer
== 0)
704 _error
->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
705 Ver
.VerStr(), Pkg
.FullName(false).c_str());
706 else if (Pkg
.CurrentVer() != Ver
)
707 _error
->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
708 Ver
.VerStr(), Pkg
.FullName(false).c_str(), Pkg
.CurrentVer().VerStr());
710 Cache
.MarkDelete(Ver
.ParentPkg(), false);
716 // ReadLine - first line from the given file descriptor /*{{{*/
717 // ---------------------------------------------------------------------
718 /* Little helper method to read a complete line into a string. Similar to
719 fgets but we need to use the low-level read() here as otherwise the
720 listparser will be confused later on as mixing of fgets and read isn't
721 a supported action according to the manpages and results are undefined */
722 static bool ReadLine(int const input
, std::string
&line
) {
727 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
734 if (line
.empty() == true && isblank(one
) != 0)
741 // StringToBool - convert yes/no to bool /*{{{*/
742 // ---------------------------------------------------------------------
743 /* we are not as lazy as we are in the global StringToBool as we really
744 only accept yes/no here */
745 static bool localStringToBool(std::string answer
, bool const defValue
) {
746 std::transform(answer
.begin(), answer
.end(), answer
.begin(), ::tolower
);
749 else if (answer
== "no")
752 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
.c_str());
756 static bool LineStartsWithAndStrip(std::string
&line
, APT::StringView
const with
)/*{{{*/
758 if (line
.compare(0, with
.size(), with
.data()) != 0)
760 line
= APT::String::Strip(line
.substr(with
.length()));
764 static bool ReadFlag(unsigned int &flags
, std::string
&line
, APT::StringView
const name
, unsigned int const setflag
)/*{{{*/
766 if (LineStartsWithAndStrip(line
, name
) == false)
768 if (localStringToBool(line
, false))
775 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
776 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
777 std::list
<std::string
> &remove
, unsigned int &flags
)
783 while (ReadLine(input
, line
) == true)
785 // Skip empty lines before request
786 if (line
.empty() == true)
788 // The first Tag must be a request, so search for it
789 if (LineStartsWithAndStrip(line
, "Request:"))
792 while (ReadLine(input
, line
) == true)
794 // empty lines are the end of the request
795 if (line
.empty() == true)
798 std::list
<std::string
> *request
= NULL
;
799 if (LineStartsWithAndStrip(line
, "Install:"))
801 else if (LineStartsWithAndStrip(line
, "Remove:"))
803 else if (ReadFlag(flags
, line
, "Upgrade:", (Request::UPGRADE_ALL
| Request::FORBID_REMOVE
| Request::FORBID_NEW_INSTALL
)) ||
804 ReadFlag(flags
, line
, "Dist-Upgrade:", Request::UPGRADE_ALL
) ||
805 ReadFlag(flags
, line
, "Upgrade-All:", Request::UPGRADE_ALL
) ||
806 ReadFlag(flags
, line
, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL
) ||
807 ReadFlag(flags
, line
, "Forbid-Remove:", Request::FORBID_REMOVE
) ||
808 ReadFlag(flags
, line
, "Autoremove:", Request::AUTOREMOVE
))
810 else if (LineStartsWithAndStrip(line
, "Architecture:"))
811 _config
->Set("APT::Architecture", line
);
812 else if (LineStartsWithAndStrip(line
, "Architectures:"))
813 _config
->Set("APT::Architectures", SubstVar(line
, " ", ","));
814 else if (LineStartsWithAndStrip(line
, "Solver:"))
815 ; // purely informational line
817 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
821 auto const pkgs
= VectorizeString(line
, ' ');
822 std::move(pkgs
.begin(), pkgs
.end(), std::back_inserter(*request
));
827 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
828 std::list
<std::string
> &remove
, bool &upgrade
,
829 bool &distUpgrade
, bool &autoRemove
)
832 auto const ret
= ReadRequest(input
, install
, remove
, flags
);
833 autoRemove
= (flags
& Request::AUTOREMOVE
);
834 if (flags
& Request::UPGRADE_ALL
)
836 if (flags
& (Request::FORBID_NEW_INSTALL
| Request::FORBID_REMOVE
))
853 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
854 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
855 std::list
<std::string
> const &remove
,
858 for (std::list
<std::string
>::const_iterator i
= install
.begin();
859 i
!= install
.end(); ++i
) {
860 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
862 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
864 Cache
.MarkInstall(P
, false);
867 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
868 i
!= remove
.end(); ++i
) {
869 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
871 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
878 // EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
879 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
881 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
882 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
884 if (Cache
[Pkg
].Delete() == true)
886 fprintf(output
, "Remove: %d\n", _system
->GetVersionMapping(Pkg
.CurrentVer()->ID
));
888 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
890 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
892 pkgCache::VerIterator
const CandVer
= Cache
.GetCandidateVersion(Pkg
);
893 fprintf(output
, "Install: %d\n", _system
->GetVersionMapping(CandVer
->ID
));
895 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), CandVer
.VerStr());
897 else if (Cache
[Pkg
].Garbage
== true)
899 fprintf(output
, "Autoremove: %d\n", _system
->GetVersionMapping(Pkg
.CurrentVer()->ID
));
901 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
905 fprintf(output
, "\n");
910 bool EDSP::WriteSolutionStanza(FileFd
&output
, char const * const Type
, pkgCache::VerIterator
const &Ver
)
912 bool Okay
= output
.Failed() == false;
913 WriteOkay(Okay
, output
, Type
, ": ", _system
->GetVersionMapping(Ver
->ID
));
914 if (_config
->FindB("Debug::EDSP::WriteSolution", false) == true)
915 WriteOkay(Okay
, output
, "\nPackage: ", Ver
.ParentPkg().FullName(), "\nVersion: ", Ver
.VerStr());
916 return WriteOkay(Okay
, output
, "\n\n");
919 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
920 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
921 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
), true).c_str());
922 fprintf(output
, "Percentage: %d\n", percent
);
923 fprintf(output
, "Message: %s\n\n", message
);
927 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FileFd
&output
) {
928 return WriteOkay(output
, "Progress: ", TimeRFC1123(time(NULL
), true), "\n",
929 "Percentage: ", percent
, "\n",
930 "Message: ", message
, "\n\n") && output
.Flush();
933 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
934 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
935 fprintf(output
, "Error: %s\n", uuid
);
936 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
939 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FileFd
&output
) {
940 return WriteOkay(output
, "Error: ", uuid
, "\n",
941 "Message: ", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n "),
945 static std::string
findExecutable(std::vector
<std::string
> const &dirs
, char const * const binary
) {/*{{{*/
946 for (auto && dir
: dirs
) {
947 std::string
const file
= flCombine(dir
, binary
);
948 if (RealFileExists(file
) == true)
954 static pid_t
ExecuteExternal(char const* const type
, char const * const binary
, char const * const configdir
, int * const solver_in
, int * const solver_out
) {/*{{{*/
955 auto const solverDirs
= _config
->FindVector(configdir
);
956 auto const file
= findExecutable(solverDirs
, binary
);
959 dumper
= findExecutable(solverDirs
, "apt-dump-solver");
961 dumper
= findExecutable(solverDirs
, "dump");
964 if (file
.empty() == true)
966 _error
->Error("Can't call external %s '%s' as it is not in a configured directory!", type
, binary
);
969 int external
[4] = {-1, -1, -1, -1};
970 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
972 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
975 for (int i
= 0; i
< 4; ++i
)
976 SetCloseExec(external
[i
], true);
978 pid_t Solver
= ExecFork();
980 dup2(external
[0], STDIN_FILENO
);
981 dup2(external
[3], STDOUT_FILENO
);
982 auto const dumpfile
= _config
->FindFile((std::string("Dir::Log::") + type
).c_str());
983 auto const dumpdir
= flNotFile(dumpfile
);
984 auto const runasuser
= _config
->Find(std::string("APT::") + type
+ "::" + binary
+ "::RunAsUser",
985 _config
->Find(std::string("APT::") + type
+ "::RunAsUser",
986 _config
->Find("APT::Sandbox::User")));
987 if (dumper
.empty() || dumpfile
.empty() || dumper
== file
|| CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false)
989 _config
->Set("APT::Sandbox::User", runasuser
);
991 char const * const calling
[] = { file
.c_str(), nullptr };
992 execv(calling
[0], const_cast<char**>(calling
));
996 char const * const calling
[] = { dumper
.c_str(), "--user", runasuser
.c_str(), dumpfile
.c_str(), file
.c_str(), nullptr };
997 execv(calling
[0], const_cast<char**>(calling
));
999 std::cerr
<< "Failed to execute " << type
<< " '" << binary
<< "'!" << std::endl
;
1005 if (WaitFd(external
[1], true, 5) == false)
1007 _error
->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type
);
1011 *solver_in
= external
[1];
1012 *solver_out
= external
[2];
1016 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1017 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
1018 return ExecuteExternal("solver", solver
, "Dir::Bin::Solvers", solver_in
, solver_out
);
1020 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
1021 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
1026 static bool CreateDumpFile(char const * const id
, char const * const type
, FileFd
&output
)/*{{{*/
1028 auto const dumpfile
= _config
->FindFile((std::string("Dir::Log::") + type
).c_str());
1029 if (dumpfile
.empty())
1031 auto const dumpdir
= flNotFile(dumpfile
);
1032 _error
->PushToStack();
1033 bool errored_out
= CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false ||
1034 output
.Open(dumpfile
, FileFd::WriteOnly
| FileFd::Exclusive
| FileFd::Create
, FileFd::Extension
, 0644) == false;
1035 std::vector
<std::string
> downgrademsgs
;
1036 while (_error
->empty() == false)
1039 _error
->PopMessage(msg
);
1040 downgrademsgs
.emplace_back(std::move(msg
));
1042 _error
->RevertToStack();
1043 for (auto && msg
: downgrademsgs
)
1044 _error
->Warning("%s", msg
.c_str());
1046 return _error
->WarningE(id
, _("Could not open file '%s'"), dumpfile
.c_str());
1050 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1051 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
1052 unsigned int const flags
, OpProgress
*Progress
) {
1053 if (strcmp(solver
, "internal") == 0)
1056 bool Okay
= CreateDumpFile("EDSP::Resolve", "solver", output
);
1057 Okay
&= EDSP::WriteRequest(Cache
, output
, flags
, nullptr);
1058 return Okay
&& EDSP::WriteScenario(Cache
, output
, nullptr);
1060 _error
->PushToStack();
1061 int solver_in
, solver_out
;
1062 pid_t
const solver_pid
= ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
1063 if (solver_pid
== 0)
1067 if (output
.OpenDescriptor(solver_in
, FileFd::WriteOnly
| FileFd::BufferedWrite
, true) == false)
1068 return _error
->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver
, solver_in
);
1070 bool Okay
= output
.Failed() == false;
1071 if (Okay
&& Progress
!= NULL
)
1072 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
1073 Okay
&= EDSP::WriteRequest(Cache
, output
, flags
, Progress
);
1074 if (Okay
&& Progress
!= NULL
)
1075 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
1076 Okay
&= EDSP::WriteScenario(Cache
, output
, Progress
);
1079 if (Okay
&& Progress
!= NULL
)
1080 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
1081 bool const ret
= EDSP::ReadResponse(solver_out
, Cache
, Progress
);
1082 _error
->MergeWithStack();
1083 if (ExecWait(solver_pid
, solver
))
1087 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
1088 bool const upgrade
, bool const distUpgrade
,
1089 bool const autoRemove
, OpProgress
*Progress
) {
1090 unsigned int flags
= 0;
1092 flags
|= Request::AUTOREMOVE
;
1094 flags
|= Request::UPGRADE_ALL
| Request::FORBID_REMOVE
| Request::FORBID_NEW_INSTALL
;
1096 flags
|= Request::UPGRADE_ALL
;
1097 return ResolveExternal(solver
, Cache
, flags
, Progress
);
1101 bool EIPP::OrderInstall(char const * const solver
, pkgPackageManager
* const PM
, /*{{{*/
1102 unsigned int const flags
, OpProgress
* const Progress
)
1104 if (strcmp(solver
, "internal") == 0)
1107 _error
->PushToStack();
1108 bool Okay
= CreateDumpFile("EIPP::OrderInstall", "planner", output
);
1109 if (Okay
== false && dynamic_cast<pkgSimulate
*>(PM
) != nullptr)
1111 _error
->RevertToStack();
1114 _error
->MergeWithStack();
1115 Okay
&= EIPP::WriteRequest(PM
->Cache
, output
, flags
, nullptr);
1116 return Okay
&& EIPP::WriteScenario(PM
->Cache
, output
, nullptr);
1118 _error
->PushToStack();
1119 int solver_in
, solver_out
;
1120 pid_t
const solver_pid
= ExecuteExternal("planner", solver
, "Dir::Bin::Planners", &solver_in
, &solver_out
);
1121 if (solver_pid
== 0)
1125 if (output
.OpenDescriptor(solver_in
, FileFd::WriteOnly
| FileFd::BufferedWrite
, true) == false)
1126 return _error
->Errno("EIPP::OrderInstall", "Opening planner %s stdin on fd %d for writing failed", solver
, solver_in
);
1128 bool Okay
= output
.Failed() == false;
1129 if (Okay
&& Progress
!= NULL
)
1130 Progress
->OverallProgress(0, 100, 5, _("Execute external planner"));
1131 Okay
&= EIPP::WriteRequest(PM
->Cache
, output
, flags
, Progress
);
1132 if (Okay
&& Progress
!= NULL
)
1133 Progress
->OverallProgress(5, 100, 20, _("Execute external planner"));
1134 Okay
&= EIPP::WriteScenario(PM
->Cache
, output
, Progress
);
1139 if (Progress
!= nullptr)
1140 Progress
->OverallProgress(25, 100, 75, _("Execute external planner"));
1142 // we don't tell the external planners about boring things
1143 for (auto Pkg
= PM
->Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1145 if (Pkg
->CurrentState
== pkgCache::State::ConfigFiles
&& PM
->Cache
[Pkg
].Purge() == true)
1146 PM
->Remove(Pkg
, true);
1149 bool const ret
= EIPP::ReadResponse(solver_out
, PM
, Progress
);
1150 _error
->MergeWithStack();
1151 if (ExecWait(solver_pid
, solver
))
1156 bool EIPP::WriteRequest(pkgDepCache
&Cache
, FileFd
&output
, /*{{{*/
1157 unsigned int const flags
,
1158 OpProgress
* const Progress
)
1160 if (Progress
!= NULL
)
1161 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to planner"));
1162 unsigned long p
= 0;
1163 string del
, inst
, reinst
;
1164 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
1166 if (Progress
!= NULL
&& p
% 100 == 0)
1167 Progress
->Progress(p
);
1169 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
1170 if (P
.Purge() == true && Pkg
->CurrentState
== pkgCache::State::ConfigFiles
)
1172 if (P
.Delete() == true)
1174 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.Downgrade() == true)
1176 else if (P
.ReInstall() == true)
1180 req
->append(" ").append(Pkg
.FullName());
1182 bool Okay
= WriteOkay(output
, "Request: EIPP 0.1\n");
1184 const char *arch
= _config
->Find("APT::Architecture").c_str();
1185 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
1186 WriteOkay(Okay
, output
, "Architecture: ", arch
, "\n",
1188 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
1189 WriteOkay(Okay
, output
, " ", *a
);
1190 WriteOkay(Okay
, output
, "\n");
1192 if (del
.empty() == false)
1193 WriteOkay(Okay
, output
, "Remove:", del
, "\n");
1194 if (inst
.empty() == false)
1195 WriteOkay(Okay
, output
, "Install:", inst
, "\n");
1196 if (reinst
.empty() == false)
1197 WriteOkay(Okay
, output
, "ReInstall:", reinst
, "\n");
1198 WriteOkay(Okay
, output
, "Planner: ", _config
->Find("APT::Planner", "internal"), "\n");
1199 if ((flags
& Request::IMMEDIATE_CONFIGURATION_ALL
) != 0)
1200 WriteOkay(Okay
, output
, "Immediate-Configuration: yes\n");
1201 else if ((flags
& Request::NO_IMMEDIATE_CONFIGURATION
) != 0)
1202 WriteOkay(Okay
, output
, "Immediate-Configuration: no\n");
1203 else if ((flags
& Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS
) != 0)
1204 WriteOkay(Okay
, output
, "Allow-Temporary-Remove-of-Essentials: yes\n");
1205 return WriteOkay(Okay
, output
, "\n");
1208 static bool WriteScenarioEIPPVersion(pkgDepCache
&, FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,/*{{{*/
1209 pkgCache::VerIterator
const &Ver
)
1212 if (Pkg
.CurrentVer() == Ver
)
1213 switch (Pkg
->CurrentState
)
1215 case pkgCache::State::NotInstalled
: WriteOkay(Okay
, output
, "\nStatus: not-installed"); break;
1216 case pkgCache::State::ConfigFiles
: WriteOkay(Okay
, output
, "\nStatus: config-files"); break;
1217 case pkgCache::State::HalfInstalled
: WriteOkay(Okay
, output
, "\nStatus: half-installed"); break;
1218 case pkgCache::State::UnPacked
: WriteOkay(Okay
, output
, "\nStatus: unpacked"); break;
1219 case pkgCache::State::HalfConfigured
: WriteOkay(Okay
, output
, "\nStatus: half-configured"); break;
1220 case pkgCache::State::TriggersAwaited
: WriteOkay(Okay
, output
, "\nStatus: triggers-awaited"); break;
1221 case pkgCache::State::TriggersPending
: WriteOkay(Okay
, output
, "\nStatus: triggers-pending"); break;
1222 case pkgCache::State::Installed
: WriteOkay(Okay
, output
, "\nStatus: installed"); break;
1227 // EIPP::WriteScenario - to the given file descriptor /*{{{*/
1228 template<typename forVersion
> void forAllInterestingVersions(pkgDepCache
&Cache
, pkgCache::PkgIterator
const &Pkg
, forVersion
const &func
)
1230 if (Pkg
->CurrentState
== pkgCache::State::NotInstalled
)
1232 auto P
= Cache
[Pkg
];
1233 if (P
.Install() == false)
1235 func(Pkg
, P
.InstVerIter(Cache
));
1239 if (Pkg
->CurrentVer
!= 0)
1240 func(Pkg
, Pkg
.CurrentVer());
1241 auto P
= Cache
[Pkg
];
1242 auto const V
= P
.InstVerIter(Cache
);
1243 if (P
.Delete() == false && Pkg
.CurrentVer() != V
)
1248 bool EIPP::WriteScenario(pkgDepCache
&Cache
, FileFd
&output
, OpProgress
* const Progress
)
1250 if (Progress
!= NULL
)
1251 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send scenario to planner"));
1252 unsigned long p
= 0;
1253 bool Okay
= output
.Failed() == false;
1254 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
1255 std::vector
<bool> pkgset(Cache
.Head().PackageCount
, false);
1256 auto const MarkVersion
= [&](pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
) {
1257 pkgset
[Pkg
->ID
] = true;
1258 for (auto D
= Ver
.DependsList(); D
.end() == false; ++D
)
1260 if (D
.IsCritical() == false)
1262 auto const P
= D
.TargetPkg();
1263 for (auto Prv
= P
.ProvidesList(); Prv
.end() == false; ++Prv
)
1265 auto const V
= Prv
.OwnerVer();
1266 auto const PV
= V
.ParentPkg();
1267 if (V
== PV
.CurrentVer() || V
== Cache
[PV
].InstVerIter(Cache
))
1268 pkgset
[PV
->ID
] = true;
1270 pkgset
[P
->ID
] = true;
1271 if (strcmp(P
.Arch(), "any") == 0)
1273 APT::StringView
const pkgname(P
.Name());
1274 auto const idxColon
= pkgname
.find(':');
1275 if (idxColon
!= APT::StringView::npos
)
1277 pkgCache::PkgIterator PA
;
1278 if (pkgname
.substr(idxColon
+ 1) == "any")
1280 auto const GA
= Cache
.FindGrp(pkgname
.substr(0, idxColon
).to_string());
1281 for (auto PA
= GA
.PackageList(); PA
.end() == false; PA
= GA
.NextPkg(PA
))
1283 pkgset
[PA
->ID
] = true;
1288 auto const PA
= Cache
.FindPkg(pkgname
.to_string());
1289 if (PA
.end() == false)
1290 pkgset
[PA
->ID
] = true;
1296 auto const PA
= Cache
.FindPkg(P
.FullName(false), "any");
1297 if (PA
.end() == false)
1298 pkgset
[PA
->ID
] = true;
1302 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1303 forAllInterestingVersions(Cache
, Pkg
, MarkVersion
);
1304 auto const WriteVersion
= [&](pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
) {
1305 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
1306 Okay
&= WriteScenarioEIPPVersion(Cache
, output
, Pkg
, Ver
);
1307 Okay
&= WriteScenarioLimitedDependency(output
, Ver
, pkgset
, true);
1308 WriteOkay(Okay
, output
, "\n");
1309 if (Progress
!= NULL
&& p
% 100 == 0)
1310 Progress
->Progress(p
);
1312 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
, ++p
)
1314 if (pkgset
[Pkg
->ID
] == false || Pkg
->VersionList
== 0)
1316 forAllInterestingVersions(Cache
, Pkg
, WriteVersion
);
1321 // EIPP::ReadResponse - from the given file descriptor /*{{{*/
1322 bool EIPP::ReadResponse(int const input
, pkgPackageManager
* const PM
, OpProgress
*Progress
) {
1323 /* We build an map id to mmap offset here
1324 In theory we could use the offset as ID, but then VersionCount
1325 couldn't be used to create other versionmappings anymore and it
1326 would be too easy for a (buggy) solver to segfault APT… */
1327 unsigned long long const VersionCount
= PM
->Cache
.Head().VersionCount
;
1328 unsigned long VerIdx
[VersionCount
];
1329 for (pkgCache::PkgIterator P
= PM
->Cache
.PkgBegin(); P
.end() == false; ++P
) {
1330 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
1331 VerIdx
[V
->ID
] = V
.Index();
1335 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
1336 pkgTagFile
response(&in
, 100);
1337 pkgTagSection section
;
1339 while (response
.Step(section
) == true) {
1340 char const * type
= nullptr;
1341 if (section
.Exists("Progress") == true) {
1342 if (Progress
!= NULL
) {
1343 string msg
= section
.FindS("Message");
1344 if (msg
.empty() == true)
1345 msg
= _("Prepare for receiving solution");
1346 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
1349 } else if (section
.Exists("Error") == true) {
1350 if (_error
->PendingError()) {
1351 if (Progress
!= nullptr)
1354 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1356 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
1357 if (msg
.empty() == true) {
1358 msg
= _("External planner failed without a proper error message");
1359 _error
->Error("%s", msg
.c_str());
1361 _error
->Error("External planner failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
1362 if (Progress
!= nullptr)
1364 std::cerr
<< "The planner encountered an error of type: " << section
.FindS("Error") << std::endl
;
1365 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
1366 std::cerr
<< msg
<< std::endl
<< std::endl
;
1368 } else if (section
.Exists("Unpack") == true)
1370 else if (section
.Exists("Configure") == true)
1372 else if (section
.Exists("Remove") == true)
1375 char const *Start
, *End
;
1376 section
.GetSection(Start
, End
);
1377 _error
->Warning("Encountered an unexpected section with %d fields: %s", section
.Count(), std::string(Start
, End
).c_str());
1381 if (type
== nullptr)
1383 size_t const id
= section
.FindULL(type
, VersionCount
);
1384 if (id
== VersionCount
) {
1385 _error
->Warning("Unable to parse %s request with id value '%s'!", type
, section
.FindS(type
).c_str());
1387 } else if (id
> PM
->Cache
.Head().VersionCount
) {
1388 _error
->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section
.FindS(type
).c_str(), type
);
1392 pkgCache::VerIterator
Ver(PM
->Cache
.GetCache(), PM
->Cache
.GetCache().VerP
+ VerIdx
[id
]);
1393 auto const Pkg
= Ver
.ParentPkg();
1394 if (strcmp(type
, "Unpack") == 0)
1395 PM
->Install(Pkg
, PM
->FileNames
[Pkg
->ID
]);
1396 else if (strcmp(type
, "Configure") == 0)
1398 else if (strcmp(type
, "Remove") == 0)
1399 PM
->Remove(Pkg
, PM
->Cache
[Pkg
].Purge());
1401 return in
.Failed() == false;
1404 bool EIPP::ReadRequest(int const input
, std::list
<std::pair
<std::string
,PKG_ACTION
>> &actions
,/*{{{*/
1405 unsigned int &flags
)
1410 while (ReadLine(input
, line
) == true)
1412 // Skip empty lines before request
1413 if (line
.empty() == true)
1415 // The first Tag must be a request, so search for it
1416 if (line
.compare(0, 8, "Request:") != 0)
1419 while (ReadLine(input
, line
) == true)
1421 // empty lines are the end of the request
1422 if (line
.empty() == true)
1425 PKG_ACTION pkgact
= PKG_ACTION::NOOP
;
1426 if (LineStartsWithAndStrip(line
, "Install:"))
1427 pkgact
= PKG_ACTION::INSTALL
;
1428 else if (LineStartsWithAndStrip(line
, "ReInstall:"))
1429 pkgact
= PKG_ACTION::REINSTALL
;
1430 else if (LineStartsWithAndStrip(line
, "Remove:"))
1431 pkgact
= PKG_ACTION::REMOVE
;
1432 else if (LineStartsWithAndStrip(line
, "Architecture:"))
1433 _config
->Set("APT::Architecture", line
);
1434 else if (LineStartsWithAndStrip(line
, "Architectures:"))
1435 _config
->Set("APT::Architectures", SubstVar(line
, " ", ","));
1436 else if (LineStartsWithAndStrip(line
, "Planner:"))
1437 ; // purely informational line
1438 else if (LineStartsWithAndStrip(line
, "Immediate-Configuration:"))
1440 if (localStringToBool(line
, true))
1441 flags
|= Request::IMMEDIATE_CONFIGURATION_ALL
;
1443 flags
|= Request::NO_IMMEDIATE_CONFIGURATION
;
1445 else if (ReadFlag(flags
, line
, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS
))
1448 _error
->Warning("Unknown line in EIPP Request stanza: %s", line
.c_str());
1450 if (pkgact
== PKG_ACTION::NOOP
)
1452 for (auto && p
: VectorizeString(line
, ' '))
1453 actions
.emplace_back(std::move(p
), pkgact
);
1459 bool EIPP::ApplyRequest(std::list
<std::pair
<std::string
,PKG_ACTION
>> &actions
,/*{{{*/
1462 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1465 for (auto Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
1468 if (Pkg
.CurrentVer() == Ver
)
1470 Cache
.SetCandidateVersion(Ver
);
1472 if (unlikely(versions
> 2))
1473 _error
->Warning("Package %s has %d versions, but should have at most 2!", Pkg
.FullName().c_str(), versions
);
1475 for (auto && a
: actions
)
1477 pkgCache::PkgIterator P
= Cache
.FindPkg(a
.first
);
1478 if (P
.end() == true)
1480 _error
->Warning("Package %s is not known, so can't be acted on", a
.first
.c_str());
1485 case PKG_ACTION::NOOP
:
1486 _error
->Warning("Package %s has NOOP as action?!?", a
.first
.c_str());
1488 case PKG_ACTION::INSTALL
:
1489 Cache
.MarkInstall(P
, false);
1491 case PKG_ACTION::REINSTALL
:
1492 Cache
.MarkInstall(P
, false);
1493 Cache
.SetReInstall(P
, true);
1495 case PKG_ACTION::REMOVE
:
1496 Cache
.MarkDelete(P
);