27#include <unordered_map> 
   28#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 ) );
 
   74          MIL << 
"Edition variant: " << edVar << 
"\n";
 
   78        MIL << 
"Failed to detect running kernel: " << errno << std::endl;
 
 
   86      MIL << 
"Set uname " << uname << std::endl;
 
   96      auto makeRcVariant = [ &release ]( std::string myVersion, 
const std::string &replace ){
 
  101        return Edition( myVersion, release );
 
  110      MIL << 
"Parsed info from uname: " << std::endl;
 
  112      MIL << 
"Kernel Edition Variants: \n";
 
  114        MIL << 
"    " << var << 
"\n";
 
 
  121    void fillKeepList(
const GroupMap &installedKernels, std::set<sat::Solvable> &keepList , std::set<sat::Solvable> &removeList ) 
const;
 
 
  153    if ( !
pool.resolver().resolvePool() ) {
 
  154      MIL << 
"Pool failed to resolve, not doing anything" << std::endl;
 
  158    MIL << 
"Request to remove package: " << pi << std::endl;
 
  161    const str::regex validRemovals(
"(kernel-syms(-.*)?|kgraft-patch(-.*)?|kernel-(.*)-livepatch(-.*)?|kernel-livepatch(-.*)?|.*-kmp(-.*)?)");
 
  164      MIL << 
"Package " << pi << 
" is locked by the user, not removing." << std::endl;
 
  169    std::set<sat::Solvable> currentSetOfRemovals;
 
  170    for ( 
const PoolItem & p : 
pool.byStatus( toBeUninstalledFilter ) ) {
 
  171      currentSetOfRemovals.insert( p.satSolvable() );
 
  176    if ( !
pool.resolver().resolvePool() ) {
 
  177      MIL << 
"Failed to resolve pool, skipping " << pi << std::endl;
 
  178      pool.resolver().problems();
 
  184    std::set<sat::Solvable> removedInThisRun;
 
  185    removedInThisRun.insert( slv );
 
  187    for ( 
const PoolItem & p : 
pool.byStatus( toBeUninstalledFilter ) ) {
 
  190      if ( p.status().isByUser()      
 
  191           || (currentSetOfRemovals.find( p.satSolvable() ) != currentSetOfRemovals.end()) 
 
  196      removedInThisRun.insert( p.satSolvable() );
 
  198      MIL << 
"Package " << p << 
" was marked by the solver for removal." << std::endl;
 
  201      if ( removeList.find( p.satSolvable() ) != removeList.end() )
 
  204      if ( keepList.find( p.satSolvable() ) != keepList.end() ) {
 
  205        MIL << 
"Package " << p << 
" is in keep spec, skipping" << pi << std::endl;
 
  220      bool mostLikelyKmod = 
false;
 
  223      for ( 
const auto &prov : p.dep_provides() ) {
 
  224        if ( matchMod.
doMatch( prov.detail().name().c_str()) || matchSym.
doMatch( prov.detail().name().c_str() ) ) {
 
  225          mostLikelyKmod = 
true;
 
  230      if ( mostLikelyKmod  ) {
 
  231        MIL << 
"Package " << p << 
" is most likely a kmod " << std::endl;
 
  235          MIL << 
"Package " << p << 
" should not be removed, skipping " << pi << std::endl;
 
  242    MIL << 
"Successfully marked package: " << pi << 
" for removal."<<std::endl;
 
  245    MIL << 
"Trying to remove debuginfo for: " << pi <<
"."<<std::endl;
 
  248      if ( solvable.arch() == Arch_noarch ||
 
  252      for ( 
const char * suffix : { 
"-debugsource", 
"-debuginfo" } ) {
 
  261          if ( debugPackage.arch() != solvable.arch() )
 
  264          MIL << 
"Found debug package for " << solvable << 
" : " << debugPackage << std::endl;
 
  270    MIL << 
"Finished removing debuginfo for: " << pi <<
"."<<std::endl;
 
 
  286    std::string versionStr = 
b.asString();
 
  288    if ( buildCntRegex.
matches( versionStr.data(), matches ) ) {
 
  289      if ( matches.
size() >= 2 ) {
 
  290        versionStr.replace( matches.
begin(0), (matches.
end(0) - matches.
begin(0))+1, matches[1] );
 
 
  305    const unsigned tokenGrp = 1; 
 
  306    const unsigned modifierGrp = 2; 
 
  311    std::vector<std::string> words;
 
  313    if ( words.empty() ) {
 
  314      WAR << 
"Invalid keep spec: " << 
_keepSpec << 
" using default latest,running." << std::endl;
 
  322    for ( 
const std::string &word : words ) {
 
  323      if ( word == 
"running" ) {
 
  330          std::string_view edition(word);
 
  331          const auto firstDash = word.find_first_of (
'-');
 
  332          if ( firstDash != std::string::npos ) {
 
  333            const auto secondDash = word.find_first_of (
'-', firstDash+1 );
 
  334            if ( secondDash != std::string::npos ) {
 
  335              WAR << 
"Ignoring possible flavor postfix:'"<< word.substr (secondDash) <<
"' in keep spec: " << word << std::endl;
 
  336              edition = std::string_view( word.c_str (), secondDash );
 
  343        auto addKeepOff = []( 
const auto &off, 
auto &set, 
const auto &constraint ){
 
  345          if ( !constraint(num) ) 
return false;
 
  346          set.insert( 
static_cast<size_t>(std::abs(num)) );
 
  350        if ( what[tokenGrp] == 
"oldest" ) {
 
  353              WAR << 
"Ignoring invalid modifier in keep spec: " << word << 
", oldest supports only positive modifiers." << std::endl;
 
  361              WAR << 
"Ignoring invalid modifier in keep spec: " << word << 
", latest supports only negative modifiers." << std::endl;
 
 
  383    const auto markAsKeep = [ &keepList, &removeList ]( 
sat::Solvable pck ) {
 
  384      MIL << 
"Marking package " << pck << 
" as to keep." << std::endl;
 
  385      keepList.insert( pck ) ;
 
  386      removeList.erase( pck );
 
  389    const auto versionPredicate = []( 
const auto &editionVariants ){
 
  390      return [ &editionVariants ]( 
const auto &elem ) {
 
  391        const auto &f = std::bind( 
versionMatch, _1, elem.first );
 
  392        return std::any_of( editionVariants.begin(), editionVariants.end(), f );
 
  396    for ( 
const auto &groupInfo : installedKernels ) {
 
  398      MIL << 
"Starting with group " << groupInfo.first << std::endl;
 
  400      for ( 
const auto &archMap : groupInfo.second.archToEdMap ) {
 
  402        MIL << 
"Starting with arch " << archMap.first << std::endl;
 
  405        size_t currROff = archMap.second.size() - 1; 
 
  420          auto it = std::find_if( map.begin(), map.end(), editionPredicate );
 
  421          if ( it == map.end() ) {
 
  427                MIL << 
" Possible Variant:" << var << 
"\n";
 
  428              MIL << 
"Not installed! \n";
 
  431              for ( 
const auto &kernelMap : map ) {
 
  440            MIL << 
"Found possible running candidate edition: " << it->first << std::endl;
 
  442            for ( nit++ ; nit != map.end() && editionPredicate( *nit ) ; nit++ ) {
 
  443              MIL << 
"Found possible more recent running candidate edition: " << nit->first << std::endl;
 
  449          if ( it != map.end() ) {
 
  456        for ( 
const auto &kernelMap : map ) {
 
  459            std::for_each( kernelMap.second.begin(), kernelMap.second.end(), markAsKeep );
 
  467          std::for_each( kernelMap.second.begin(), kernelMap.second.end(), [ & ]( 
sat::Solvable solv ){
 
  468            for ( Capability prov : solv.dep_provides() ) {
 
  469              if ( prov.detail().name() == solv.name() && _keepSpecificEditions.count( prov.detail().ed() ) ) {
 
 
  488    MIL << std::endl << 
"--------------------- Starting to mark obsolete kernels ---------------------"<<std::endl;
 
  490    if ( 
_pimpl->_keepSpec.empty() ) {
 
  491      WAR << 
"Keep spec is empty, removing nothing." << std::endl;
 
  497    if ( 
_pimpl->_keepRunning && !
_pimpl->_detectedRunning ) {
 
  498      WAR << 
"Unable to detect running kernel, but keeping the running kernel was requested. Not removing any packages." << std::endl;
 
  503    pool.resolver().setForceResolve( 
true ); 
 
  508    const str::regex kernelFlavourRegex(
"^kernel-(.*)$");
 
  516    std::set<sat::Solvable> packagesToRemove;
 
  518    const auto addPackageToMap = [&installedKrnlPackages, &packagesToRemove] ( 
const GroupInfo::GroupType type, 
const std::string &ident, 
const std::string &flavour, 
const sat::Solvable &installedKrnlPck ) {
 
  520      if ( !installedKrnlPackages.count( ident ) )
 
  521        installedKrnlPackages.insert( std::make_pair( ident, 
GroupInfo(type, flavour) ) );
 
  523      auto &groupInfo = installedKrnlPackages[ ident ];
 
  524      if ( groupInfo.groupType != type || groupInfo.groupFlavour != flavour ) {
 
  525        ERR << 
"Got inconsistent type and flavour for ident this is a BUG: " <<  ident << std::endl
 
  526            << 
"Original Flavour-Type:  "<<groupInfo.groupFlavour<<
"-"<<groupInfo.groupType << std::endl
 
  527            << 
"Competing Flavour-Type: "<< flavour << 
"-" << type << std::endl;
 
  530      const auto currArch = installedKrnlPck.arch();
 
  531      if ( !groupInfo.archToEdMap.count( currArch ) )
 
  534      auto &editionToSolvableMap = groupInfo.archToEdMap[ currArch ];
 
  540      auto currCount = INT_MAX;
 
  542      for ( 
Capability prov : installedKrnlPck.dep_provides() ) {
 
  543        if ( prov.detail().name() == installedKrnlPck.name() ) {
 
  545            edToUse = installedKrnlPck.edition();
 
  546            const auto &relStr = edToUse.
release();
 
  547            currCount = std::count( relStr.begin(), relStr.end(), 
'.');
 
  549            const auto &pckEd = prov.detail().ed();
 
  550            const auto &relStr = pckEd.release();
 
  551            if ( 
const auto pntCnt = std::count( relStr.begin(), relStr.end(), 
'.'); pntCnt < currCount ) {
 
  559      if ( !editionToSolvableMap.count( edToUse ) )
 
  560        editionToSolvableMap.insert( std::make_pair( edToUse, 
SolvableList{} ) );
 
  562      editionToSolvableMap[edToUse].push_back( installedKrnlPck );
 
  565      packagesToRemove.insert( installedKrnlPck );
 
  569    std::set<sat::Solvable> packagesToKeep;
 
  578    MIL << 
"Searching for obsolete multiversion kernel packages." << std::endl;
 
  582      MIL << 
"Found installed multiversion kernel package " << installedKrnlPck << std::endl;
 
  584      if ( installedKrnlPck.dep_provides().matches(
Capability(
"kernel-uname-r")) ) {
 
  585        MIL << 
"Identified as a kernel package " << std::endl;
 
  590        if ( what[1].empty() ) {
 
  591          WAR << 
"Could not detect flavour for: " << installedKrnlPck << 
" ...skipping" << std::endl;
 
  595        std::string flavour = what[1];
 
  598        const auto dash = flavour.find_first_of(
'-');
 
  599        if ( dash != std::string::npos ) {
 
  600          flavour = flavour.substr( 0, dash );
 
  609        const str::regex explicitlyHandled(
"kernel-syms(-.*)?|kernel(-.*)?-devel");
 
  611        MIL << 
"Not a kernel package, inspecting more closely " << std::endl;
 
  614        if ( installedKrnlPck.arch() == Arch_noarch ) {
 
  616          MIL << 
"Handling package explicitly due to architecture (noarch)."<< std::endl;
 
  628          if ( match.size() > 1 && match[1].size() )
 
  629            flav = match[1].substr(1);
 
  631          else if ( match.size() > 2 &&  match[2].size() )
 
  632            flav = match[2].substr(1);
 
  633          else if ( installedKrnlPck.name() == 
"kernel-syms" )
 
  636          MIL << 
"Handling package explicitly due to name match."<< std::endl;
 
  639          MIL << 
"Package not explicitly handled" << std::endl;
 
  645    MIL << 
"Grouped packages: " << std::endl;
 
  646    std::for_each( installedKrnlPackages.begin(), installedKrnlPackages.end(),[]( 
const auto &ident ){
 
  647      MIL << 
"\tGroup ident: "<<ident.first<<std::endl;
 
  648      MIL << 
"\t Group type: "<<ident.second.groupType<<std::endl;
 
  649      MIL << 
"\t Group flav: "<<ident.second.groupFlavour<<std::endl;
 
  650      std::for_each( ident.second.archToEdMap.begin(), ident.second.archToEdMap.end(), []( const auto &arch) {
 
  651        MIL << 
"\t\tArch: "<<arch.first<<std::endl;
 
  652        std::for_each( arch.second.begin(), arch.second.end(), []( const auto &edition) {
 
  653          MIL << 
"\t\t\tEdition: "<<edition.first<<std::endl;
 
  654          std::for_each( edition.second.begin(), edition.second.end(), []( const auto &packageId) {
 
  655            MIL << 
"\t\t\t\t "<<sat::Solvable(packageId)<<std::endl;
 
  661    _pimpl->fillKeepList( installedKrnlPackages, packagesToKeep, packagesToRemove );
 
  664      _pimpl->removePackageAndCheck( slv, packagesToKeep, packagesToRemove );
 
 
  679    _pimpl->_kernelArch = arch;
 
 
  684    return _pimpl->_kernelArch;
 
 
Edition represents [epoch:]version[-release]
std::string release() const
Release.
static const Edition noedition
Value representing noedition ("") This is in fact a valid Edition.
Access to the sat-pools string space.
Combining sat::Solvable and ResStatus.
ResStatus & status() const
Returns the current status.
ResStatus & statusReset() const
Resets status to the default state (KEEP_STATE bySOLVER; clears any lock!).
void setMatchExact()
Set to match exact string instead of substring.
void addKind(const ResKind &kind)
Filter by selectable kind.
void addAttribute(const sat::SolvAttr &attr, const std::string &value="")
Filter by the value of the specified attr attribute.
void setInstalledOnly()
Return only @System repo packages.
void addDependency(const sat::SolvAttr &attr, const std::string &name, const Rel &op, const Edition &edition)
Query "name|global op edition".
std::string unameR() const
void setKeepSpec(const std::string &val)
std::string keepSpec() const
void setUnameR(const std::string &val)
RW_pointer< Impl > _pimpl
void setKernelArch(const zypp::Arch &arch)
void markObsoleteKernels()
static const ResKind package
static ResPool instance()
Singleton ctor.
bool setToBeUninstalled(TransactByValue causer)
bool isToBeUninstalled() const
String matching (STRING|SUBSTRING|GLOB|REGEX).
bool doMatch(const char *string_r) const
Return whether string matches.
static ZConfig & instance()
Singleton ctor.
std::string multiversionKernels() const
Filter solvables according to their status.
static const SolvAttr dep_provides
A Solvable object within the sat Pool.
@ match_extended
Use POSIX Extended Regular Expression syntax when interpreting regex.
bool matches(const char *s, str::smatch &matches, int flags=none) const
Regular expression match result.
std::string::size_type end(unsigned i) const
End index of subexpression i in match_str (or std::string::npos)
std::string::size_type begin(unsigned i) const
Begin index of subexpression i in match_str (or std::string::npos)
bool regex_match(const std::string &s, smatch &matches, const regex ®ex)
\relates regex \ingroup ZYPP_STR_REGEX    \relates regex \ingroup ZYPP_STR_REGEX
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", const Trim trim_r=NO_TRIM)
Split line_r into words.
TInt strtonum(const C_Str &str)
Parsing numbers from string.
std::string regex_substitute(const std::string &s, const regex ®ex, const std::string &replacement, bool global=true) ZYPP_API
Replaces the matched regex with the string passed in replacement.
Easy-to use interface to the ZYPP dependency resolver.
std::map< Arch, EditionToSolvableMap > ArchToEditionMap
const Arch Arch_empty(IdString::Empty)
std::unordered_map< std::string, GroupInfo > GroupMap
std::list< sat::Solvable > SolvableList
std::map< Edition, SolvableList > EditionToSolvableMap
enum zypp::GroupInfo::GroupType groupType
ArchToEditionMap archToEdMap
GroupInfo(const GroupType type=None, std::string flav="")
static bool versionMatch(const Edition &a, const Edition &b)
std::set< Edition > _keepSpecificEditions
bool removePackageAndCheck(const sat::Solvable slv, const std::set< sat::Solvable > &keepList, const std::set< sat::Solvable > &removeList) const
std::set< Edition > _runningKernelEditionVariants
std::set< size_t > _keepLatestOffsets
void setUnameR(const std::string &uname)
std::set< size_t > _keepOldestOffsets
void fillKeepList(const GroupMap &installedKernels, std::set< sat::Solvable > &keepList, std::set< sat::Solvable > &removeList) const
Flavour _runningKernelFlavour