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/error.h>
11 #include <apt-pkg/cacheset.h>
12 #include <apt-pkg/depcache.h>
13 #include <apt-pkg/pkgcache.h>
14 #include <apt-pkg/cacheiterators.h>
15 #include <apt-pkg/prettyprinters.h>
16 #include <apt-pkg/packagemanager.h>
17 #include <apt-pkg/progress.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/edsp.h>
20 #include <apt-pkg/tagfile.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/string_view.h>
23 #include <apt-pkg/pkgsystem.h>
41 // we could use pkgCache::DepType and ::Priority, but these would be localized strings…
42 constexpr char const * const PrioMap
[] = {
43 nullptr, "important", "required", "standard",
46 constexpr char const * const DepMap
[] = {
47 nullptr, "Depends", "Pre-Depends", "Suggests",
48 "Recommends" , "Conflicts", "Replaces",
49 "Obsoletes", "Breaks", "Enhances"
52 // WriteOkay - varaidic helper to easily Write to a FileFd /*{{{*/
53 static bool WriteOkay_fn(FileFd
&) { return true; }
54 template<typename
... Tail
> static bool WriteOkay_fn(FileFd
&output
, APT::StringView data
, Tail
... more_data
)
56 return likely(output
.Write(data
.data(), data
.length()) && WriteOkay_fn(output
, more_data
...));
58 template<typename
... Tail
> static bool WriteOkay_fn(FileFd
&output
, unsigned int data
, Tail
... more_data
)
61 strprintf(number
, "%d", data
);
62 return likely(output
.Write(number
.data(), number
.length()) && WriteOkay_fn(output
, more_data
...));
64 template<typename
... Data
> static bool WriteOkay(bool &Okay
, FileFd
&output
, Data
&&... data
)
66 Okay
= likely(Okay
&& WriteOkay_fn(output
, std::forward
<Data
>(data
)...));
69 template<typename
... Data
> static bool WriteOkay(FileFd
&output
, Data
&&... data
)
71 bool Okay
= likely(output
.Failed() == false);
72 return WriteOkay(Okay
, output
, std::forward
<Data
>(data
)...);
75 // WriteScenarioVersion /*{{{*/
76 static void WriteScenarioVersion(pkgDepCache
&Cache
, FILE* output
, pkgCache::PkgIterator
const &Pkg
,
77 pkgCache::VerIterator
const &Ver
)
79 fprintf(output
, "Package: %s\n", Pkg
.Name());
80 fprintf(output
, "Source: %s\n", Ver
.SourcePkgName());
81 fprintf(output
, "Architecture: %s\n", Ver
.Arch());
82 fprintf(output
, "Version: %s\n", Ver
.VerStr());
83 fprintf(output
, "Source-Version: %s\n", Ver
.SourceVerStr());
84 if (Pkg
.CurrentVer() == Ver
)
85 fprintf(output
, "Installed: yes\n");
86 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
87 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
88 fprintf(output
, "Hold: yes\n");
89 fprintf(output
, "APT-ID: %d\n", Ver
->ID
);
90 if (PrioMap
[Ver
->Priority
] != nullptr)
91 fprintf(output
, "Priority: %s\n", PrioMap
[Ver
->Priority
]);
92 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
93 fprintf(output
, "Essential: yes\n");
94 if (Ver
->Section
!= 0)
95 fprintf(output
, "Section: %s\n", Ver
.Section());
96 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
97 fprintf(output
, "Multi-Arch: allowed\n");
98 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
99 fprintf(output
, "Multi-Arch: foreign\n");
100 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
101 fprintf(output
, "Multi-Arch: same\n");
102 std::set
<string
> Releases
;
103 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
104 pkgCache::PkgFileIterator File
= I
.File();
105 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
106 string Release
= File
.RelStr();
107 if (!Release
.empty())
108 Releases
.insert(Release
);
111 if (!Releases
.empty()) {
112 fprintf(output
, "APT-Release:\n");
113 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
114 fprintf(output
, " %s\n", R
->c_str());
116 fprintf(output
, "APT-Pin: %d\n", Cache
.GetPolicy().GetPriority(Ver
));
117 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
118 fprintf(output
, "APT-Candidate: yes\n");
119 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
120 fprintf(output
, "APT-Automatic: yes\n");
122 static bool WriteScenarioVersion(FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,
123 pkgCache::VerIterator
const &Ver
)
125 bool Okay
= WriteOkay(output
, "Package: ", Pkg
.Name(),
126 "\nArchitecture: ", Ver
.Arch(),
127 "\nVersion: ", Ver
.VerStr());
128 WriteOkay(Okay
, output
, "\nAPT-ID: ", Ver
->ID
);
129 if ((Pkg
->Flags
& pkgCache::Flag::Essential
) == pkgCache::Flag::Essential
)
130 WriteOkay(Okay
, output
, "\nEssential: yes");
131 if ((Ver
->MultiArch
& pkgCache::Version::Allowed
) == pkgCache::Version::Allowed
)
132 WriteOkay(Okay
, output
, "\nMulti-Arch: allowed");
133 else if ((Ver
->MultiArch
& pkgCache::Version::Foreign
) == pkgCache::Version::Foreign
)
134 WriteOkay(Okay
, output
, "\nMulti-Arch: foreign");
135 else if ((Ver
->MultiArch
& pkgCache::Version::Same
) == pkgCache::Version::Same
)
136 WriteOkay(Okay
, output
, "\nMulti-Arch: same");
140 // WriteScenarioDependency /*{{{*/
141 static void WriteScenarioDependency( FILE* output
, pkgCache::VerIterator
const &Ver
)
143 std::array
<std::string
, _count(DepMap
)> dependencies
;
144 bool orGroup
= false;
145 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
147 if (Dep
.IsImplicit() == true)
149 if (orGroup
== false)
150 dependencies
[Dep
->Type
].append(", ");
151 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
152 if (Dep
->Version
!= 0)
153 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
154 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
156 dependencies
[Dep
->Type
].append(" | ");
162 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
163 if (dependencies
[i
].empty() == false)
164 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str()+2);
166 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
168 if (Prv
.IsMultiArchImplicit() == true)
170 if (provides
.empty() == false)
171 provides
.append(", ");
172 provides
.append(Prv
.Name());
173 if (Prv
->ProvideVersion
!= 0)
174 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
176 if (provides
.empty() == false)
177 fprintf(output
, "Provides: %s\n", provides
.c_str());
179 static bool WriteScenarioDependency(FileFd
&output
, pkgCache::VerIterator
const &Ver
, bool const OnlyCritical
)
181 std::array
<std::string
, _count(DepMap
)> dependencies
;
182 bool orGroup
= false;
183 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
185 if (Dep
.IsImplicit() == true)
187 if (OnlyCritical
&& Dep
.IsCritical() == false)
189 if (orGroup
== false && dependencies
[Dep
->Type
].empty() == false)
190 dependencies
[Dep
->Type
].append(", ");
191 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
192 if (Dep
->Version
!= 0)
193 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
194 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
196 dependencies
[Dep
->Type
].append(" | ");
202 bool Okay
= output
.Failed() == false;
203 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
204 if (dependencies
[i
].empty() == false)
205 WriteOkay(Okay
, output
, "\n", DepMap
[i
], ": ", dependencies
[i
]);
207 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
209 if (Prv
.IsMultiArchImplicit() == true)
211 if (provides
.empty() == false)
212 provides
.append(", ");
213 provides
.append(Prv
.Name());
214 if (Prv
->ProvideVersion
!= 0)
215 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
217 if (provides
.empty() == false)
218 WriteOkay(Okay
, output
, "\nProvides: ", provides
);
219 return WriteOkay(Okay
, output
, "\n");
222 // WriteScenarioLimitedDependency /*{{{*/
223 static void WriteScenarioLimitedDependency(FILE* output
,
224 pkgCache::VerIterator
const &Ver
,
225 APT::PackageSet
const &pkgset
)
227 std::array
<std::string
, _count(DepMap
)> dependencies
;
228 bool orGroup
= false;
229 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
231 if (Dep
.IsImplicit() == true)
233 if (orGroup
== false)
235 if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
237 if (dependencies
[Dep
->Type
].empty() == false)
238 dependencies
[Dep
->Type
].append(", ");
240 else if (pkgset
.find(Dep
.TargetPkg()) == pkgset
.end())
242 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
244 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
248 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
249 if (Dep
->Version
!= 0)
250 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
251 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
253 dependencies
[Dep
->Type
].append(" | ");
259 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
260 if (dependencies
[i
].empty() == false)
261 fprintf(output
, "%s: %s\n", DepMap
[i
], dependencies
[i
].c_str());
263 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
265 if (Prv
.IsMultiArchImplicit() == true)
267 if (pkgset
.find(Prv
.ParentPkg()) == pkgset
.end())
269 if (provides
.empty() == false)
270 provides
.append(", ");
271 provides
.append(Prv
.Name());
272 if (Prv
->ProvideVersion
!= 0)
273 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
275 if (provides
.empty() == false)
276 fprintf(output
, "Provides: %s\n", provides
.c_str());
278 static bool WriteScenarioLimitedDependency(FileFd
&output
,
279 pkgCache::VerIterator
const &Ver
,
280 std::vector
<bool> const &pkgset
,
281 bool const OnlyCritical
)
283 std::array
<std::string
, _count(DepMap
)> dependencies
;
284 bool orGroup
= false;
285 for (pkgCache::DepIterator Dep
= Ver
.DependsList(); Dep
.end() == false; ++Dep
)
287 if (Dep
.IsImplicit() == true)
289 if (OnlyCritical
&& Dep
.IsCritical() == false)
291 if (orGroup
== false)
293 if (pkgset
[Dep
.TargetPkg()->ID
] == false)
295 if (dependencies
[Dep
->Type
].empty() == false)
296 dependencies
[Dep
->Type
].append(", ");
298 else if (pkgset
[Dep
.TargetPkg()->ID
] == false)
300 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
302 dependencies
[Dep
->Type
].erase(dependencies
[Dep
->Type
].end()-3, dependencies
[Dep
->Type
].end());
306 dependencies
[Dep
->Type
].append(Dep
.TargetPkg().Name());
307 if (Dep
->Version
!= 0)
308 dependencies
[Dep
->Type
].append(" (").append(pkgCache::CompTypeDeb(Dep
->CompareOp
)).append(" ").append(Dep
.TargetVer()).append(")");
309 if ((Dep
->CompareOp
& pkgCache::Dep::Or
) == pkgCache::Dep::Or
)
311 dependencies
[Dep
->Type
].append(" | ");
317 bool Okay
= output
.Failed() == false;
318 for (size_t i
= 1; i
< dependencies
.size(); ++i
)
319 if (dependencies
[i
].empty() == false)
320 WriteOkay(Okay
, output
, "\n", DepMap
[i
], ": ", dependencies
[i
]);
322 for (pkgCache::PrvIterator Prv
= Ver
.ProvidesList(); Prv
.end() == false; ++Prv
)
324 if (Prv
.IsMultiArchImplicit() == true)
326 if (pkgset
[Prv
.ParentPkg()->ID
] == false)
328 if (provides
.empty() == false)
329 provides
.append(", ");
330 provides
.append(Prv
.Name());
331 if (Prv
->ProvideVersion
!= 0)
332 provides
.append(" (= ").append(Prv
.ProvideVersion()).append(")");
334 if (provides
.empty() == false)
335 WriteOkay(Okay
, output
, "\nProvides: ", provides
);
336 return WriteOkay(Okay
, output
, "\n");
339 static bool SkipUnavailableVersions(pkgDepCache
&Cache
, pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
)/*{{{*/
341 /* versions which aren't current and aren't available in
342 any "online" source file are bad, expect if they are the choosen
343 candidate: The exception is for build-dep implementation as it creates
344 such pseudo (package) versions and removes them later on again.
345 We filter out versions at all so packages in 'rc' state only available
346 in dpkg/status aren't passed to solvers as they can't be installed. */
347 if (Pkg
->CurrentVer
!= 0)
349 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
351 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
)
352 if (I
.File().Flagged(pkgCache::Flag::NotSource
) == false)
357 static bool WriteScenarioEDSPVersion(pkgDepCache
&Cache
, FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,/*{{{*/
358 pkgCache::VerIterator
const &Ver
)
360 bool Okay
= WriteOkay(output
, "\nSource: ", Ver
.SourcePkgName(),
361 "\nSource-Version: ", Ver
.SourceVerStr());
362 if (PrioMap
[Ver
->Priority
] != nullptr)
363 WriteOkay(Okay
, output
, "\nPriority: ", PrioMap
[Ver
->Priority
]);
364 if (Ver
->Section
!= 0)
365 WriteOkay(Okay
, output
, "\nSection: ", Ver
.Section());
366 if (Pkg
.CurrentVer() == Ver
)
367 WriteOkay(Okay
, output
, "\nInstalled: yes");
368 if (Pkg
->SelectedState
== pkgCache::State::Hold
||
369 (Cache
[Pkg
].Keep() == true && Cache
[Pkg
].Protect() == true))
370 WriteOkay(Okay
, output
, "\nHold: yes");
371 std::set
<string
> Releases
;
372 for (pkgCache::VerFileIterator I
= Ver
.FileList(); I
.end() == false; ++I
) {
373 pkgCache::PkgFileIterator File
= I
.File();
374 if (File
.Flagged(pkgCache::Flag::NotSource
) == false) {
375 string Release
= File
.RelStr();
376 if (!Release
.empty())
377 Releases
.insert(Release
);
380 if (!Releases
.empty()) {
381 WriteOkay(Okay
, output
, "\nAPT-Release:");
382 for (std::set
<string
>::iterator R
= Releases
.begin(); R
!= Releases
.end(); ++R
)
383 WriteOkay(Okay
, output
, "\n ", *R
);
385 WriteOkay(Okay
, output
, "\nAPT-Pin: ", Cache
.GetPolicy().GetPriority(Ver
));
386 if (Cache
.GetCandidateVersion(Pkg
) == Ver
)
387 WriteOkay(Okay
, output
, "\nAPT-Candidate: yes");
388 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
389 WriteOkay(Okay
, output
, "\nAPT-Automatic: yes");
393 // EDSP::WriteScenario - to the given file descriptor /*{{{*/
394 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FILE* output
, OpProgress
*Progress
)
396 if (Progress
!= NULL
)
397 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
399 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
400 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
402 std::string
const arch
= Pkg
.Arch();
403 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
405 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
, ++p
)
407 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
409 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
410 WriteScenarioDependency(output
, Ver
);
411 fprintf(output
, "\n");
412 if (Progress
!= NULL
&& p
% 100 == 0)
413 Progress
->Progress(p
);
418 bool EDSP::WriteScenario(pkgDepCache
&Cache
, FileFd
&output
, OpProgress
*Progress
)
420 if (Progress
!= NULL
)
421 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
423 bool Okay
= output
.Failed() == false;
424 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
425 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
)
427 std::string
const arch
= Pkg
.Arch();
428 if (std::find(archs
.begin(), archs
.end(), arch
) == archs
.end())
430 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false && likely(Okay
); ++Ver
, ++p
)
432 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
434 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
435 Okay
&= WriteScenarioEDSPVersion(Cache
, output
, Pkg
, Ver
);
436 Okay
&= WriteScenarioDependency(output
, Ver
, false);
437 WriteOkay(Okay
, output
, "\n");
438 if (Progress
!= NULL
&& p
% 100 == 0)
439 Progress
->Progress(p
);
445 // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
446 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FILE* output
,
447 APT::PackageSet
const &pkgset
,
448 OpProgress
*Progress
)
450 if (Progress
!= NULL
)
451 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
453 for (APT::PackageSet::const_iterator Pkg
= pkgset
.begin(); Pkg
!= pkgset
.end(); ++Pkg
, ++p
)
454 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
456 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
458 WriteScenarioVersion(Cache
, output
, Pkg
, Ver
);
459 WriteScenarioLimitedDependency(output
, Ver
, pkgset
);
460 fprintf(output
, "\n");
461 if (Progress
!= NULL
&& p
% 100 == 0)
462 Progress
->Progress(p
);
464 if (Progress
!= NULL
)
468 bool EDSP::WriteLimitedScenario(pkgDepCache
&Cache
, FileFd
&output
,
469 std::vector
<bool> const &pkgset
,
470 OpProgress
*Progress
)
472 if (Progress
!= NULL
)
473 Progress
->SubProgress(Cache
.Head().VersionCount
, _("Send scenario to solver"));
475 bool Okay
= output
.Failed() == false;
476 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
, ++p
)
478 if (pkgset
[Pkg
->ID
] == false)
480 for (pkgCache::VerIterator Ver
= Pkg
.VersionList(); Ver
.end() == false && likely(Okay
); ++Ver
)
482 if (SkipUnavailableVersions(Cache
, Pkg
, Ver
))
484 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
485 Okay
&= WriteScenarioEDSPVersion(Cache
, output
, Pkg
, Ver
);
486 Okay
&= WriteScenarioLimitedDependency(output
, Ver
, pkgset
, false);
487 WriteOkay(Okay
, output
, "\n");
488 if (Progress
!= NULL
&& p
% 100 == 0)
489 Progress
->Progress(p
);
492 if (Progress
!= NULL
)
497 // EDSP::WriteRequest - to the given file descriptor /*{{{*/
498 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FILE* output
, bool const Upgrade
,
499 bool const DistUpgrade
, bool const AutoRemove
,
500 OpProgress
*Progress
)
502 if (Progress
!= NULL
)
503 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
506 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
508 if (Progress
!= NULL
&& p
% 100 == 0)
509 Progress
->Progress(p
);
511 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
512 if (P
.Delete() == true)
514 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
515 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
519 req
->append(" ").append(Pkg
.FullName());
521 fprintf(output
, "Request: EDSP 0.5\n");
523 const char *arch
= _config
->Find("APT::Architecture").c_str();
524 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
525 fprintf(output
, "Architecture: %s\n", arch
);
526 fprintf(output
, "Architectures:");
527 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
528 fprintf(output
, " %s", a
->c_str());
529 fprintf(output
, "\n");
531 if (del
.empty() == false)
532 fprintf(output
, "Remove: %s\n", del
.c_str()+1);
533 if (inst
.empty() == false)
534 fprintf(output
, "Install: %s\n", inst
.c_str()+1);
536 fprintf(output
, "Upgrade: yes\n");
537 if (DistUpgrade
== true)
538 fprintf(output
, "Dist-Upgrade: yes\n");
539 if (AutoRemove
== true)
540 fprintf(output
, "Autoremove: yes\n");
541 auto const solver
= _config
->Find("APT::Solver", "internal");
542 fprintf(output
, "Solver: %s\n", solver
.c_str());
543 auto const solverconf
= std::string("APT::Solver::") + solver
+ "::";
544 if (_config
->FindB(solverconf
+ "Strict-Pinning", _config
->FindB("APT::Solver::Strict-Pinning", true)) == false)
545 fprintf(output
, "Strict-Pinning: no\n");
546 auto const solverpref
= _config
->Find(solverconf
+ "Preferences", _config
->Find("APT::Solver::Preferences", ""));
547 if (solverpref
.empty() == false)
548 fprintf(output
, "Preferences: %s\n", solverpref
.c_str());
549 fprintf(output
, "\n");
552 bool EDSP::WriteRequest(pkgDepCache
&Cache
, FileFd
&output
,
553 unsigned int const flags
,
554 OpProgress
*Progress
)
556 if (Progress
!= NULL
)
557 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to solver"));
560 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
562 if (Progress
!= NULL
&& p
% 100 == 0)
563 Progress
->Progress(p
);
565 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
566 if (P
.Delete() == true)
568 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.ReInstall() == true ||
569 (P
.Mode
== pkgDepCache::ModeKeep
&& (P
.iFlags
& pkgDepCache::Protected
) == pkgDepCache::Protected
))
573 req
->append(" ").append(Pkg
.FullName());
575 bool Okay
= WriteOkay(output
, "Request: EDSP 0.5\n");
577 const char *arch
= _config
->Find("APT::Architecture").c_str();
578 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
579 WriteOkay(Okay
, output
, "Architecture: ", arch
, "\n",
581 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
582 WriteOkay(Okay
, output
, " ", *a
);
583 WriteOkay(Okay
, output
, "\n");
585 if (del
.empty() == false)
586 WriteOkay(Okay
, output
, "Remove:", del
, "\n");
587 if (inst
.empty() == false)
588 WriteOkay(Okay
, output
, "Install:", inst
, "\n");
589 if (flags
& Request::AUTOREMOVE
)
590 WriteOkay(Okay
, output
, "Autoremove: yes\n");
591 if (flags
& Request::UPGRADE_ALL
)
593 WriteOkay(Okay
, output
, "Upgrade-All: yes\n");
594 if (flags
& (Request::FORBID_NEW_INSTALL
| Request::FORBID_REMOVE
))
595 WriteOkay(Okay
, output
, "Upgrade: yes\n");
597 WriteOkay(Okay
, output
, "Dist-Upgrade: yes\n");
599 if (flags
& Request::FORBID_NEW_INSTALL
)
600 WriteOkay(Okay
, output
, "Forbid-New-Install: yes\n");
601 if (flags
& Request::FORBID_REMOVE
)
602 WriteOkay(Okay
, output
, "Forbid-Remove: yes\n");
603 auto const solver
= _config
->Find("APT::Solver", "internal");
604 WriteOkay(Okay
, output
, "Solver: ", solver
, "\n");
605 if (_config
->FindB("APT::Solver::Strict-Pinning", true) == false)
606 WriteOkay(Okay
, output
, "Strict-Pinning: no\n");
607 string
solverpref("APT::Solver::");
608 solverpref
.append(solver
).append("::Preferences");
609 if (_config
->Exists(solverpref
) == true)
610 WriteOkay(Okay
, output
, "Preferences: ", _config
->Find(solverpref
,""), "\n");
611 return WriteOkay(Okay
, output
, "\n");
614 // EDSP::ReadResponse - from the given file descriptor /*{{{*/
615 bool EDSP::ReadResponse(int const input
, pkgDepCache
&Cache
, OpProgress
*Progress
) {
616 /* We build an map id to mmap offset here
617 In theory we could use the offset as ID, but then VersionCount
618 couldn't be used to create other versionmappings anymore and it
619 would be too easy for a (buggy) solver to segfault APT… */
620 unsigned long long const VersionCount
= Cache
.Head().VersionCount
;
621 unsigned long VerIdx
[VersionCount
];
622 for (pkgCache::PkgIterator P
= Cache
.PkgBegin(); P
.end() == false; ++P
) {
623 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
624 VerIdx
[V
->ID
] = V
.Index();
625 Cache
[P
].Marked
= true;
626 Cache
[P
].Garbage
= false;
630 in
.OpenDescriptor(input
, FileFd::ReadOnly
, true);
631 pkgTagFile
response(&in
, 100);
632 pkgTagSection section
;
634 std::set
<decltype(Cache
.PkgBegin()->ID
)> seenOnce
;
635 while (response
.Step(section
) == true) {
637 if (section
.Exists("Install") == true)
639 else if (section
.Exists("Remove") == true)
641 else if (section
.Exists("Progress") == true) {
642 if (Progress
!= NULL
) {
643 string msg
= section
.FindS("Message");
644 if (msg
.empty() == true)
645 msg
= _("Prepare for receiving solution");
646 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
649 } else if (section
.Exists("Error") == true) {
650 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
651 if (msg
.empty() == true) {
652 msg
= _("External solver failed without a proper error message");
653 _error
->Error("%s", msg
.c_str());
655 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
656 if (Progress
!= NULL
)
658 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
659 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
660 std::cerr
<< msg
<< std::endl
<< std::endl
;
662 } else if (section
.Exists("Autoremove") == true)
665 char const *Start
, *End
;
666 section
.GetSection(Start
, End
);
667 _error
->Warning("Encountered an unexpected section with %d fields: %s", section
.Count(), std::string(Start
, End
).c_str());
671 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
672 if (id
== VersionCount
) {
673 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
675 } else if (id
> Cache
.Head().VersionCount
) {
676 _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());
680 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
681 auto const Pkg
= Ver
.ParentPkg();
682 if (type
== "Autoremove") {
683 Cache
[Pkg
].Marked
= false;
684 Cache
[Pkg
].Garbage
= true;
685 } else if (seenOnce
.emplace(Pkg
->ID
).second
== false) {
686 _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());
687 } else if (type
== "Install") {
688 if (Pkg
.CurrentVer() == Ver
) {
689 _error
->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
690 Ver
.VerStr(), Pkg
.FullName(false).c_str());
692 Cache
.SetCandidateVersion(Ver
);
693 Cache
.MarkInstall(Pkg
, false, 0, false);
695 } else if (type
== "Remove") {
696 if (Pkg
->CurrentVer
== 0)
697 _error
->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
698 Ver
.VerStr(), Pkg
.FullName(false).c_str());
699 else if (Pkg
.CurrentVer() != Ver
)
700 _error
->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
701 Ver
.VerStr(), Pkg
.FullName(false).c_str(), Pkg
.CurrentVer().VerStr());
703 Cache
.MarkDelete(Ver
.ParentPkg(), false);
709 // ReadLine - first line from the given file descriptor /*{{{*/
710 // ---------------------------------------------------------------------
711 /* Little helper method to read a complete line into a string. Similar to
712 fgets but we need to use the low-level read() here as otherwise the
713 listparser will be confused later on as mixing of fgets and read isn't
714 a supported action according to the manpages and results are undefined */
715 static bool ReadLine(int const input
, std::string
&line
) {
720 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
727 if (line
.empty() == true && isblank(one
) != 0)
734 // StringToBool - convert yes/no to bool /*{{{*/
735 // ---------------------------------------------------------------------
736 /* we are not as lazy as we are in the global StringToBool as we really
737 only accept yes/no here */
738 static bool localStringToBool(std::string answer
, bool const defValue
) {
739 std::transform(answer
.begin(), answer
.end(), answer
.begin(), ::tolower
);
742 else if (answer
== "no")
745 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
.c_str());
749 static bool LineStartsWithAndStrip(std::string
&line
, APT::StringView
const with
)/*{{{*/
751 if (line
.compare(0, with
.size(), with
.data()) != 0)
753 line
= APT::String::Strip(line
.substr(with
.length()));
757 static bool ReadFlag(unsigned int &flags
, std::string
&line
, APT::StringView
const name
, unsigned int const setflag
)/*{{{*/
759 if (LineStartsWithAndStrip(line
, name
) == false)
761 if (localStringToBool(line
, false))
768 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
769 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
770 std::list
<std::string
> &remove
, unsigned int &flags
)
776 while (ReadLine(input
, line
) == true)
778 // Skip empty lines before request
779 if (line
.empty() == true)
781 // The first Tag must be a request, so search for it
782 if (LineStartsWithAndStrip(line
, "Request:"))
785 while (ReadLine(input
, line
) == true)
787 // empty lines are the end of the request
788 if (line
.empty() == true)
791 std::list
<std::string
> *request
= NULL
;
792 if (LineStartsWithAndStrip(line
, "Install:"))
794 else if (LineStartsWithAndStrip(line
, "Remove:"))
796 else if (ReadFlag(flags
, line
, "Upgrade:", (Request::UPGRADE_ALL
| Request::FORBID_REMOVE
| Request::FORBID_NEW_INSTALL
)) ||
797 ReadFlag(flags
, line
, "Dist-Upgrade:", Request::UPGRADE_ALL
) ||
798 ReadFlag(flags
, line
, "Upgrade-All:", Request::UPGRADE_ALL
) ||
799 ReadFlag(flags
, line
, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL
) ||
800 ReadFlag(flags
, line
, "Forbid-Remove:", Request::FORBID_REMOVE
) ||
801 ReadFlag(flags
, line
, "Autoremove:", Request::AUTOREMOVE
))
803 else if (LineStartsWithAndStrip(line
, "Architecture:"))
804 _config
->Set("APT::Architecture", line
);
805 else if (LineStartsWithAndStrip(line
, "Architectures:"))
806 _config
->Set("APT::Architectures", SubstVar(line
, " ", ","));
807 else if (LineStartsWithAndStrip(line
, "Solver:"))
808 ; // purely informational line
810 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
814 auto const pkgs
= VectorizeString(line
, ' ');
815 std::move(pkgs
.begin(), pkgs
.end(), std::back_inserter(*request
));
820 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
821 std::list
<std::string
> &remove
, bool &upgrade
,
822 bool &distUpgrade
, bool &autoRemove
)
825 auto const ret
= ReadRequest(input
, install
, remove
, flags
);
826 autoRemove
= (flags
& Request::AUTOREMOVE
);
827 if (flags
& Request::UPGRADE_ALL
)
829 if (flags
& (Request::FORBID_NEW_INSTALL
| Request::FORBID_REMOVE
))
846 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
847 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
848 std::list
<std::string
> const &remove
,
851 for (std::list
<std::string
>::const_iterator i
= install
.begin();
852 i
!= install
.end(); ++i
) {
853 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
855 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
857 Cache
.MarkInstall(P
, false);
860 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
861 i
!= remove
.end(); ++i
) {
862 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
864 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
871 // EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
872 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
874 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
875 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
877 if (Cache
[Pkg
].Delete() == true)
879 fprintf(output
, "Remove: %d\n", _system
->GetVersionMapping(Pkg
.CurrentVer()->ID
));
881 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
883 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
885 pkgCache::VerIterator
const CandVer
= Cache
.GetCandidateVersion(Pkg
);
886 fprintf(output
, "Install: %d\n", _system
->GetVersionMapping(CandVer
->ID
));
888 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), CandVer
.VerStr());
890 else if (Cache
[Pkg
].Garbage
== true)
892 fprintf(output
, "Autoremove: %d\n", _system
->GetVersionMapping(Pkg
.CurrentVer()->ID
));
894 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
898 fprintf(output
, "\n");
903 bool EDSP::WriteSolutionStanza(FileFd
&output
, char const * const Type
, pkgCache::VerIterator
const &Ver
)
905 bool Okay
= output
.Failed() == false;
906 WriteOkay(Okay
, output
, Type
, ": ", _system
->GetVersionMapping(Ver
->ID
));
907 if (_config
->FindB("Debug::EDSP::WriteSolution", false) == true)
908 WriteOkay(Okay
, output
, "\nPackage: ", Ver
.ParentPkg().FullName(), "\nVersion: ", Ver
.VerStr());
909 return WriteOkay(Okay
, output
, "\n\n");
912 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
913 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
914 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
), true).c_str());
915 fprintf(output
, "Percentage: %d\n", percent
);
916 fprintf(output
, "Message: %s\n\n", message
);
920 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FileFd
&output
) {
921 return WriteOkay(output
, "Progress: ", TimeRFC1123(time(NULL
), true), "\n",
922 "Percentage: ", percent
, "\n",
923 "Message: ", message
, "\n\n") && output
.Flush();
926 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
927 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
928 fprintf(output
, "Error: %s\n", uuid
);
929 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
932 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FileFd
&output
) {
933 return WriteOkay(output
, "Error: ", uuid
, "\n",
934 "Message: ", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n "),
938 static std::string
findExecutable(std::vector
<std::string
> const &dirs
, char const * const binary
) {/*{{{*/
939 for (auto && dir
: dirs
) {
940 std::string
const file
= flCombine(dir
, binary
);
941 if (RealFileExists(file
) == true)
947 static pid_t
ExecuteExternal(char const* const type
, char const * const binary
, char const * const configdir
, int * const solver_in
, int * const solver_out
) {/*{{{*/
948 auto const solverDirs
= _config
->FindVector(configdir
);
949 auto const file
= findExecutable(solverDirs
, binary
);
952 dumper
= findExecutable(solverDirs
, "apt-dump-solver");
954 dumper
= findExecutable(solverDirs
, "dump");
957 if (file
.empty() == true)
959 _error
->Error("Can't call external %s '%s' as it is not in a configured directory!", type
, binary
);
962 int external
[4] = {-1, -1, -1, -1};
963 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
965 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
968 for (int i
= 0; i
< 4; ++i
)
969 SetCloseExec(external
[i
], true);
971 pid_t Solver
= ExecFork();
973 dup2(external
[0], STDIN_FILENO
);
974 dup2(external
[3], STDOUT_FILENO
);
975 auto const dumpfile
= _config
->FindFile((std::string("Dir::Log::") + type
).c_str());
976 auto const dumpdir
= flNotFile(dumpfile
);
977 auto const runasuser
= _config
->Find(std::string("APT::") + type
+ "::" + binary
+ "::RunAsUser",
978 _config
->Find(std::string("APT::") + type
+ "::RunAsUser",
979 _config
->Find("APT::Sandbox::User")));
980 if (dumper
.empty() || dumpfile
.empty() || dumper
== file
|| CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false)
982 _config
->Set("APT::Sandbox::User", runasuser
);
984 char const * const calling
[] = { file
.c_str(), nullptr };
985 execv(calling
[0], const_cast<char**>(calling
));
989 char const * const calling
[] = { dumper
.c_str(), "--user", runasuser
.c_str(), dumpfile
.c_str(), file
.c_str(), nullptr };
990 execv(calling
[0], const_cast<char**>(calling
));
992 std::cerr
<< "Failed to execute " << type
<< " '" << binary
<< "'!" << std::endl
;
998 if (WaitFd(external
[1], true, 5) == false)
1000 _error
->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type
);
1004 *solver_in
= external
[1];
1005 *solver_out
= external
[2];
1009 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1010 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
1011 return ExecuteExternal("solver", solver
, "Dir::Bin::Solvers", solver_in
, solver_out
);
1013 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
1014 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
1019 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1020 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
1021 unsigned int const flags
, OpProgress
*Progress
) {
1022 if (strcmp(solver
, "internal") == 0)
1024 auto const dumpfile
= _config
->FindFile("Dir::Log::Solver");
1025 if (dumpfile
.empty())
1027 auto const dumpdir
= flNotFile(dumpfile
);
1029 _error
->PushToStack();
1030 bool errored_out
= CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false ||
1031 output
.Open(dumpfile
, FileFd::WriteOnly
| FileFd::Exclusive
| FileFd::Create
, FileFd::Extension
, 0644) == false;
1032 std::vector
<std::string
> downgrademsgs
;
1033 while (_error
->empty() == false)
1036 _error
->PopMessage(msg
);
1037 downgrademsgs
.emplace_back(std::move(msg
));
1039 _error
->RevertToStack();
1040 for (auto && msg
: downgrademsgs
)
1041 _error
->Warning("%s", msg
.c_str());
1043 return _error
->WarningE("EDSP::Resolve", _("Could not open file '%s'"), dumpfile
.c_str());
1044 bool Okay
= EDSP::WriteRequest(Cache
, output
, flags
, nullptr);
1045 return Okay
&& EDSP::WriteScenario(Cache
, output
, nullptr);
1047 int solver_in
, solver_out
;
1048 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
1049 if (solver_pid
== 0)
1053 if (output
.OpenDescriptor(solver_in
, FileFd::WriteOnly
| FileFd::BufferedWrite
, true) == false)
1054 return _error
->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver
, solver_in
);
1056 bool Okay
= output
.Failed() == false;
1057 if (Progress
!= NULL
)
1058 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
1059 Okay
&= EDSP::WriteRequest(Cache
, output
, flags
, Progress
);
1060 if (Progress
!= NULL
)
1061 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
1062 Okay
&= EDSP::WriteScenario(Cache
, output
, Progress
);
1065 if (Progress
!= NULL
)
1066 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
1067 if (Okay
&& EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
1070 return ExecWait(solver_pid
, solver
);
1072 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
1073 bool const upgrade
, bool const distUpgrade
,
1074 bool const autoRemove
, OpProgress
*Progress
) {
1075 unsigned int flags
= 0;
1077 flags
|= Request::AUTOREMOVE
;
1079 flags
|= Request::UPGRADE_ALL
| Request::FORBID_REMOVE
| Request::FORBID_NEW_INSTALL
;
1081 flags
|= Request::UPGRADE_ALL
;
1082 return ResolveExternal(solver
, Cache
, flags
, Progress
);
1086 bool EIPP::OrderInstall(char const * const solver
, pkgPackageManager
* const PM
, /*{{{*/
1087 unsigned int const flags
, OpProgress
* const Progress
)
1089 if (strcmp(solver
, "internal") == 0)
1091 auto const dumpfile
= _config
->FindFile("Dir::Log::Planner");
1092 if (dumpfile
.empty())
1094 auto const dumpdir
= flNotFile(dumpfile
);
1096 _error
->PushToStack();
1097 bool errored_out
= CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false ||
1098 output
.Open(dumpfile
, FileFd::WriteOnly
| FileFd::Exclusive
| FileFd::Create
, FileFd::Extension
, 0644) == false;
1099 std::vector
<std::string
> downgrademsgs
;
1100 while (_error
->empty() == false)
1103 _error
->PopMessage(msg
);
1104 downgrademsgs
.emplace_back(std::move(msg
));
1106 _error
->RevertToStack();
1107 for (auto && msg
: downgrademsgs
)
1108 _error
->Warning("%s", msg
.c_str());
1110 return _error
->WarningE("EIPP::OrderInstall", _("Could not open file '%s'"), dumpfile
.c_str());
1111 bool Okay
= EIPP::WriteRequest(PM
->Cache
, output
, flags
, nullptr);
1112 return Okay
&& EIPP::WriteScenario(PM
->Cache
, output
, nullptr);
1115 int solver_in
, solver_out
;
1116 pid_t
const solver_pid
= ExecuteExternal("planner", solver
, "Dir::Bin::Planners", &solver_in
, &solver_out
);
1117 if (solver_pid
== 0)
1121 if (output
.OpenDescriptor(solver_in
, FileFd::WriteOnly
| FileFd::BufferedWrite
, true) == false)
1122 return _error
->Errno("EIPP::OrderInstall", "Opening planner %s stdin on fd %d for writing failed", solver
, solver_in
);
1124 bool Okay
= output
.Failed() == false;
1125 if (Progress
!= NULL
)
1126 Progress
->OverallProgress(0, 100, 5, _("Execute external planner"));
1127 Okay
&= EIPP::WriteRequest(PM
->Cache
, output
, flags
, Progress
);
1128 if (Progress
!= NULL
)
1129 Progress
->OverallProgress(5, 100, 20, _("Execute external planner"));
1130 Okay
&= EIPP::WriteScenario(PM
->Cache
, output
, Progress
);
1133 if (Progress
!= NULL
)
1134 Progress
->OverallProgress(25, 100, 75, _("Execute external planner"));
1136 // we don't tell the external planners about boring things
1137 for (auto Pkg
= PM
->Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1139 if (Pkg
->CurrentState
== pkgCache::State::ConfigFiles
&& PM
->Cache
[Pkg
].Purge() == true)
1140 PM
->Remove(Pkg
, true);
1143 if (Okay
&& EIPP::ReadResponse(solver_out
, PM
, Progress
) == false)
1146 return ExecWait(solver_pid
, solver
);
1149 bool EIPP::WriteRequest(pkgDepCache
&Cache
, FileFd
&output
, /*{{{*/
1150 unsigned int const flags
,
1151 OpProgress
* const Progress
)
1153 if (Progress
!= NULL
)
1154 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to planner"));
1155 unsigned long p
= 0;
1156 string del
, inst
, reinst
;
1157 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
1159 if (Progress
!= NULL
&& p
% 100 == 0)
1160 Progress
->Progress(p
);
1162 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
1163 if (P
.Purge() == true && Pkg
->CurrentState
== pkgCache::State::ConfigFiles
)
1165 if (P
.Delete() == true)
1167 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.Downgrade() == true)
1169 else if (P
.ReInstall() == true)
1173 req
->append(" ").append(Pkg
.FullName());
1175 bool Okay
= WriteOkay(output
, "Request: EIPP 0.1\n");
1177 const char *arch
= _config
->Find("APT::Architecture").c_str();
1178 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
1179 WriteOkay(Okay
, output
, "Architecture: ", arch
, "\n",
1181 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
1182 WriteOkay(Okay
, output
, " ", *a
);
1183 WriteOkay(Okay
, output
, "\n");
1185 if (del
.empty() == false)
1186 WriteOkay(Okay
, output
, "Remove:", del
, "\n");
1187 if (inst
.empty() == false)
1188 WriteOkay(Okay
, output
, "Install:", inst
, "\n");
1189 if (reinst
.empty() == false)
1190 WriteOkay(Okay
, output
, "ReInstall:", reinst
, "\n");
1191 WriteOkay(Okay
, output
, "Planner: ", _config
->Find("APT::Planner", "internal"), "\n");
1192 if ((flags
& Request::IMMEDIATE_CONFIGURATION_ALL
) != 0)
1193 WriteOkay(Okay
, output
, "Immediate-Configuration: yes\n");
1194 else if ((flags
& Request::NO_IMMEDIATE_CONFIGURATION
) != 0)
1195 WriteOkay(Okay
, output
, "Immediate-Configuration: no\n");
1196 else if ((flags
& Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS
) != 0)
1197 WriteOkay(Okay
, output
, "Allow-Temporary-Remove-of-Essentials: yes\n");
1198 return WriteOkay(Okay
, output
, "\n");
1201 static bool WriteScenarioEIPPVersion(pkgDepCache
&, FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,/*{{{*/
1202 pkgCache::VerIterator
const &Ver
)
1205 if (Pkg
.CurrentVer() == Ver
)
1206 switch (Pkg
->CurrentState
)
1208 case pkgCache::State::NotInstalled
: WriteOkay(Okay
, output
, "\nStatus: not-installed"); break;
1209 case pkgCache::State::ConfigFiles
: WriteOkay(Okay
, output
, "\nStatus: config-files"); break;
1210 case pkgCache::State::HalfInstalled
: WriteOkay(Okay
, output
, "\nStatus: half-installed"); break;
1211 case pkgCache::State::UnPacked
: WriteOkay(Okay
, output
, "\nStatus: unpacked"); break;
1212 case pkgCache::State::HalfConfigured
: WriteOkay(Okay
, output
, "\nStatus: half-configured"); break;
1213 case pkgCache::State::TriggersAwaited
: WriteOkay(Okay
, output
, "\nStatus: triggers-awaited"); break;
1214 case pkgCache::State::TriggersPending
: WriteOkay(Okay
, output
, "\nStatus: triggers-pending"); break;
1215 case pkgCache::State::Installed
: WriteOkay(Okay
, output
, "\nStatus: installed"); break;
1220 // EIPP::WriteScenario - to the given file descriptor /*{{{*/
1221 template<typename forVersion
> void forAllInterestingVersions(pkgDepCache
&Cache
, pkgCache::PkgIterator
const &Pkg
, forVersion
const &func
)
1223 if (Pkg
->CurrentState
== pkgCache::State::NotInstalled
)
1225 auto P
= Cache
[Pkg
];
1226 if (P
.Install() == false)
1228 func(Pkg
, P
.InstVerIter(Cache
));
1232 if (Pkg
->CurrentVer
!= 0)
1233 func(Pkg
, Pkg
.CurrentVer());
1234 auto P
= Cache
[Pkg
];
1235 auto const V
= P
.InstVerIter(Cache
);
1236 if (P
.Delete() == false && Pkg
.CurrentVer() != V
)
1241 bool EIPP::WriteScenario(pkgDepCache
&Cache
, FileFd
&output
, OpProgress
* const Progress
)
1243 if (Progress
!= NULL
)
1244 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send scenario to planner"));
1245 unsigned long p
= 0;
1246 bool Okay
= output
.Failed() == false;
1247 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
1248 std::vector
<bool> pkgset(Cache
.Head().PackageCount
, false);
1249 auto const MarkVersion
= [&](pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
) {
1250 pkgset
[Pkg
->ID
] = true;
1251 for (auto D
= Ver
.DependsList(); D
.end() == false; ++D
)
1253 if (D
.IsCritical() == false)
1255 auto const P
= D
.TargetPkg();
1256 for (auto Prv
= P
.ProvidesList(); Prv
.end() == false; ++Prv
)
1258 auto const V
= Prv
.OwnerVer();
1259 auto const PV
= V
.ParentPkg();
1260 if (V
== PV
.CurrentVer() || V
== Cache
[PV
].InstVerIter(Cache
))
1261 pkgset
[PV
->ID
] = true;
1263 pkgset
[P
->ID
] = true;
1264 if (strcmp(P
.Arch(), "any") == 0)
1266 APT::StringView
const pkgname(P
.Name());
1267 auto const idxColon
= pkgname
.find(':');
1268 if (idxColon
!= APT::StringView::npos
)
1270 pkgCache::PkgIterator PA
;
1271 if (pkgname
.substr(idxColon
+ 1) == "any")
1273 auto const GA
= Cache
.FindGrp(pkgname
.substr(0, idxColon
).to_string());
1274 for (auto PA
= GA
.PackageList(); PA
.end() == false; PA
= GA
.NextPkg(PA
))
1276 pkgset
[PA
->ID
] = true;
1281 auto const PA
= Cache
.FindPkg(pkgname
.to_string());
1282 if (PA
.end() == false)
1283 pkgset
[PA
->ID
] = true;
1289 auto const PA
= Cache
.FindPkg(P
.FullName(false), "any");
1290 if (PA
.end() == false)
1291 pkgset
[PA
->ID
] = true;
1295 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1296 forAllInterestingVersions(Cache
, Pkg
, MarkVersion
);
1297 auto const WriteVersion
= [&](pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
) {
1298 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
1299 Okay
&= WriteScenarioEIPPVersion(Cache
, output
, Pkg
, Ver
);
1300 Okay
&= WriteScenarioLimitedDependency(output
, Ver
, pkgset
, true);
1301 WriteOkay(Okay
, output
, "\n");
1302 if (Progress
!= NULL
&& p
% 100 == 0)
1303 Progress
->Progress(p
);
1305 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
, ++p
)
1307 if (pkgset
[Pkg
->ID
] == false || Pkg
->VersionList
== 0)
1309 forAllInterestingVersions(Cache
, Pkg
, WriteVersion
);
1314 // EIPP::ReadResponse - from the given file descriptor /*{{{*/
1315 bool EIPP::ReadResponse(int const input
, pkgPackageManager
* const PM
, OpProgress
*Progress
) {
1316 /* We build an map id to mmap offset here
1317 In theory we could use the offset as ID, but then VersionCount
1318 couldn't be used to create other versionmappings anymore and it
1319 would be too easy for a (buggy) solver to segfault APT… */
1320 unsigned long long const VersionCount
= PM
->Cache
.Head().VersionCount
;
1321 unsigned long VerIdx
[VersionCount
];
1322 for (pkgCache::PkgIterator P
= PM
->Cache
.PkgBegin(); P
.end() == false; ++P
) {
1323 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
1324 VerIdx
[V
->ID
] = V
.Index();
1328 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
1329 pkgTagFile
response(&in
, 100);
1330 pkgTagSection section
;
1332 while (response
.Step(section
) == true) {
1333 char const * type
= nullptr;
1334 if (section
.Exists("Progress") == true) {
1335 if (Progress
!= NULL
) {
1336 string msg
= section
.FindS("Message");
1337 if (msg
.empty() == true)
1338 msg
= _("Prepare for receiving solution");
1339 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
1342 } else if (section
.Exists("Error") == true) {
1343 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
1344 if (msg
.empty() == true) {
1345 msg
= _("External planner failed without a proper error message");
1346 _error
->Error("%s", msg
.c_str());
1348 _error
->Error("External planner failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
1349 if (Progress
!= NULL
)
1351 std::cerr
<< "The planner encountered an error of type: " << section
.FindS("Error") << std::endl
;
1352 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
1353 std::cerr
<< msg
<< std::endl
<< std::endl
;
1355 } else if (section
.Exists("Unpack") == true)
1357 else if (section
.Exists("Configure") == true)
1359 else if (section
.Exists("Remove") == true)
1362 char const *Start
, *End
;
1363 section
.GetSection(Start
, End
);
1364 _error
->Warning("Encountered an unexpected section with %d fields: %s", section
.Count(), std::string(Start
, End
).c_str());
1368 if (type
== nullptr)
1370 size_t const id
= section
.FindULL(type
, VersionCount
);
1371 if (id
== VersionCount
) {
1372 _error
->Warning("Unable to parse %s request with id value '%s'!", type
, section
.FindS(type
).c_str());
1374 } else if (id
> PM
->Cache
.Head().VersionCount
) {
1375 _error
->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section
.FindS(type
).c_str(), type
);
1379 pkgCache::VerIterator
Ver(PM
->Cache
.GetCache(), PM
->Cache
.GetCache().VerP
+ VerIdx
[id
]);
1380 auto const Pkg
= Ver
.ParentPkg();
1381 if (strcmp(type
, "Unpack") == 0)
1382 PM
->Install(Pkg
, PM
->FileNames
[Pkg
->ID
]);
1383 else if (strcmp(type
, "Configure") == 0)
1385 else if (strcmp(type
, "Remove") == 0)
1386 PM
->Remove(Pkg
, PM
->Cache
[Pkg
].Purge());
1388 return in
.Failed() == false;
1391 bool EIPP::ReadRequest(int const input
, std::list
<std::pair
<std::string
,PKG_ACTION
>> &actions
,/*{{{*/
1392 unsigned int &flags
)
1397 while (ReadLine(input
, line
) == true)
1399 // Skip empty lines before request
1400 if (line
.empty() == true)
1402 // The first Tag must be a request, so search for it
1403 if (line
.compare(0, 8, "Request:") != 0)
1406 while (ReadLine(input
, line
) == true)
1408 // empty lines are the end of the request
1409 if (line
.empty() == true)
1412 PKG_ACTION pkgact
= PKG_ACTION::NOOP
;
1413 if (LineStartsWithAndStrip(line
, "Install:"))
1414 pkgact
= PKG_ACTION::INSTALL
;
1415 else if (LineStartsWithAndStrip(line
, "ReInstall:"))
1416 pkgact
= PKG_ACTION::REINSTALL
;
1417 else if (LineStartsWithAndStrip(line
, "Remove:"))
1418 pkgact
= PKG_ACTION::REMOVE
;
1419 else if (LineStartsWithAndStrip(line
, "Architecture:"))
1420 _config
->Set("APT::Architecture", line
);
1421 else if (LineStartsWithAndStrip(line
, "Architectures:"))
1422 _config
->Set("APT::Architectures", SubstVar(line
, " ", ","));
1423 else if (LineStartsWithAndStrip(line
, "Planner:"))
1424 ; // purely informational line
1425 else if (LineStartsWithAndStrip(line
, "Immediate-Configuration:"))
1427 if (localStringToBool(line
, true))
1428 flags
|= Request::IMMEDIATE_CONFIGURATION_ALL
;
1430 flags
|= Request::NO_IMMEDIATE_CONFIGURATION
;
1432 else if (ReadFlag(flags
, line
, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS
))
1435 _error
->Warning("Unknown line in EIPP Request stanza: %s", line
.c_str());
1437 if (pkgact
== PKG_ACTION::NOOP
)
1439 for (auto && p
: VectorizeString(line
, ' '))
1440 actions
.emplace_back(std::move(p
), pkgact
);
1446 bool EIPP::ApplyRequest(std::list
<std::pair
<std::string
,PKG_ACTION
>> &actions
,/*{{{*/
1449 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1452 for (auto Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
1455 if (Pkg
.CurrentVer() == Ver
)
1457 Cache
.SetCandidateVersion(Ver
);
1459 if (unlikely(versions
> 2))
1460 _error
->Warning("Package %s has %d versions, but should have at most 2!", Pkg
.FullName().c_str(), versions
);
1462 for (auto && a
: actions
)
1464 pkgCache::PkgIterator P
= Cache
.FindPkg(a
.first
);
1465 if (P
.end() == true)
1467 _error
->Warning("Package %s is not known, so can't be acted on", a
.first
.c_str());
1472 case PKG_ACTION::NOOP
:
1473 _error
->Warning("Package %s has NOOP as action?!?", a
.first
.c_str());
1475 case PKG_ACTION::INSTALL
:
1476 Cache
.MarkInstall(P
, false);
1478 case PKG_ACTION::REINSTALL
:
1479 Cache
.MarkInstall(P
, false);
1480 Cache
.SetReInstall(P
, true);
1482 case PKG_ACTION::REMOVE
:
1483 Cache
.MarkDelete(P
);