13 #include <zypp/base/String.h> 14 #include <zypp/base/Logger.h> 15 #include <zypp/base/Regex.h> 16 #include <zypp/base/Iterator.h> 20 #include <zypp/Resolver.h> 27 #include <unordered_map> 28 #include <sys/utsname.h> 32 #undef ZYPP_BASE_LOGGER_LOGGROUP 33 #define ZYPP_BASE_LOGGER_LOGGROUP "PurgeKernels" 56 using GroupMap = std::unordered_map<std::string, GroupInfo>;
61 struct utsname unameData;
62 if ( uname( &unameData) == 0 ) {
67 setUnameR( std::string( unameData.release ) );
73 MIL <<
"Edition variant: " << edVar <<
"\n";
77 MIL <<
"Failed to detect running kernel: " << errno << std::endl;
85 MIL <<
"Set uname " << uname << std::endl;
95 auto makeRcVariant = [ &release ]( std::string myVersion,
const std::string &replace ){
100 return Edition( myVersion, release );
109 MIL <<
"Parsed info from uname: " << std::endl;
111 MIL <<
"Kernel Edition Variants: \n";
113 MIL <<
" " << var <<
"\n";
120 void fillKeepList(
const GroupMap &installedKernels, std::set<sat::Solvable> &keepList , std::set<sat::Solvable> &removeList )
const;
152 if ( !pool.resolver().resolvePool() ) {
153 MIL <<
"Pool failed to resolve, not doing anything" << std::endl;
157 MIL <<
"Request to remove package: " << pi << std::endl;
160 const str::regex validRemovals(
"(kernel-syms(-.*)?|kgraft-patch(-.*)?|kernel-(.*)-livepatch(-.*)?|kernel-livepatch(-.*)?|.*-kmp(-.*)?)");
163 MIL <<
"Package " << pi <<
" is locked by the user, not removing." << std::endl;
168 std::set<sat::Solvable> currentSetOfRemovals;
169 for (
const PoolItem & p : pool.byStatus( toBeUninstalledFilter ) ) {
170 currentSetOfRemovals.insert( p.satSolvable() );
175 if ( !pool.resolver().resolvePool() ) {
176 MIL <<
"Failed to resolve pool, skipping " << pi << std::endl;
177 pool.resolver().problems();
183 std::set<sat::Solvable> removedInThisRun;
184 removedInThisRun.insert( slv );
186 for (
const PoolItem & p : pool.byStatus( toBeUninstalledFilter ) ) {
189 if ( p.status().isByUser()
190 || (currentSetOfRemovals.find( p.satSolvable() ) != currentSetOfRemovals.end())
195 removedInThisRun.insert( p.satSolvable() );
197 MIL <<
"Package " << p <<
" was marked by the solver for removal." << std::endl;
200 if ( removeList.find( p.satSolvable() ) != removeList.end() )
203 if ( keepList.find( p.satSolvable() ) != keepList.end() ) {
204 MIL <<
"Package " << p <<
" is in keep spec, skipping" << pi << std::endl;
219 bool mostLikelyKmod =
false;
222 for (
const auto &prov : p.provides() ) {
223 if ( matchMod.
doMatch( prov.detail().name().c_str()) || matchSym.
doMatch( prov.detail().name().c_str() ) ) {
224 mostLikelyKmod =
true;
229 if ( mostLikelyKmod ) {
230 MIL <<
"Package " << p <<
" is most likely a kmod " << std::endl;
234 MIL <<
"Package " << p <<
" should not be removed, skipping " << pi << std::endl;
241 MIL <<
"Successfully marked package: " << pi <<
" for removal."<<std::endl;
244 MIL <<
"Trying to remove debuginfo for: " << pi <<
"."<<std::endl;
247 if ( solvable.arch() == Arch_noarch ||
251 for (
const char * suffix : {
"-debugsource",
"-debuginfo" } ) {
260 if ( debugPackage.arch() != solvable.arch() )
263 MIL <<
"Found debug package for " << solvable <<
" : " << debugPackage << std::endl;
269 MIL <<
"Finished removing debuginfo for: " << pi <<
"."<<std::endl;
285 std::string versionStr = b.
asString();
287 if ( buildCntRegex.
matches( versionStr.data(), matches ) ) {
288 if ( matches.
size() >= 2 ) {
289 versionStr.replace( matches.
begin(0), (matches.
end(0) - matches.
begin(0))+1, matches[1] );
290 return a ==
Edition(versionStr);
304 const unsigned tokenGrp = 1;
305 const unsigned modifierGrp = 2;
308 MIL <<
"Parsing keep spec: " << _keepSpec << std::endl;
310 std::vector<std::string> words;
312 if ( words.empty() ) {
313 WAR <<
"Invalid keep spec: " << _keepSpec <<
" using default latest,running." << std::endl;
317 _keepRunning =
false;
318 _keepLatestOffsets.clear();
319 _keepOldestOffsets.clear();
321 for (
const std::string &word : words ) {
322 if ( word ==
"running" ) {
327 _keepSpecificEditions.insert(
Edition(word) );
331 auto addKeepOff = [](
const auto &off,
auto &
set,
const auto &constraint ){
332 const off_t num = off.empty() ? 0 : str::strtonum<off_t>( off );
333 if ( !constraint(num) )
return false;
334 set.insert( static_cast<size_t>(std::abs(num)) );
338 if ( what[tokenGrp] ==
"oldest" ) {
339 addKeepOff( what[modifierGrp], _keepOldestOffsets, [ &word ]( off_t num ) {
341 WAR <<
"Ignoring invalid modifier in keep spec: " << word <<
", oldest supports only positive modifiers." << std::endl;
347 addKeepOff( what[modifierGrp], _keepLatestOffsets, [ &word ]( off_t num ) {
349 WAR <<
"Ignoring invalid modifier in keep spec: " << word <<
", latest supports only negative modifiers." << std::endl;
371 const auto markAsKeep = [ &keepList, &removeList ](
sat::Solvable pck ) {
372 MIL <<
"Marking package " << pck <<
" as to keep." << std::endl;
373 keepList.insert( pck ) ;
374 removeList.erase( pck );
377 const auto versionPredicate = [](
const auto &editionVariants ){
378 return [ &editionVariants ](
const auto &elem ) {
379 const auto &f = std::bind( versionMatch, std::placeholders::_1, elem.first );
380 return std::any_of( editionVariants.begin(), editionVariants.end(), f );
384 for (
const auto &groupInfo : installedKernels ) {
386 MIL <<
"Starting with group " << groupInfo.first << std::endl;
388 for (
const auto &archMap : groupInfo.second.archToEdMap ) {
390 MIL <<
"Starting with arch " << archMap.first << std::endl;
393 size_t currROff = archMap.second.size() - 1;
399 && ( ( archMap.first == _kernelArch && groupInfo.second.groupFlavour == _runningKernelFlavour )
402 MIL <<
"Matching packages against running kernel "<< _runningKernelFlavour <<
"-" <<_kernelArch <<
"\nVariants:\n";
403 for (
const auto &var : _runningKernelEditionVariants )
407 const auto &editionPredicate = versionPredicate( _runningKernelEditionVariants );
408 auto it = std::find_if( map.begin(), map.end(), editionPredicate );
409 if ( it == map.end() ) {
413 MIL <<
"Running kernel " << _runningKernelFlavour <<
"-" <<_kernelArch <<
"\n";
414 for (
const auto &var : _runningKernelEditionVariants )
415 MIL <<
" Possible Variant:" << var <<
"\n";
416 MIL <<
"Not installed! \n";
417 MIL <<
"NOT removing any packages for flavor "<<_runningKernelFlavour<<
"-"<<_kernelArch<<
" ."<<std::endl;
419 for (
const auto &kernelMap : map ) {
428 MIL <<
"Found possible running candidate edition: " << it->first << std::endl;
430 for ( nit++ ; nit != map.end() && editionPredicate( *nit ) ; nit++ ) {
431 MIL <<
"Found possible more recent running candidate edition: " << nit->first << std::endl;
437 if ( it != map.end() ) {
444 for (
const auto &kernelMap : map ) {
446 if ( _keepOldestOffsets.find( currOff ) != _keepOldestOffsets.end() || _keepLatestOffsets.find( currROff ) != _keepLatestOffsets.end() ) {
447 std::for_each( kernelMap.second.begin(), kernelMap.second.end(), markAsKeep );
455 std::for_each( kernelMap.second.begin(), kernelMap.second.end(), [ & ](
sat::Solvable solv ){
457 if ( prov.detail().name() == solv.name() && _keepSpecificEditions.count( prov.detail().ed() ) ) {
475 MIL << std::endl <<
"--------------------- Starting to mark obsolete kernels ---------------------"<<std::endl;
478 WAR <<
"Keep spec is empty, removing nothing." << std::endl;
485 WAR <<
"Unable to detect running kernel, but keeping the running kernel was requested. Not removing any packages." << std::endl;
490 pool.resolver().setForceResolve(
true );
495 const str::regex kernelFlavourRegex(
"^kernel-(.*)$");
503 std::set<sat::Solvable> packagesToRemove;
505 const auto addPackageToMap = [&installedKrnlPackages, &packagesToRemove] (
const GroupInfo::GroupType type,
const std::string &ident,
const std::string &flavour,
const sat::Solvable &installedKrnlPck ) {
507 if ( !installedKrnlPackages.count( ident ) )
508 installedKrnlPackages.insert( std::make_pair( ident,
GroupInfo(type, flavour) ) );
510 auto &groupInfo = installedKrnlPackages[ ident ];
511 if ( groupInfo.groupType != type || groupInfo.groupFlavour != flavour ) {
512 ERR <<
"Got inconsistent type and flavour for ident this is a BUG: " << ident << std::endl
513 <<
"Original Flavour-Type: "<<groupInfo.groupFlavour<<
"-"<<groupInfo.groupType << std::endl
514 <<
"Competing Flavour-Type: "<< flavour <<
"-" << type << std::endl;
517 const auto currArch = installedKrnlPck.arch();
518 if ( !groupInfo.archToEdMap.count( currArch ) )
521 auto &editionToSolvableMap = groupInfo.archToEdMap[ currArch ];
527 auto currCount = INT_MAX;
529 for (
Capability prov : installedKrnlPck.provides() ) {
530 if ( prov.detail().name() == installedKrnlPck.name() ) {
532 edToUse = installedKrnlPck.edition();
533 const auto &relStr = edToUse.
release();
534 currCount = std::count( relStr.begin(), relStr.end(),
'.');
536 const auto &pckEd = prov.detail().ed();
537 const auto &relStr = pckEd.release();
538 if (
const auto pntCnt = std::count( relStr.begin(), relStr.end(),
'.'); pntCnt < currCount ) {
546 if ( !editionToSolvableMap.count( edToUse ) )
547 editionToSolvableMap.insert( std::make_pair( edToUse,
SolvableList{} ) );
549 editionToSolvableMap[edToUse].push_back( installedKrnlPck );
552 packagesToRemove.insert( installedKrnlPck );
556 std::set<sat::Solvable> packagesToKeep;
565 MIL <<
"Searching for obsolete multiversion kernel packages." << std::endl;
569 MIL <<
"Found installed multiversion kernel package " << installedKrnlPck << std::endl;
571 if ( installedKrnlPck.provides().matches(
Capability(
"kernel-uname-r")) ) {
572 MIL <<
"Identified as a kernel package " << std::endl;
577 if ( what[1].empty() ) {
578 WAR <<
"Could not detect flavour for: " << installedKrnlPck <<
" ...skipping" << std::endl;
582 std::string flavour = what[1];
585 const auto dash = flavour.find_first_of(
'-');
586 if ( dash != std::string::npos ) {
587 flavour = flavour.substr( 0, dash );
596 const str::regex explicitelyHandled(
"kernel-syms(-.*)?|kernel(-.*)?-devel");
598 MIL <<
"Not a kernel package, inspecting more closely " << std::endl;
601 if ( installedKrnlPck.arch() == Arch_noarch ) {
603 MIL <<
"Handling package explicitely due to architecture (noarch)."<< std::endl;
615 if ( match.size() > 1 && match[1].size() )
616 flav = match[1].substr(1);
618 else if ( match.size() > 2 && match[2].size() )
619 flav = match[2].substr(1);
620 else if ( installedKrnlPck.name() ==
"kernel-syms" )
623 MIL <<
"Handling package explicitely due to name match."<< std::endl;
626 MIL <<
"Package not explicitely handled" << std::endl;
632 MIL <<
"Grouped packages: " << std::endl;
633 std::for_each( installedKrnlPackages.begin(), installedKrnlPackages.end(),[](
const auto &ident ){
634 MIL <<
"\tGroup ident: "<<ident.first<<std::endl;
635 MIL <<
"\t Group type: "<<ident.second.groupType<<std::endl;
636 MIL <<
"\t Group flav: "<<ident.second.groupFlavour<<std::endl;
637 std::for_each( ident.second.archToEdMap.begin(), ident.second.archToEdMap.end(), [](
const auto &arch) {
638 MIL <<
"\t\tArch: "<<arch.first<<std::endl;
639 std::for_each( arch.second.begin(), arch.second.end(), [](
const auto &edition) {
640 MIL <<
"\t\t\tEdition: "<<edition.first<<std::endl;
641 std::for_each( edition.second.begin(), edition.second.end(), [](
const auto &packageId) {
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
void fillKeepList(const GroupMap &installedKernels, std::set< sat::Solvable > &keepList, std::set< sat::Solvable > &removeList) const
std::set< size_t > _keepOldestOffsets
Filter solvables according to their status.
String matching (STRING|SUBSTRING|GLOB|REGEX).
std::map< Arch, EditionToSolvableMap > ArchToEditionMap
ResStatus & status() const
Returns the current status.
const Arch Arch_empty(IdString::Empty)
std::string unameR() const
bool doMatch(const char *string_r) const
Return whether string matches.
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.
std::string::size_type end(unsigned i) const
End index of subexpression i in match_str (or std::string::npos)
Edition represents [epoch:]version[-release]
std::string::size_type begin(unsigned i) const
Begin index of subexpression i in match_str (or std::string::npos)
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 asString() const
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.
bool removePackageAndCheck(const sat::Solvable slv, const std::set< sat::Solvable > &keepList, const std::set< sat::Solvable > &removeList) const
void setUnameR(const std::string &uname)
std::string keepSpec() const
bool matches(const char *s, str::smatch &matches, int flags=none) const
void setInstalledOnly()
Return only repo packages.
bool setToBeUninstalled(TransactByValue causer)
std::unordered_map< std::string, GroupInfo > GroupMap
void setKeepSpec(const std::string &val)
Regular expression match result.
std::list< sat::Solvable > SolvableList
std::set< Edition > _runningKernelEditionVariants
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
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.
Easy-to use interface to the ZYPP dependency resolver.
static const Edition noedition
Value representing noedition ("") This is in fact a valid Edition.
static ResPool instance()
Singleton ctor.