28 #include <unordered_map> 29 #include <sys/utsname.h> 33 #undef ZYPP_BASE_LOGGER_LOGGROUP 34 #define ZYPP_BASE_LOGGER_LOGGROUP "PurgeKernels" 57 using GroupMap = std::unordered_map<std::string, GroupInfo>;
62 struct utsname unameData;
63 if ( uname( &unameData) == 0 ) {
68 setUnameR( std::string( unameData.release ) );
75 MIL <<
"Failed to detect running kernel: " << errno << std::endl;
83 MIL <<
"Set uname " << uname << std::endl;
100 MIL <<
"Parsed info from uname: " << std::endl;
108 void fillKeepList(
const GroupMap &installedKernels, std::set<sat::Solvable::IdType> &keepList , std::set<sat::Solvable::IdType> &removeList )
const;
135 if ( !pool.resolver().resolvePool() ) {
136 MIL <<
"Pool failed to resolve, not doing anything" << std::endl;
140 MIL <<
"Request to remove package: " << pi << std::endl;
143 const str::regex validRemovals(
"(kernel-syms(-.*)?|kgraft-patch(-.*)?|kernel-livepatch(-.*)?|.*-kmp(-.*)?)");
146 MIL <<
"Package " << pi <<
" is locked by the user, not removing." << std::endl;
151 std::set< sat::Solvable::IdType> currentSetOfRemovals;
152 for (
auto it = pool.byStatusBegin( toBeUninstalledFilter ); it != pool.byStatusEnd( toBeUninstalledFilter ); it++ ) {
153 currentSetOfRemovals.insert( it->id() );
158 if ( !pool.resolver().resolvePool() ) {
159 MIL <<
"Failed to resolve pool, skipping " << pi << std::endl;
160 pool.resolver().problems();
166 std::set<sat::Solvable::IdType> removedInThisRun;
167 removedInThisRun.insert( pi.
id() );
169 for (
auto it = pool.byStatusBegin( toBeUninstalledFilter ); it != pool.byStatusEnd( toBeUninstalledFilter ); it++ ) {
172 if ( it->status().isByUser()
173 || (currentSetOfRemovals.find( it->id() ) != currentSetOfRemovals.end())
178 removedInThisRun.insert( it->id() );
180 MIL <<
"Package " <<
PoolItem(*it) <<
" was marked by the solver for removal." << std::endl;
183 if ( removeList.find( it->id() ) != removeList.end() )
186 if ( keepList.find( it->id() ) != keepList.end() ) {
187 MIL <<
"Package " <<
PoolItem(*it) <<
" is in keep spec, skipping" << pi << std::endl;
194 MIL <<
"Package " <<
PoolItem(*it) <<
" should not be removed, skipping " << pi << std::endl;
200 MIL <<
"Successfully marked package: " << pi <<
" for removal."<<std::endl;
203 MIL <<
"Trying to remove debuginfo for: " << pi <<
"."<<std::endl;
204 for (
const auto id : removedInThisRun ) {
207 if ( solvable.arch() == Arch_noarch ||
211 for (
const auto suffix : {
"-debugsource",
"-debuginfo" } ) {
218 for (
const auto debugPackage : q ) {
220 if ( debugPackage.arch() != solvable.arch() )
223 MIL <<
"Found debug package for " << solvable <<
" : " << debugPackage << std::endl;
229 MIL <<
"Finished removing debuginfo for: " << pi <<
"."<<std::endl;
240 const auto dotOffset = b.
release().find_last_of(
".");
241 if ( dotOffset != std::string::npos ) {
255 const unsigned tokenGrp = 1;
256 const unsigned modifierGrp = 2;
259 MIL <<
"Parsing keep spec: " << _keepSpec << std::endl;
261 std::vector<std::string> words;
263 if ( words.empty() ) {
264 WAR <<
"Invalid keep spec: " << _keepSpec <<
" using default latest,running." << std::endl;
268 _keepRunning =
false;
269 _keepLatestOffsets.clear();
270 _keepOldestOffsets.clear();
272 for (
const std::string &word : words ) {
273 if ( word ==
"running" ) {
278 _keepSpecificEditions.insert(
Edition(word) );
282 auto addKeepOff = [](
const auto &off,
auto &
set,
const auto &constraint ){
283 const off_t num = off.empty() ? 0 : str::strtonum<off_t>( off );
284 if ( !constraint(num) )
return false;
285 set.insert( static_cast<size_t>(std::abs(num)) );
289 if ( what[tokenGrp] ==
"oldest" ) {
290 addKeepOff( what[modifierGrp], _keepOldestOffsets, [ &word ]( off_t num ) {
292 WAR <<
"Ignoring invalid modifier in keep spec: " << word <<
", oldest supports only positive modifiers." << std::endl;
298 addKeepOff( what[modifierGrp], _keepLatestOffsets, [ &word ]( off_t num ) {
300 WAR <<
"Ignoring invalid modifier in keep spec: " << word <<
", latest supports only negative modifiers." << std::endl;
322 const auto markAsKeep = [ &keepList, &removeList ](
const auto &pck ) {
323 MIL <<
"Marking package " <<
sat::Solvable(pck) <<
" as to keep." << std::endl;
324 keepList.insert( pck ) ;
325 removeList.erase( pck );
328 const auto versionPredicate = [](
const auto &edition ){
329 return [ &edition ](
const auto &elem ) {
330 return versionMatch( edition, elem.first );
334 for (
const auto &groupInfo : installedKernels ) {
336 MIL <<
"Starting with group " << groupInfo.first << std::endl;
338 for (
const auto &archMap : groupInfo.second.archToEdMap ) {
340 MIL <<
"Starting with arch " << archMap.first << std::endl;
343 size_t currROff = archMap.second.size() - 1;
349 && ( ( archMap.first == _kernelArch && groupInfo.second.groupFlavour == _runningKernelFlavour )
352 MIL <<
"Matching packages against running kernel "<< _runningKernelEdition <<
"-" << _runningKernelFlavour <<
"-" <<_kernelArch << std::endl;
354 auto it = std::find_if( map.begin(), map.end(), versionPredicate( _runningKernelEdition ) );
355 if ( it == map.end() ) {
359 MIL <<
"Running kernel "<< _runningKernelEdition <<
"-" << _runningKernelFlavour <<
"-" <<_kernelArch <<
" not installed."<<std::endl;
360 MIL <<
"NOT removing any packages for flavor "<<_runningKernelFlavour<<
"-"<<_kernelArch<<
" ."<<std::endl;
362 for (
const auto &kernelMap : map ) {
363 for(
const auto &pck : kernelMap.second )
371 MIL <<
"Found possible running candidate edition: " << it->first << std::endl;
373 for ( nit++ ; nit != map.end() && versionMatch( _runningKernelEdition, nit->first ) ; nit++ ) {
374 MIL <<
"Found possible more recent running candidate edition: " << nit->first << std::endl;
380 if ( it != map.end() ) {
381 for(
const auto &pck : it->second ) {
387 for (
const auto &kernelMap : map ) {
389 if ( _keepOldestOffsets.find( currOff ) != _keepOldestOffsets.end()
390 || _keepLatestOffsets.find( currROff ) != _keepLatestOffsets.end()
393 || std::find_if( _keepSpecificEditions.begin(), _keepSpecificEditions.end(),
394 [ edition = &kernelMap.first ](
const auto &elem ) {
return versionMatch( *edition, elem ); } ) != _keepSpecificEditions.end() ) {
396 for(
const auto &pck : kernelMap.second ) {
415 MIL << std::endl <<
"--------------------- Starting to mark obsolete kernels ---------------------"<<std::endl;
418 WAR <<
"Keep spec is empty, removing nothing." << std::endl;
425 WAR <<
"Unable to detect running kernel, but keeping the running kernel was requested. Not removing any packages." << std::endl;
430 pool.resolver().setForceResolve(
true );
435 const str::regex kernelFlavourRegex(
"^kernel-(.*)$");
443 std::set<sat::Solvable::IdType> packagesToRemove;
445 const auto addPackageToMap = [&installedKrnlPackages, &packagesToRemove] (
const GroupInfo::GroupType type,
const std::string &ident,
const std::string &flavour,
const auto &installedKrnlPck ) {
447 if ( !installedKrnlPackages.count( ident ) )
448 installedKrnlPackages.insert( std::make_pair( ident,
GroupInfo(type, flavour) ) );
450 auto &groupInfo = installedKrnlPackages[ ident ];
451 if ( groupInfo.groupType != type || groupInfo.groupFlavour != flavour ) {
452 ERR <<
"Got inconsistent type and flavour for ident this is a BUG: " << ident << std::endl
453 <<
"Original Flavour-Type: "<<groupInfo.groupFlavour<<
"-"<<groupInfo.groupType << std::endl
454 <<
"Competing Flavour-Type: "<< flavour <<
"-" << type << std::endl;
457 const auto currArch = installedKrnlPck.arch();
458 if ( !groupInfo.archToEdMap.count( currArch ) )
461 auto &editionToSolvableMap = groupInfo.archToEdMap[ currArch ];
463 const auto currEd = installedKrnlPck.edition();
464 if ( !editionToSolvableMap.count( currEd ) )
465 editionToSolvableMap.insert( std::make_pair( currEd,
SolvableList{} ) );
467 editionToSolvableMap[currEd].push_back( installedKrnlPck.id() );
470 packagesToRemove.insert( installedKrnlPck.id() );
474 std::set<sat::Solvable::IdType> packagesToKeep;
483 MIL <<
"Searching for obsolete multiversion kernel packages." << std::endl;
485 for (
auto installedKrnlPck : q ) {
487 MIL <<
"Found installed multiversion kernel package " << installedKrnlPck << std::endl;
489 if ( installedKrnlPck.provides().matches(
Capability(
"kernel-uname-r")) ) {
490 MIL <<
"Identified as a kernel package " << std::endl;
495 if ( what[1].empty() ) {
496 WAR <<
"Could not detect flavour for: " << installedKrnlPck <<
" ...skipping" << std::endl;
500 std::string flavour = what[1];
503 const auto dash = flavour.find_first_of(
'-');
504 if ( dash != std::string::npos ) {
505 flavour = flavour.substr( 0, dash );
513 const str::regex explicitelyHandled(
"kernel-syms(-.*)?|kernel(-.*)?-devel");
515 MIL <<
"Not a kernel package, inspecting more closely " << std::endl;
518 if ( installedKrnlPck.arch() == Arch_noarch ) {
520 MIL <<
"Handling package explicitely due to architecture (noarch)."<< std::endl;
530 if ( match.size() > 1 ) {
531 flav = match[2].substr(1);
532 }
else if ( installedKrnlPck.name() ==
"kernel-syms" ) {
536 MIL <<
"Handling package explicitely due to name match."<< std::endl;
540 MIL <<
"Package not explicitely handled" << std::endl;
546 MIL <<
"Grouped packages: " << std::endl;
547 std::for_each( installedKrnlPackages.begin(), installedKrnlPackages.end(),[](
const auto &ident ){
548 MIL <<
"\tGroup ident: "<<ident.first<<std::endl;
549 MIL <<
"\t Group type: "<<ident.second.groupType<<std::endl;
550 MIL <<
"\t Group flav: "<<ident.second.groupFlavour<<std::endl;
551 std::for_each( ident.second.archToEdMap.begin(), ident.second.archToEdMap.end(), [](
const auto &arch) {
552 MIL <<
"\t\tArch: "<<arch.first<<std::endl;
553 std::for_each( arch.second.begin(), arch.second.end(), [](
const auto &edition) {
554 MIL <<
"\t\t\tEdition: "<<edition.first<<std::endl;
555 std::for_each( edition.second.begin(), edition.second.end(), [](
const auto &packageId) {
564 for (
const auto id : packagesToRemove )
Flavour _runningKernelFlavour
ArchToEditionMap archToEdMap
A Solvable object within the sat Pool.
std::string regex_substitute(const std::string &s, const regex ®ex, const std::string &replacement, bool global=true)
Replaces the matched regex with the string passed in replacement.
void addAttribute(const sat::SolvAttr &attr, const std::string &value="")
Filter by the value of the specified attr attribute.
static const ResKind package
void markObsoleteKernels()
static ZConfig & instance()
Singleton ctor.
std::map< Edition, SolvableList > EditionToSolvableMap
std::set< size_t > _keepOldestOffsets
Filter solvables according to their status.
Edition _runningKernelEdition
std::map< Arch, EditionToSolvableMap > ArchToEditionMap
ResStatus & status() const
Returns the current status.
const Arch Arch_empty(IdString::Empty)
std::string unameR() const
void addDependency(const sat::SolvAttr &attr, const std::string &name, const Rel &op, const Edition &edition)
Query "name|global op edition".
void addKind(const ResKind &kind)
Filter by selectable kind.
Edition represents [epoch:]version[-release]
RW_pointer< Impl > _pimpl
std::set< Edition > _keepSpecificEditions
void setUnameR(const std::string &val)
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \, const Trim trim_r=NO_TRIM)
Split line_r into words.
std::string version() const
Version.
ResStatus & statusReset() const
Reset status.
static bool versionMatch(const Edition &a, const Edition &b)
void setKernelArch(const zypp::Arch &arch)
std::set< size_t > _keepLatestOffsets
std::string release() const
Release.
Solvable::IdType id() const
void setUnameR(const std::string &uname)
std::string keepSpec() const
bool removePackageAndCheck(const sat::Solvable::IdType id, const std::set< sat::Solvable::IdType > &keepList, const std::set< sat::Solvable::IdType > &removeList) const
void setInstalledOnly()
Return only repo packages.
bool setToBeUninstalled(TransactByValue causer)
std::unordered_map< std::string, GroupInfo > GroupMap
void fillKeepList(const GroupMap &installedKernels, std::set< sat::Solvable::IdType > &keepList, std::set< sat::Solvable::IdType > &removeList) const
void setKeepSpec(const std::string &val)
Regular expression match result.
Use POSIX Extended Regular Expression syntax when interpreting regex.
bool isToBeUninstalled() const
void setMatchExact()
Set to match exact string instead of substring.
GroupInfo(const GroupType type=None, std::string flav="")
static const SolvAttr provides
epoch_t epoch() const
Epoch.
std::string multiversionKernels() const
enum zypp::GroupInfo::GroupType groupType
bool regex_match(const std::string &s, smatch &matches, const regex ®ex)
regex ZYPP_STR_REGEX regex ZYPP_STR_REGEX
Combining sat::Solvable and ResStatus.
sat::detail::SolvableIdType IdType
Easy-to use interface to the ZYPP dependency resolver.
Solvable to Selectable transform functor.
std::list< sat::Solvable::IdType > SolvableList
static ResPool instance()
Singleton ctor.