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 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
652 if (msg
.empty() == true) {
653 msg
= _("External solver failed without a proper error message");
654 _error
->Error("%s", msg
.c_str());
656 _error
->Error("External solver failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
657 if (Progress
!= NULL
)
659 std::cerr
<< "The solver encountered an error of type: " << section
.FindS("Error") << std::endl
;
660 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
661 std::cerr
<< msg
<< std::endl
<< std::endl
;
663 } else if (section
.Exists("Autoremove") == true)
666 char const *Start
, *End
;
667 section
.GetSection(Start
, End
);
668 _error
->Warning("Encountered an unexpected section with %d fields: %s", section
.Count(), std::string(Start
, End
).c_str());
672 size_t const id
= section
.FindULL(type
.c_str(), VersionCount
);
673 if (id
== VersionCount
) {
674 _error
->Warning("Unable to parse %s request with id value '%s'!", type
.c_str(), section
.FindS(type
.c_str()).c_str());
676 } else if (id
> Cache
.Head().VersionCount
) {
677 _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());
681 pkgCache::VerIterator
Ver(Cache
.GetCache(), Cache
.GetCache().VerP
+ VerIdx
[id
]);
682 auto const Pkg
= Ver
.ParentPkg();
683 if (type
== "Autoremove") {
684 Cache
[Pkg
].Marked
= false;
685 Cache
[Pkg
].Garbage
= true;
686 } else if (seenOnce
.emplace(Pkg
->ID
).second
== false) {
687 _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());
688 } else if (type
== "Install") {
689 if (Pkg
.CurrentVer() == Ver
) {
690 _error
->Warning("Ignoring Install stanza received for version %s of package %s which is already installed!",
691 Ver
.VerStr(), Pkg
.FullName(false).c_str());
693 Cache
.SetCandidateVersion(Ver
);
694 Cache
.MarkInstall(Pkg
, false, 0, false);
696 } else if (type
== "Remove") {
697 if (Pkg
->CurrentVer
== 0)
698 _error
->Warning("Ignoring Remove stanza received for version %s of package %s which isn't installed!",
699 Ver
.VerStr(), Pkg
.FullName(false).c_str());
700 else if (Pkg
.CurrentVer() != Ver
)
701 _error
->Warning("Ignoring Remove stanza received for version %s of package %s which isn't the installed version %s!",
702 Ver
.VerStr(), Pkg
.FullName(false).c_str(), Pkg
.CurrentVer().VerStr());
704 Cache
.MarkDelete(Ver
.ParentPkg(), false);
710 // ReadLine - first line from the given file descriptor /*{{{*/
711 // ---------------------------------------------------------------------
712 /* Little helper method to read a complete line into a string. Similar to
713 fgets but we need to use the low-level read() here as otherwise the
714 listparser will be confused later on as mixing of fgets and read isn't
715 a supported action according to the manpages and results are undefined */
716 static bool ReadLine(int const input
, std::string
&line
) {
721 while ((data
= read(input
, &one
, sizeof(one
))) != -1) {
728 if (line
.empty() == true && isblank(one
) != 0)
735 // StringToBool - convert yes/no to bool /*{{{*/
736 // ---------------------------------------------------------------------
737 /* we are not as lazy as we are in the global StringToBool as we really
738 only accept yes/no here */
739 static bool localStringToBool(std::string answer
, bool const defValue
) {
740 std::transform(answer
.begin(), answer
.end(), answer
.begin(), ::tolower
);
743 else if (answer
== "no")
746 _error
->Warning("Value '%s' is not a boolean 'yes' or 'no'!", answer
.c_str());
750 static bool LineStartsWithAndStrip(std::string
&line
, APT::StringView
const with
)/*{{{*/
752 if (line
.compare(0, with
.size(), with
.data()) != 0)
754 line
= APT::String::Strip(line
.substr(with
.length()));
758 static bool ReadFlag(unsigned int &flags
, std::string
&line
, APT::StringView
const name
, unsigned int const setflag
)/*{{{*/
760 if (LineStartsWithAndStrip(line
, name
) == false)
762 if (localStringToBool(line
, false))
769 // EDSP::ReadRequest - first stanza from the given file descriptor /*{{{*/
770 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
771 std::list
<std::string
> &remove
, unsigned int &flags
)
777 while (ReadLine(input
, line
) == true)
779 // Skip empty lines before request
780 if (line
.empty() == true)
782 // The first Tag must be a request, so search for it
783 if (LineStartsWithAndStrip(line
, "Request:"))
786 while (ReadLine(input
, line
) == true)
788 // empty lines are the end of the request
789 if (line
.empty() == true)
792 std::list
<std::string
> *request
= NULL
;
793 if (LineStartsWithAndStrip(line
, "Install:"))
795 else if (LineStartsWithAndStrip(line
, "Remove:"))
797 else if (ReadFlag(flags
, line
, "Upgrade:", (Request::UPGRADE_ALL
| Request::FORBID_REMOVE
| Request::FORBID_NEW_INSTALL
)) ||
798 ReadFlag(flags
, line
, "Dist-Upgrade:", Request::UPGRADE_ALL
) ||
799 ReadFlag(flags
, line
, "Upgrade-All:", Request::UPGRADE_ALL
) ||
800 ReadFlag(flags
, line
, "Forbid-New-Install:", Request::FORBID_NEW_INSTALL
) ||
801 ReadFlag(flags
, line
, "Forbid-Remove:", Request::FORBID_REMOVE
) ||
802 ReadFlag(flags
, line
, "Autoremove:", Request::AUTOREMOVE
))
804 else if (LineStartsWithAndStrip(line
, "Architecture:"))
805 _config
->Set("APT::Architecture", line
);
806 else if (LineStartsWithAndStrip(line
, "Architectures:"))
807 _config
->Set("APT::Architectures", SubstVar(line
, " ", ","));
808 else if (LineStartsWithAndStrip(line
, "Solver:"))
809 ; // purely informational line
811 _error
->Warning("Unknown line in EDSP Request stanza: %s", line
.c_str());
815 auto const pkgs
= VectorizeString(line
, ' ');
816 std::move(pkgs
.begin(), pkgs
.end(), std::back_inserter(*request
));
821 bool EDSP::ReadRequest(int const input
, std::list
<std::string
> &install
,
822 std::list
<std::string
> &remove
, bool &upgrade
,
823 bool &distUpgrade
, bool &autoRemove
)
826 auto const ret
= ReadRequest(input
, install
, remove
, flags
);
827 autoRemove
= (flags
& Request::AUTOREMOVE
);
828 if (flags
& Request::UPGRADE_ALL
)
830 if (flags
& (Request::FORBID_NEW_INSTALL
| Request::FORBID_REMOVE
))
847 // EDSP::ApplyRequest - first stanza from the given file descriptor /*{{{*/
848 bool EDSP::ApplyRequest(std::list
<std::string
> const &install
,
849 std::list
<std::string
> const &remove
,
852 for (std::list
<std::string
>::const_iterator i
= install
.begin();
853 i
!= install
.end(); ++i
) {
854 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
856 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
858 Cache
.MarkInstall(P
, false);
861 for (std::list
<std::string
>::const_iterator i
= remove
.begin();
862 i
!= remove
.end(); ++i
) {
863 pkgCache::PkgIterator P
= Cache
.FindPkg(*i
);
865 _error
->Warning("Package %s is not known, so can't be installed", i
->c_str());
872 // EDSP::WriteSolutionStanza - to the given file descriptor /*{{{*/
873 bool EDSP::WriteSolution(pkgDepCache
&Cache
, FILE* output
)
875 bool const Debug
= _config
->FindB("Debug::EDSP::WriteSolution", false);
876 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
878 if (Cache
[Pkg
].Delete() == true)
880 fprintf(output
, "Remove: %d\n", _system
->GetVersionMapping(Pkg
.CurrentVer()->ID
));
882 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
884 else if (Cache
[Pkg
].NewInstall() == true || Cache
[Pkg
].Upgrade() == true)
886 pkgCache::VerIterator
const CandVer
= Cache
.GetCandidateVersion(Pkg
);
887 fprintf(output
, "Install: %d\n", _system
->GetVersionMapping(CandVer
->ID
));
889 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), CandVer
.VerStr());
891 else if (Cache
[Pkg
].Garbage
== true)
893 fprintf(output
, "Autoremove: %d\n", _system
->GetVersionMapping(Pkg
.CurrentVer()->ID
));
895 fprintf(output
, "Package: %s\nVersion: %s\n", Pkg
.FullName().c_str(), Pkg
.CurrentVer().VerStr());
899 fprintf(output
, "\n");
904 bool EDSP::WriteSolutionStanza(FileFd
&output
, char const * const Type
, pkgCache::VerIterator
const &Ver
)
906 bool Okay
= output
.Failed() == false;
907 WriteOkay(Okay
, output
, Type
, ": ", _system
->GetVersionMapping(Ver
->ID
));
908 if (_config
->FindB("Debug::EDSP::WriteSolution", false) == true)
909 WriteOkay(Okay
, output
, "\nPackage: ", Ver
.ParentPkg().FullName(), "\nVersion: ", Ver
.VerStr());
910 return WriteOkay(Okay
, output
, "\n\n");
913 // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/
914 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FILE* output
) {
915 fprintf(output
, "Progress: %s\n", TimeRFC1123(time(NULL
), true).c_str());
916 fprintf(output
, "Percentage: %d\n", percent
);
917 fprintf(output
, "Message: %s\n\n", message
);
921 bool EDSP::WriteProgress(unsigned short const percent
, const char* const message
, FileFd
&output
) {
922 return WriteOkay(output
, "Progress: ", TimeRFC1123(time(NULL
), true), "\n",
923 "Percentage: ", percent
, "\n",
924 "Message: ", message
, "\n\n") && output
.Flush();
927 // EDSP::WriteError - format an error message to be send to file descriptor /*{{{*/
928 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FILE* output
) {
929 fprintf(output
, "Error: %s\n", uuid
);
930 fprintf(output
, "Message: %s\n\n", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n ").c_str());
933 bool EDSP::WriteError(char const * const uuid
, std::string
const &message
, FileFd
&output
) {
934 return WriteOkay(output
, "Error: ", uuid
, "\n",
935 "Message: ", SubstVar(SubstVar(message
, "\n\n", "\n.\n"), "\n", "\n "),
939 static std::string
findExecutable(std::vector
<std::string
> const &dirs
, char const * const binary
) {/*{{{*/
940 for (auto && dir
: dirs
) {
941 std::string
const file
= flCombine(dir
, binary
);
942 if (RealFileExists(file
) == true)
948 static pid_t
ExecuteExternal(char const* const type
, char const * const binary
, char const * const configdir
, int * const solver_in
, int * const solver_out
) {/*{{{*/
949 auto const solverDirs
= _config
->FindVector(configdir
);
950 auto const file
= findExecutable(solverDirs
, binary
);
953 dumper
= findExecutable(solverDirs
, "apt-dump-solver");
955 dumper
= findExecutable(solverDirs
, "dump");
958 if (file
.empty() == true)
960 _error
->Error("Can't call external %s '%s' as it is not in a configured directory!", type
, binary
);
963 int external
[4] = {-1, -1, -1, -1};
964 if (pipe(external
) != 0 || pipe(external
+ 2) != 0)
966 _error
->Errno("Resolve", "Can't create needed IPC pipes for EDSP");
969 for (int i
= 0; i
< 4; ++i
)
970 SetCloseExec(external
[i
], true);
972 pid_t Solver
= ExecFork();
974 dup2(external
[0], STDIN_FILENO
);
975 dup2(external
[3], STDOUT_FILENO
);
976 auto const dumpfile
= _config
->FindFile((std::string("Dir::Log::") + type
).c_str());
977 auto const dumpdir
= flNotFile(dumpfile
);
978 auto const runasuser
= _config
->Find(std::string("APT::") + type
+ "::" + binary
+ "::RunAsUser",
979 _config
->Find(std::string("APT::") + type
+ "::RunAsUser",
980 _config
->Find("APT::Sandbox::User")));
981 if (dumper
.empty() || dumpfile
.empty() || dumper
== file
|| CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false)
983 _config
->Set("APT::Sandbox::User", runasuser
);
985 char const * const calling
[] = { file
.c_str(), nullptr };
986 execv(calling
[0], const_cast<char**>(calling
));
990 char const * const calling
[] = { dumper
.c_str(), "--user", runasuser
.c_str(), dumpfile
.c_str(), file
.c_str(), nullptr };
991 execv(calling
[0], const_cast<char**>(calling
));
993 std::cerr
<< "Failed to execute " << type
<< " '" << binary
<< "'!" << std::endl
;
999 if (WaitFd(external
[1], true, 5) == false)
1001 _error
->Errno("Resolve", "Timed out while Waiting on availability of %s stdin", type
);
1005 *solver_in
= external
[1];
1006 *solver_out
= external
[2];
1010 // EDSP::ExecuteSolver - fork requested solver and setup ipc pipes {{{*/
1011 pid_t
EDSP::ExecuteSolver(const char* const solver
, int * const solver_in
, int * const solver_out
, bool) {
1012 return ExecuteExternal("solver", solver
, "Dir::Bin::Solvers", solver_in
, solver_out
);
1014 bool EDSP::ExecuteSolver(const char* const solver
, int *solver_in
, int *solver_out
) {
1015 if (ExecuteSolver(solver
, solver_in
, solver_out
, true) == 0)
1020 static bool CreateDumpFile(char const * const id
, char const * const type
, FileFd
&output
)/*{{{*/
1022 auto const dumpfile
= _config
->FindFile((std::string("Dir::Log::") + type
).c_str());
1023 if (dumpfile
.empty())
1025 auto const dumpdir
= flNotFile(dumpfile
);
1026 _error
->PushToStack();
1027 bool errored_out
= CreateAPTDirectoryIfNeeded(dumpdir
, dumpdir
) == false ||
1028 output
.Open(dumpfile
, FileFd::WriteOnly
| FileFd::Exclusive
| FileFd::Create
, FileFd::Extension
, 0644) == false;
1029 std::vector
<std::string
> downgrademsgs
;
1030 while (_error
->empty() == false)
1033 _error
->PopMessage(msg
);
1034 downgrademsgs
.emplace_back(std::move(msg
));
1036 _error
->RevertToStack();
1037 for (auto && msg
: downgrademsgs
)
1038 _error
->Warning("%s", msg
.c_str());
1040 return _error
->WarningE(id
, _("Could not open file '%s'"), dumpfile
.c_str());
1044 // EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
1045 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
1046 unsigned int const flags
, OpProgress
*Progress
) {
1047 if (strcmp(solver
, "internal") == 0)
1050 bool Okay
= CreateDumpFile("EDSP::Resolve", "solver", output
);
1051 Okay
&= EDSP::WriteRequest(Cache
, output
, flags
, nullptr);
1052 return Okay
&& EDSP::WriteScenario(Cache
, output
, nullptr);
1054 int solver_in
, solver_out
;
1055 pid_t
const solver_pid
= EDSP::ExecuteSolver(solver
, &solver_in
, &solver_out
, true);
1056 if (solver_pid
== 0)
1060 if (output
.OpenDescriptor(solver_in
, FileFd::WriteOnly
| FileFd::BufferedWrite
, true) == false)
1061 return _error
->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver
, solver_in
);
1063 bool Okay
= output
.Failed() == false;
1064 if (Okay
&& Progress
!= NULL
)
1065 Progress
->OverallProgress(0, 100, 5, _("Execute external solver"));
1066 Okay
&= EDSP::WriteRequest(Cache
, output
, flags
, Progress
);
1067 if (Okay
&& Progress
!= NULL
)
1068 Progress
->OverallProgress(5, 100, 20, _("Execute external solver"));
1069 Okay
&= EDSP::WriteScenario(Cache
, output
, Progress
);
1072 if (Okay
&& Progress
!= NULL
)
1073 Progress
->OverallProgress(25, 100, 75, _("Execute external solver"));
1074 if (Okay
&& EDSP::ReadResponse(solver_out
, Cache
, Progress
) == false)
1077 bool const waited
= ExecWait(solver_pid
, solver
);
1078 return Okay
&& waited
;
1080 bool EDSP::ResolveExternal(const char* const solver
, pkgDepCache
&Cache
,
1081 bool const upgrade
, bool const distUpgrade
,
1082 bool const autoRemove
, OpProgress
*Progress
) {
1083 unsigned int flags
= 0;
1085 flags
|= Request::AUTOREMOVE
;
1087 flags
|= Request::UPGRADE_ALL
| Request::FORBID_REMOVE
| Request::FORBID_NEW_INSTALL
;
1089 flags
|= Request::UPGRADE_ALL
;
1090 return ResolveExternal(solver
, Cache
, flags
, Progress
);
1094 bool EIPP::OrderInstall(char const * const solver
, pkgPackageManager
* const PM
, /*{{{*/
1095 unsigned int const flags
, OpProgress
* const Progress
)
1097 if (strcmp(solver
, "internal") == 0)
1100 _error
->PushToStack();
1101 bool Okay
= CreateDumpFile("EIPP::OrderInstall", "planner", output
);
1102 if (Okay
== false && dynamic_cast<pkgSimulate
*>(PM
) != nullptr)
1104 _error
->RevertToStack();
1107 _error
->MergeWithStack();
1108 Okay
&= EIPP::WriteRequest(PM
->Cache
, output
, flags
, nullptr);
1109 return Okay
&& EIPP::WriteScenario(PM
->Cache
, output
, nullptr);
1112 int solver_in
, solver_out
;
1113 pid_t
const solver_pid
= ExecuteExternal("planner", solver
, "Dir::Bin::Planners", &solver_in
, &solver_out
);
1114 if (solver_pid
== 0)
1118 if (output
.OpenDescriptor(solver_in
, FileFd::WriteOnly
| FileFd::BufferedWrite
, true) == false)
1119 return _error
->Errno("EIPP::OrderInstall", "Opening planner %s stdin on fd %d for writing failed", solver
, solver_in
);
1121 bool Okay
= output
.Failed() == false;
1122 if (Okay
&& Progress
!= NULL
)
1123 Progress
->OverallProgress(0, 100, 5, _("Execute external planner"));
1124 Okay
&= EIPP::WriteRequest(PM
->Cache
, output
, flags
, Progress
);
1125 if (Okay
&& Progress
!= NULL
)
1126 Progress
->OverallProgress(5, 100, 20, _("Execute external planner"));
1127 Okay
&= EIPP::WriteScenario(PM
->Cache
, output
, Progress
);
1132 if (Progress
!= nullptr)
1133 Progress
->OverallProgress(25, 100, 75, _("Execute external planner"));
1135 // we don't tell the external planners about boring things
1136 for (auto Pkg
= PM
->Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1138 if (Pkg
->CurrentState
== pkgCache::State::ConfigFiles
&& PM
->Cache
[Pkg
].Purge() == true)
1139 PM
->Remove(Pkg
, true);
1143 if (EIPP::ReadResponse(solver_out
, PM
, Progress
) == false)
1146 bool const waited
= ExecWait(solver_pid
, solver
);
1147 return Okay
&& waited
;
1150 bool EIPP::WriteRequest(pkgDepCache
&Cache
, FileFd
&output
, /*{{{*/
1151 unsigned int const flags
,
1152 OpProgress
* const Progress
)
1154 if (Progress
!= NULL
)
1155 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send request to planner"));
1156 unsigned long p
= 0;
1157 string del
, inst
, reinst
;
1158 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
, ++p
)
1160 if (Progress
!= NULL
&& p
% 100 == 0)
1161 Progress
->Progress(p
);
1163 pkgDepCache::StateCache
&P
= Cache
[Pkg
];
1164 if (P
.Purge() == true && Pkg
->CurrentState
== pkgCache::State::ConfigFiles
)
1166 if (P
.Delete() == true)
1168 else if (P
.NewInstall() == true || P
.Upgrade() == true || P
.Downgrade() == true)
1170 else if (P
.ReInstall() == true)
1174 req
->append(" ").append(Pkg
.FullName());
1176 bool Okay
= WriteOkay(output
, "Request: EIPP 0.1\n");
1178 const char *arch
= _config
->Find("APT::Architecture").c_str();
1179 std::vector
<string
> archs
= APT::Configuration::getArchitectures();
1180 WriteOkay(Okay
, output
, "Architecture: ", arch
, "\n",
1182 for (std::vector
<string
>::const_iterator a
= archs
.begin(); a
!= archs
.end(); ++a
)
1183 WriteOkay(Okay
, output
, " ", *a
);
1184 WriteOkay(Okay
, output
, "\n");
1186 if (del
.empty() == false)
1187 WriteOkay(Okay
, output
, "Remove:", del
, "\n");
1188 if (inst
.empty() == false)
1189 WriteOkay(Okay
, output
, "Install:", inst
, "\n");
1190 if (reinst
.empty() == false)
1191 WriteOkay(Okay
, output
, "ReInstall:", reinst
, "\n");
1192 WriteOkay(Okay
, output
, "Planner: ", _config
->Find("APT::Planner", "internal"), "\n");
1193 if ((flags
& Request::IMMEDIATE_CONFIGURATION_ALL
) != 0)
1194 WriteOkay(Okay
, output
, "Immediate-Configuration: yes\n");
1195 else if ((flags
& Request::NO_IMMEDIATE_CONFIGURATION
) != 0)
1196 WriteOkay(Okay
, output
, "Immediate-Configuration: no\n");
1197 else if ((flags
& Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS
) != 0)
1198 WriteOkay(Okay
, output
, "Allow-Temporary-Remove-of-Essentials: yes\n");
1199 return WriteOkay(Okay
, output
, "\n");
1202 static bool WriteScenarioEIPPVersion(pkgDepCache
&, FileFd
&output
, pkgCache::PkgIterator
const &Pkg
,/*{{{*/
1203 pkgCache::VerIterator
const &Ver
)
1206 if (Pkg
.CurrentVer() == Ver
)
1207 switch (Pkg
->CurrentState
)
1209 case pkgCache::State::NotInstalled
: WriteOkay(Okay
, output
, "\nStatus: not-installed"); break;
1210 case pkgCache::State::ConfigFiles
: WriteOkay(Okay
, output
, "\nStatus: config-files"); break;
1211 case pkgCache::State::HalfInstalled
: WriteOkay(Okay
, output
, "\nStatus: half-installed"); break;
1212 case pkgCache::State::UnPacked
: WriteOkay(Okay
, output
, "\nStatus: unpacked"); break;
1213 case pkgCache::State::HalfConfigured
: WriteOkay(Okay
, output
, "\nStatus: half-configured"); break;
1214 case pkgCache::State::TriggersAwaited
: WriteOkay(Okay
, output
, "\nStatus: triggers-awaited"); break;
1215 case pkgCache::State::TriggersPending
: WriteOkay(Okay
, output
, "\nStatus: triggers-pending"); break;
1216 case pkgCache::State::Installed
: WriteOkay(Okay
, output
, "\nStatus: installed"); break;
1221 // EIPP::WriteScenario - to the given file descriptor /*{{{*/
1222 template<typename forVersion
> void forAllInterestingVersions(pkgDepCache
&Cache
, pkgCache::PkgIterator
const &Pkg
, forVersion
const &func
)
1224 if (Pkg
->CurrentState
== pkgCache::State::NotInstalled
)
1226 auto P
= Cache
[Pkg
];
1227 if (P
.Install() == false)
1229 func(Pkg
, P
.InstVerIter(Cache
));
1233 if (Pkg
->CurrentVer
!= 0)
1234 func(Pkg
, Pkg
.CurrentVer());
1235 auto P
= Cache
[Pkg
];
1236 auto const V
= P
.InstVerIter(Cache
);
1237 if (P
.Delete() == false && Pkg
.CurrentVer() != V
)
1242 bool EIPP::WriteScenario(pkgDepCache
&Cache
, FileFd
&output
, OpProgress
* const Progress
)
1244 if (Progress
!= NULL
)
1245 Progress
->SubProgress(Cache
.Head().PackageCount
, _("Send scenario to planner"));
1246 unsigned long p
= 0;
1247 bool Okay
= output
.Failed() == false;
1248 std::vector
<std::string
> archs
= APT::Configuration::getArchitectures();
1249 std::vector
<bool> pkgset(Cache
.Head().PackageCount
, false);
1250 auto const MarkVersion
= [&](pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
) {
1251 pkgset
[Pkg
->ID
] = true;
1252 for (auto D
= Ver
.DependsList(); D
.end() == false; ++D
)
1254 if (D
.IsCritical() == false)
1256 auto const P
= D
.TargetPkg();
1257 for (auto Prv
= P
.ProvidesList(); Prv
.end() == false; ++Prv
)
1259 auto const V
= Prv
.OwnerVer();
1260 auto const PV
= V
.ParentPkg();
1261 if (V
== PV
.CurrentVer() || V
== Cache
[PV
].InstVerIter(Cache
))
1262 pkgset
[PV
->ID
] = true;
1264 pkgset
[P
->ID
] = true;
1265 if (strcmp(P
.Arch(), "any") == 0)
1267 APT::StringView
const pkgname(P
.Name());
1268 auto const idxColon
= pkgname
.find(':');
1269 if (idxColon
!= APT::StringView::npos
)
1271 pkgCache::PkgIterator PA
;
1272 if (pkgname
.substr(idxColon
+ 1) == "any")
1274 auto const GA
= Cache
.FindGrp(pkgname
.substr(0, idxColon
).to_string());
1275 for (auto PA
= GA
.PackageList(); PA
.end() == false; PA
= GA
.NextPkg(PA
))
1277 pkgset
[PA
->ID
] = true;
1282 auto const PA
= Cache
.FindPkg(pkgname
.to_string());
1283 if (PA
.end() == false)
1284 pkgset
[PA
->ID
] = true;
1290 auto const PA
= Cache
.FindPkg(P
.FullName(false), "any");
1291 if (PA
.end() == false)
1292 pkgset
[PA
->ID
] = true;
1296 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1297 forAllInterestingVersions(Cache
, Pkg
, MarkVersion
);
1298 auto const WriteVersion
= [&](pkgCache::PkgIterator
const &Pkg
, pkgCache::VerIterator
const &Ver
) {
1299 Okay
&= WriteScenarioVersion(output
, Pkg
, Ver
);
1300 Okay
&= WriteScenarioEIPPVersion(Cache
, output
, Pkg
, Ver
);
1301 Okay
&= WriteScenarioLimitedDependency(output
, Ver
, pkgset
, true);
1302 WriteOkay(Okay
, output
, "\n");
1303 if (Progress
!= NULL
&& p
% 100 == 0)
1304 Progress
->Progress(p
);
1306 for (pkgCache::PkgIterator Pkg
= Cache
.PkgBegin(); Pkg
.end() == false && likely(Okay
); ++Pkg
, ++p
)
1308 if (pkgset
[Pkg
->ID
] == false || Pkg
->VersionList
== 0)
1310 forAllInterestingVersions(Cache
, Pkg
, WriteVersion
);
1315 // EIPP::ReadResponse - from the given file descriptor /*{{{*/
1316 bool EIPP::ReadResponse(int const input
, pkgPackageManager
* const PM
, OpProgress
*Progress
) {
1317 /* We build an map id to mmap offset here
1318 In theory we could use the offset as ID, but then VersionCount
1319 couldn't be used to create other versionmappings anymore and it
1320 would be too easy for a (buggy) solver to segfault APT… */
1321 unsigned long long const VersionCount
= PM
->Cache
.Head().VersionCount
;
1322 unsigned long VerIdx
[VersionCount
];
1323 for (pkgCache::PkgIterator P
= PM
->Cache
.PkgBegin(); P
.end() == false; ++P
) {
1324 for (pkgCache::VerIterator V
= P
.VersionList(); V
.end() == false; ++V
)
1325 VerIdx
[V
->ID
] = V
.Index();
1329 in
.OpenDescriptor(input
, FileFd::ReadOnly
);
1330 pkgTagFile
response(&in
, 100);
1331 pkgTagSection section
;
1333 while (response
.Step(section
) == true) {
1334 char const * type
= nullptr;
1335 if (section
.Exists("Progress") == true) {
1336 if (Progress
!= NULL
) {
1337 string msg
= section
.FindS("Message");
1338 if (msg
.empty() == true)
1339 msg
= _("Prepare for receiving solution");
1340 Progress
->SubProgress(100, msg
, section
.FindI("Percentage", 0));
1343 } else if (section
.Exists("Error") == true) {
1344 std::string msg
= SubstVar(SubstVar(section
.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n");
1345 if (msg
.empty() == true) {
1346 msg
= _("External planner failed without a proper error message");
1347 _error
->Error("%s", msg
.c_str());
1349 _error
->Error("External planner failed with: %s", msg
.substr(0,msg
.find('\n')).c_str());
1350 if (Progress
!= NULL
)
1352 std::cerr
<< "The planner encountered an error of type: " << section
.FindS("Error") << std::endl
;
1353 std::cerr
<< "The following information might help you to understand what is wrong:" << std::endl
;
1354 std::cerr
<< msg
<< std::endl
<< std::endl
;
1356 } else if (section
.Exists("Unpack") == true)
1358 else if (section
.Exists("Configure") == true)
1360 else if (section
.Exists("Remove") == true)
1363 char const *Start
, *End
;
1364 section
.GetSection(Start
, End
);
1365 _error
->Warning("Encountered an unexpected section with %d fields: %s", section
.Count(), std::string(Start
, End
).c_str());
1369 if (type
== nullptr)
1371 size_t const id
= section
.FindULL(type
, VersionCount
);
1372 if (id
== VersionCount
) {
1373 _error
->Warning("Unable to parse %s request with id value '%s'!", type
, section
.FindS(type
).c_str());
1375 } else if (id
> PM
->Cache
.Head().VersionCount
) {
1376 _error
->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section
.FindS(type
).c_str(), type
);
1380 pkgCache::VerIterator
Ver(PM
->Cache
.GetCache(), PM
->Cache
.GetCache().VerP
+ VerIdx
[id
]);
1381 auto const Pkg
= Ver
.ParentPkg();
1382 if (strcmp(type
, "Unpack") == 0)
1383 PM
->Install(Pkg
, PM
->FileNames
[Pkg
->ID
]);
1384 else if (strcmp(type
, "Configure") == 0)
1386 else if (strcmp(type
, "Remove") == 0)
1387 PM
->Remove(Pkg
, PM
->Cache
[Pkg
].Purge());
1389 return in
.Failed() == false;
1392 bool EIPP::ReadRequest(int const input
, std::list
<std::pair
<std::string
,PKG_ACTION
>> &actions
,/*{{{*/
1393 unsigned int &flags
)
1398 while (ReadLine(input
, line
) == true)
1400 // Skip empty lines before request
1401 if (line
.empty() == true)
1403 // The first Tag must be a request, so search for it
1404 if (line
.compare(0, 8, "Request:") != 0)
1407 while (ReadLine(input
, line
) == true)
1409 // empty lines are the end of the request
1410 if (line
.empty() == true)
1413 PKG_ACTION pkgact
= PKG_ACTION::NOOP
;
1414 if (LineStartsWithAndStrip(line
, "Install:"))
1415 pkgact
= PKG_ACTION::INSTALL
;
1416 else if (LineStartsWithAndStrip(line
, "ReInstall:"))
1417 pkgact
= PKG_ACTION::REINSTALL
;
1418 else if (LineStartsWithAndStrip(line
, "Remove:"))
1419 pkgact
= PKG_ACTION::REMOVE
;
1420 else if (LineStartsWithAndStrip(line
, "Architecture:"))
1421 _config
->Set("APT::Architecture", line
);
1422 else if (LineStartsWithAndStrip(line
, "Architectures:"))
1423 _config
->Set("APT::Architectures", SubstVar(line
, " ", ","));
1424 else if (LineStartsWithAndStrip(line
, "Planner:"))
1425 ; // purely informational line
1426 else if (LineStartsWithAndStrip(line
, "Immediate-Configuration:"))
1428 if (localStringToBool(line
, true))
1429 flags
|= Request::IMMEDIATE_CONFIGURATION_ALL
;
1431 flags
|= Request::NO_IMMEDIATE_CONFIGURATION
;
1433 else if (ReadFlag(flags
, line
, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS
))
1436 _error
->Warning("Unknown line in EIPP Request stanza: %s", line
.c_str());
1438 if (pkgact
== PKG_ACTION::NOOP
)
1440 for (auto && p
: VectorizeString(line
, ' '))
1441 actions
.emplace_back(std::move(p
), pkgact
);
1447 bool EIPP::ApplyRequest(std::list
<std::pair
<std::string
,PKG_ACTION
>> &actions
,/*{{{*/
1450 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1453 for (auto Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
1456 if (Pkg
.CurrentVer() == Ver
)
1458 Cache
.SetCandidateVersion(Ver
);
1460 if (unlikely(versions
> 2))
1461 _error
->Warning("Package %s has %d versions, but should have at most 2!", Pkg
.FullName().c_str(), versions
);
1463 for (auto && a
: actions
)
1465 pkgCache::PkgIterator P
= Cache
.FindPkg(a
.first
);
1466 if (P
.end() == true)
1468 _error
->Warning("Package %s is not known, so can't be acted on", a
.first
.c_str());
1473 case PKG_ACTION::NOOP
:
1474 _error
->Warning("Package %s has NOOP as action?!?", a
.first
.c_str());
1476 case PKG_ACTION::INSTALL
:
1477 Cache
.MarkInstall(P
, false);
1479 case PKG_ACTION::REINSTALL
:
1480 Cache
.MarkInstall(P
, false);
1481 Cache
.SetReInstall(P
, true);
1483 case PKG_ACTION::REMOVE
:
1484 Cache
.MarkDelete(P
);