libzypp  17.31.31
PurgeKernels.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include <zypp/base/String.h>
14 #include <zypp/base/Logger.h>
15 #include <zypp/base/Regex.h>
16 #include <zypp/base/Iterator.h>
17 #include <zypp/PurgeKernels.h>
18 #include <zypp/PoolQuery.h>
19 #include <zypp/ResPool.h>
20 #include <zypp/Resolver.h>
21 #include <zypp/Filter.h>
22 #include <zypp/ZConfig.h>
23 
24 #include <iostream>
25 #include <fstream>
26 #include <map>
27 #include <unordered_map>
28 #include <sys/utsname.h>
29 #include <functional>
30 #include <array>
31 #include <climits>
32 
33 #undef ZYPP_BASE_LOGGER_LOGGROUP
34 #define ZYPP_BASE_LOGGER_LOGGROUP "PurgeKernels"
35 
36 namespace zypp {
37 
38  using Flavour = std::string;
39  using SolvableList = std::list<sat::Solvable>;
40  using EditionToSolvableMap = std::map<Edition, SolvableList >;
41  using ArchToEditionMap = std::map<Arch, EditionToSolvableMap >;
42 
43  struct GroupInfo {
44 
45  enum GroupType {
46  None, //<< Just here to support default construction
47  Kernels, //<< Map contains kernel packages, so need to receive special handling and flavour matching
48  RelatedBinaries, //<< Map contains related binary packages, so need to receive special handling and flavour matching
49  Sources //<< Map contains source packages, so when matching those against running we ignore the flavour
50  } groupType;
51 
52  GroupInfo( const GroupType type = None, std::string flav = "") : groupType(type), groupFlavour( std::move(flav) ) { }
53 
54  ArchToEditionMap archToEdMap; //<< Map of actual packages
55  std::string groupFlavour; //<< This would contain a specific flavour if there is one calculated
56  };
57  using GroupMap = std::unordered_map<std::string, GroupInfo>;
58 
60 
61  Impl() {
62  struct utsname unameData;
63  if ( uname( &unameData) == 0 ) {
64 
65  const auto archStr = str::regex_substitute( unameData.machine, str::regex( "^i.86$", str::regex::match_extended ), "i586" );
66 
67  _kernelArch = Arch( archStr );
68  setUnameR( std::string( unameData.release ) );
69 
70  _detectedRunning = true;
71 
72  MIL << "Detected running kernel: Flavour: " << _runningKernelFlavour << " Arch: " << _kernelArch << "\n";
73  for ( const auto &edVar : _runningKernelEditionVariants )
74  MIL << "Edition variant: " << edVar << "\n";
75  MIL << std::endl;
76 
77  } else {
78  MIL << "Failed to detect running kernel: " << errno << std::endl;
79  }
80  }
81 
82  void setUnameR ( const std::string &uname ) {
83 
84  _uname_r = uname;
85 
86  MIL << "Set uname " << uname << std::endl;
87 
88  const std::string flavour = str::regex_substitute( _uname_r, str::regex( ".*-", str::regex::match_extended ), "", true );
89  std::string version = str::regex_substitute( _uname_r, str::regex( "-[^-]*$", str::regex::match_extended | str::regex::newline ), "", true );
90 
91  const std::string release = str::regex_substitute( version, str::regex( ".*-", str::regex::match_extended ), "", true );
92 
93  version = str::regex_substitute( version, str::regex( "-[^-]*$", str::regex::match_extended | str::regex::newline ), "", true );
94 
95 
96  auto makeRcVariant = [ &release ]( std::string myVersion, const std::string &replace ){
97  // from purge-kernels script, was copied from kernel-source/rpm/mkspec
98  myVersion = str::regex_substitute( myVersion, str::regex( "\\.0-rc", str::regex::match_extended ), replace, true );
99  myVersion = str::regex_substitute( myVersion, str::regex( "-rc\\d+", str::regex::match_extended ), "", true );
100  myVersion = str::regex_substitute( myVersion, str::regex( "-", str::regex::match_extended ), ".", true );
101  return Edition( myVersion, release );
102  };
103 
105  _runningKernelEditionVariants.insert( makeRcVariant( version, "~rc") );
106  _runningKernelEditionVariants.insert( makeRcVariant( version, ".rc") );
107 
108  _runningKernelFlavour = flavour;
109 
110  MIL << "Parsed info from uname: " << std::endl;
111  MIL << "Kernel Flavour: " << _runningKernelFlavour << std::endl;
112  MIL << "Kernel Edition Variants: \n";
113  for ( const auto &var : _runningKernelEditionVariants )
114  MIL << " " << var << "\n";
115  MIL << std::endl;
116  }
117 
118  bool removePackageAndCheck( const sat::Solvable slv, const std::set<sat::Solvable> &keepList , const std::set<sat::Solvable> &removeList ) const;
119  static bool versionMatch ( const Edition &a, const Edition &b );
120  void parseKeepSpec();
121  void fillKeepList(const GroupMap &installedKernels, std::set<sat::Solvable> &keepList , std::set<sat::Solvable> &removeList ) const;
122 
123  std::set<size_t> _keepLatestOffsets = { 0 };
124  std::set<size_t> _keepOldestOffsets;
125  std::set<Edition> _keepSpecificEditions;
126  std::string _uname_r;
132  std::set<Edition> _runningKernelEditionVariants;
136  bool _keepRunning = true;
137  bool _detectedRunning = false;
138  };
139 
144  bool PurgeKernels::Impl::removePackageAndCheck( const sat::Solvable slv, const std::set<sat::Solvable> &keepList , const std::set<sat::Solvable> &removeList ) const
145  {
146  const filter::ByStatus toBeUninstalledFilter( &ResStatus::isToBeUninstalled );
147 
148  PoolItem pi ( slv );
149 
150  auto pool = ResPool::instance();
151 
152  // make sure the pool is clean
153  if ( !pool.resolver().resolvePool() ) {
154  MIL << "Pool failed to resolve, not doing anything" << std::endl;
155  return false;
156  }
157 
158  MIL << "Request to remove package: " << pi << std::endl;
159 
160  //list of packages that are allowed to be removed automatically.
161  const str::regex validRemovals("(kernel-syms(-.*)?|kgraft-patch(-.*)?|kernel-(.*)-livepatch(-.*)?|kernel-livepatch(-.*)?|.*-kmp(-.*)?)");
162 
163  if ( pi.status().isLocked() ) {
164  MIL << "Package " << pi << " is locked by the user, not removing." << std::endl;
165  return false;
166  }
167 
168  //remember which packages are already marked for removal, we do not need to check them again
169  std::set<sat::Solvable> currentSetOfRemovals;
170  for ( const PoolItem & p : pool.byStatus( toBeUninstalledFilter ) ) {
171  currentSetOfRemovals.insert( p.satSolvable() );
172  }
173 
175 
176  if ( !pool.resolver().resolvePool() ) {
177  MIL << "Failed to resolve pool, skipping " << pi << std::endl;
178  pool.resolver().problems();
179  pi.statusReset();
180 
181  return false;
182  }
183 
184  std::set<sat::Solvable> removedInThisRun;
185  removedInThisRun.insert( slv );
186 
187  for ( const PoolItem & p : pool.byStatus( toBeUninstalledFilter ) ) {
188 
189  //check if that package is removeable
190  if ( p.status().isByUser() //this was set by us, ignore it
191  || (currentSetOfRemovals.find( p.satSolvable() ) != currentSetOfRemovals.end()) //this was marked by a previous removal, ignore them
192  )
193  continue;
194 
195  // remember for later we need remove the debugsource and debuginfo packages as well
196  removedInThisRun.insert( p.satSolvable() );
197 
198  MIL << "Package " << p << " was marked by the solver for removal." << std::endl;
199 
200  // if we do not plan to remove that package anyway, we need to check if its allowed to be removed ( package in removelist can never be in keep list )
201  if ( removeList.find( p.satSolvable() ) != removeList.end() )
202  continue;
203 
204  if ( keepList.find( p.satSolvable() ) != keepList.end() ) {
205  MIL << "Package " << p << " is in keep spec, skipping" << pi << std::endl;
206  pi.statusReset();
207  return false;
208  }
209 
210  /*
211  * bsc#1185325 We can not solely rely on name matching to figure out
212  * which packages are kmod's, in SLES from Leap 15.3 forward we have the
213  * kernel-flavour-extra packages ( and others similarly named ) that are basically
214  * a collection of kmod's. So checking the name for .*-kmp(-.*)? is not enough.
215  * We first check if the package provides kmod(*) or ksym(*) and only fall back to name
216  * checking if that is not the case.
217  * Just to be safe I'll leave the regex in the fallback case as well, but it should be completely
218  * redundant now.
219  */
220  bool mostLikelyKmod = false;
221  StrMatcher matchMod( "kmod(*)", Match::GLOB );
222  StrMatcher matchSym( "ksym(*)", Match::GLOB );
223  for ( const auto &prov : p.provides() ) {
224  if ( matchMod.doMatch( prov.detail().name().c_str()) || matchSym.doMatch( prov.detail().name().c_str() ) ) {
225  mostLikelyKmod = true;
226  break;
227  }
228  }
229 
230  if ( mostLikelyKmod ) {
231  MIL << "Package " << p << " is most likely a kmod " << std::endl;
232  } else {
233  str::smatch what;
234  if ( !str::regex_match( p.name(), what, validRemovals) ) {
235  MIL << "Package " << p << " should not be removed, skipping " << pi << std::endl;
236  pi.statusReset();
237  return false;
238  }
239  }
240  }
241 
242  MIL << "Successfully marked package: " << pi << " for removal."<<std::endl;
243 
244  //now check and mark the -debugsource and -debuginfo packages for this package and all the packages that were removed. Maybe collect it before and just remove here
245  MIL << "Trying to remove debuginfo for: " << pi <<"."<<std::endl;
246  for ( sat::Solvable solvable : removedInThisRun ) {
247 
248  if ( solvable.arch() == Arch_noarch ||
249  solvable.arch() == Arch_empty )
250  continue;
251 
252  for ( const char * suffix : { "-debugsource", "-debuginfo" } ) {
253  PoolQuery q;
255  q.addDependency( sat::SolvAttr::provides, Capability( solvable.name()+suffix, Rel::EQ, solvable.edition() ) );
256  q.setInstalledOnly();
257  q.setMatchExact();
258 
259  for ( sat::Solvable debugPackage : q ) {
260 
261  if ( debugPackage.arch() != solvable.arch() )
262  continue;
263 
264  MIL << "Found debug package for " << solvable << " : " << debugPackage << std::endl;
265  //if removing the package fails it will not stop us from going on , so no need to check
266  removePackageAndCheck( debugPackage, keepList, removeList );
267  }
268  }
269  }
270  MIL << "Finished removing debuginfo for: " << pi <<"."<<std::endl;
271 
272  return true;
273  }
274 
279  {
280  if ( a == b )
281  return true;
282 
283  // the build counter should not be considered here, so if there is one we cut it off
284  const str::regex buildCntRegex( "\\.[0-9]+($|\\.g[0-9a-f]{7}$)", str::regex::match_extended );
285 
286  std::string versionStr = b.asString();
287  str::smatch matches;
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] );
291  return a == Edition(versionStr);
292  }
293  }
294  return false;
295  }
296 
301  {
302  //keep spec parse regex, make sure to edit the group offsets if changing this regex
303  const str::regex specRegex( "^(latest|oldest)([+-][0-9]+)?$", str::regex::match_extended );
304 
305  const unsigned tokenGrp = 1; //index of the group matching the token
306  const unsigned modifierGrp = 2; //index of the group matching the offset modifier
307 
308 
309  MIL << "Parsing keep spec: " << _keepSpec << std::endl;
310 
311  std::vector<std::string> words;
312  str::split( _keepSpec, std::back_inserter(words), ",", str::TRIM );
313  if ( words.empty() ) {
314  WAR << "Invalid keep spec: " << _keepSpec << " using default latest,running." << std::endl;
315  return;
316  }
317 
318  _keepRunning = false;
319  _keepLatestOffsets.clear();
320  _keepOldestOffsets.clear();
321 
322  for ( const std::string &word : words ) {
323  if ( word == "running" ) {
324  _keepRunning = true;
325  } else {
326  str::smatch what;
327  if ( !str::regex_match( word, what, specRegex ) ) {
328  // Allow uname_r version numbers by cutting off any postfix that comes after a second dash, including the second dash.
329  // Github-Issue openSUSE/zypper#418
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 );
337  }
338  }
339  _keepSpecificEditions.insert( Edition(IdString(edition)) );
340  continue;
341  }
342 
343  auto addKeepOff = []( const auto &off, auto &set, const auto &constraint ){
344  const off_t num = off.empty() ? 0 : str::strtonum<off_t>( off );
345  if ( !constraint(num) ) return false;
346  set.insert( static_cast<size_t>(std::abs(num)) );
347  return true;
348  };
349 
350  if ( what[tokenGrp] == "oldest" ) {
351  addKeepOff( what[modifierGrp], _keepOldestOffsets, [ &word ]( off_t num ) {
352  if ( num < 0 ) {
353  WAR << "Ignoring invalid modifier in keep spec: " << word << ", oldest supports only positive modifiers." << std::endl;
354  return false;
355  }
356  return true;
357  });
358  } else {
359  addKeepOff( what[modifierGrp], _keepLatestOffsets, [ &word ]( off_t num ) {
360  if ( num > 0 ) {
361  WAR << "Ignoring invalid modifier in keep spec: " << word << ", latest supports only negative modifiers." << std::endl;
362  return false;
363  }
364  return true;
365  });
366  }
367  }
368  }
369  }
370 
380  void PurgeKernels::Impl::fillKeepList( const GroupMap &installedKernels, std::set<sat::Solvable> &keepList, std::set<sat::Solvable> &removeList ) const
381  {
382 
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 );
387  };
388 
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 );
393  };
394  };
395 
396  for ( const auto &groupInfo : installedKernels ) {
397 
398  MIL << "Starting with group " << groupInfo.first << std::endl;
399 
400  for ( const auto &archMap : groupInfo.second.archToEdMap ) {
401 
402  MIL << "Starting with arch " << archMap.first << std::endl;
403 
404  size_t currOff = 0; //the current "oldest" offset ( runs from map start to end )
405  size_t currROff = archMap.second.size() - 1; // the current "latest" offset ( runs from map end to start )
406 
407 
408  const EditionToSolvableMap &map = archMap.second;
409 
410  if ( _keepRunning
411  && ( ( archMap.first == _kernelArch && groupInfo.second.groupFlavour == _runningKernelFlavour )
412  || groupInfo.second.groupType == GroupInfo::Sources ) ) {
413 
414  MIL << "Matching packages against running kernel "<< _runningKernelFlavour << "-" <<_kernelArch << "\nVariants:\n";
415  for ( const auto &var : _runningKernelEditionVariants )
416  MIL << var << "\n";
417  MIL << std::endl;
418 
419  const auto &editionPredicate = versionPredicate( _runningKernelEditionVariants );
420  auto it = std::find_if( map.begin(), map.end(), editionPredicate );
421  if ( it == map.end() ) {
422 
423  // If we look at Sources we cannot match the flavour but we still want to keep on checking the rest of the keep spec
424  if ( groupInfo.second.groupType != GroupInfo::Sources ) {
425  MIL << "Running kernel " << _runningKernelFlavour << "-" <<_kernelArch << "\n";
426  for ( const auto &var : _runningKernelEditionVariants )
427  MIL << " Possible Variant:" << var << "\n";
428  MIL << "Not installed! \n";
429  MIL << "NOT removing any packages for flavor "<<_runningKernelFlavour<<"-"<<_kernelArch<<" ."<<std::endl;
430 
431  for ( const auto &kernelMap : map ) {
432  for( sat::Solvable pck : kernelMap.second )
433  markAsKeep(pck);
434  }
435  continue;
436  }
437 
438  } else {
439  // there could be multiple matches here because of rebuild counter, lets try to find the last one
440  MIL << "Found possible running candidate edition: " << it->first << std::endl;
441  auto nit = it;
442  for ( nit++ ; nit != map.end() && editionPredicate( *nit ) ; nit++ ) {
443  MIL << "Found possible more recent running candidate edition: " << nit->first << std::endl;
444  it = nit;
445  }
446  }
447 
448  // mark all packages of the running version as keep
449  if ( it != map.end() ) {
450  for( sat::Solvable pck : it->second ) {
451  markAsKeep(pck);
452  }
453  }
454  }
455 
456  for ( const auto &kernelMap : map ) {
457  //if we find one of the running offsets in the keepspec, we add the kernel id the the list of packages to keep
458  if ( _keepOldestOffsets.find( currOff ) != _keepOldestOffsets.end() || _keepLatestOffsets.find( currROff ) != _keepLatestOffsets.end() ) {
459  std::for_each( kernelMap.second.begin(), kernelMap.second.end(), markAsKeep );
460  }
461  currOff++;
462  currROff--;
463 
464  // a kernel package might be explicitly locked by version
465  // We need to go over all package name provides ( provides is named like the package ) and match
466  // them against the specified version to know which ones to keep. (bsc#1176740 bsc#1176192)
467  std::for_each( kernelMap.second.begin(), kernelMap.second.end(), [ & ]( sat::Solvable solv ){
468  for ( Capability prov : solv.provides() ) {
469  if ( prov.detail().name() == solv.name() && _keepSpecificEditions.count( prov.detail().ed() ) ) {
470  markAsKeep( solv );
471  break;
472  }
473  }
474  });
475  }
476  }
477  }
478  }
479 
481  : _pimpl( new Impl() )
482  {
483 
484  }
485 
487  {
488  MIL << std::endl << "--------------------- Starting to mark obsolete kernels ---------------------"<<std::endl;
489 
490  if ( _pimpl->_keepSpec.empty() ) {
491  WAR << "Keep spec is empty, removing nothing." << std::endl;
492  return;
493  }
494 
496 
498  WAR << "Unable to detect running kernel, but keeping the running kernel was requested. Not removing any packages." << std::endl;
499  return;
500  }
501 
502  auto pool = ResPool::instance();
503  pool.resolver().setForceResolve( true ); // set allow uninstall flag
504 
505  const filter::ByStatus toBeUninstalledFilter( &ResStatus::isToBeUninstalled );
506 
507  // kernel flavour regex
508  const str::regex kernelFlavourRegex("^kernel-(.*)$");
509 
510  // the map of all installed kernel packages, grouped by Flavour -> Arch -> Version -> (List of all packages in that category)
511  // devel and source packages are grouped together
512  GroupMap installedKrnlPackages;
513 
514 
515  // packages that we plan to remove
516  std::set<sat::Solvable> packagesToRemove;
517 
518  const auto addPackageToMap = [&installedKrnlPackages, &packagesToRemove] ( const GroupInfo::GroupType type, const std::string &ident, const std::string &flavour, const sat::Solvable &installedKrnlPck ) {
519 
520  if ( !installedKrnlPackages.count( ident ) )
521  installedKrnlPackages.insert( std::make_pair( ident, GroupInfo(type, flavour) ) );
522 
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;
528  }
529 
530  const auto currArch = installedKrnlPck.arch();
531  if ( !groupInfo.archToEdMap.count( currArch ) )
532  groupInfo.archToEdMap.insert( std::make_pair( currArch , EditionToSolvableMap {} ) );
533 
534  auto &editionToSolvableMap = groupInfo.archToEdMap[ currArch ];
535 
536  // calculate the "shortest" or most generic edition of all the package name provides
537  // ( the key of the provides is the package name ). This generic edition is used to
538  // group the packages together. This should get us around the issue that uname -r does
539  // not represent the actual rpm package version anymore. ( bsc#1176740 )
540  auto currCount = INT_MAX;
541  Edition edToUse;
542  for ( Capability prov : installedKrnlPck.provides() ) {
543  if ( prov.detail().name() == installedKrnlPck.name() ) {
544  if ( edToUse == Edition::noedition ) {
545  edToUse = installedKrnlPck.edition();
546  const auto &relStr = edToUse.release();
547  currCount = std::count( relStr.begin(), relStr.end(), '.');
548  } else {
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 ) {
552  currCount = pntCnt;
553  edToUse = pckEd;
554  }
555  }
556  }
557  }
558 
559  if ( !editionToSolvableMap.count( edToUse ) )
560  editionToSolvableMap.insert( std::make_pair( edToUse, SolvableList{} ) );
561 
562  editionToSolvableMap[edToUse].push_back( installedKrnlPck );
563 
564  //in the first step we collect all packages in this list, then later we will remove the packages we want to explicitly keep
565  packagesToRemove.insert( installedKrnlPck );
566  };
567 
568  // the set of satSolvables that have to be kept always
569  std::set<sat::Solvable> packagesToKeep;
570 
571  //collect the list of installed kernel packages
572  PoolQuery q;
574  q.addAttribute( sat::SolvAttr::provides, "multiversion(kernel)" );
575  q.setInstalledOnly();
576  q.setMatchExact();
577 
578  MIL << "Searching for obsolete multiversion kernel packages." << std::endl;
579 
580  for ( sat::Solvable installedKrnlPck : q ) {
581 
582  MIL << "Found installed multiversion kernel package " << installedKrnlPck << std::endl;
583 
584  if ( installedKrnlPck.provides().matches(Capability("kernel-uname-r")) ) {
585  MIL << "Identified as a kernel package " << std::endl;
586 
587  // we group kernel packages by flavour
588  str::smatch what;
589  str::regex_match( installedKrnlPck.name(), what, kernelFlavourRegex );
590  if ( what[1].empty() ) {
591  WAR << "Could not detect flavour for: " << installedKrnlPck << " ...skipping" << std::endl;
592  continue;
593  }
594 
595  std::string flavour = what[1];
596 
597  // XXX: No dashes in flavor names
598  const auto dash = flavour.find_first_of('-');
599  if ( dash != std::string::npos ) {
600  flavour = flavour.substr( 0, dash );
601  }
602 
603  // the ident for kernels is the flavour, to also handle cases like kernel-base and kernel which should be in the same group handled together
604  addPackageToMap( GroupInfo::Kernels, flavour, flavour, installedKrnlPck );
605 
606  } else {
607 
608  // if adapting the groups do not forget to explicitly handle the group when querying the matches
609  const str::regex explicitlyHandled("kernel-syms(-.*)?|kernel(-.*)?-devel");
610 
611  MIL << "Not a kernel package, inspecting more closely " << std::endl;
612 
613  // we directly handle all noarch packages that export multiversion(kernel)
614  if ( installedKrnlPck.arch() == Arch_noarch ) {
615 
616  MIL << "Handling package explicitly due to architecture (noarch)."<< std::endl;
617  addPackageToMap( GroupInfo::Sources, installedKrnlPck.name(), "", installedKrnlPck );
618 
619  } else if ( str::smatch match; str::regex_match( installedKrnlPck.name(), match, explicitlyHandled ) ) {
620 
621  // try to get the flavour from the name
622  // if we have a kernel-syms getting no flavour means we have the "default" one, otherwise we use the flavour
623  // getting no flavour for a kernel(-*)?-devel means we have the kernel-devel package otherwise the flavour specific one
624  // ...yes this is horrible
625  std::string flav;
626 
627  // first group match is a kernel-syms
628  if ( match.size() > 1 && match[1].size() )
629  flav = match[1].substr(1);
630  // second group match is a kernel-flavour-devel
631  else if ( match.size() > 2 && match[2].size() )
632  flav = match[2].substr(1);
633  else if ( installedKrnlPck.name() == "kernel-syms" )
634  flav = "default";
635 
636  MIL << "Handling package explicitly due to name match."<< std::endl;
637  addPackageToMap ( GroupInfo::RelatedBinaries, installedKrnlPck.name(), flav, installedKrnlPck );
638  } else {
639  MIL << "Package not explicitly handled" << std::endl;
640  }
641  }
642 
643  }
644 
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;
656  });
657  });
658  });
659  });
660 
661  _pimpl->fillKeepList( installedKrnlPackages, packagesToKeep, packagesToRemove );
662 
663  for ( sat::Solvable slv : packagesToRemove )
664  _pimpl->removePackageAndCheck( slv, packagesToKeep, packagesToRemove );
665  }
666 
667  void PurgeKernels::setUnameR( const std::string &val )
668  {
669  _pimpl->setUnameR( val );
670  }
671 
672  std::string PurgeKernels::unameR() const
673  {
674  return _pimpl->_uname_r;
675  }
676 
678  {
679  _pimpl->_kernelArch = arch;
680  }
681 
683  {
684  return _pimpl->_kernelArch;
685  }
686 
687  void PurgeKernels::setKeepSpec( const std::string &val )
688  {
689  _pimpl->_keepSpec = val;
690  }
691 
692  std::string PurgeKernels::keepSpec() const
693  {
694  return _pimpl->_keepSpec;
695  }
696 
697 }
#define MIL
Definition: Logger.h:96
ArchToEditionMap archToEdMap
Definition: PurgeKernels.cc:54
A Solvable object within the sat Pool.
Definition: Solvable.h:53
unsigned size() const
Definition: Regex.cc:106
std::string regex_substitute(const std::string &s, const regex &regex, const std::string &replacement, bool global=true)
Replaces the matched regex with the string passed in replacement.
Definition: Regex.cc:120
void addAttribute(const sat::SolvAttr &attr, const std::string &value="")
Filter by the value of the specified attr attribute.
Definition: PoolQuery.cc:884
static const ResKind package
Definition: ResKind.h:40
Regular expression.
Definition: Regex.h:94
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:922
std::map< Edition, SolvableList > EditionToSolvableMap
Definition: PurgeKernels.cc:40
unsigned short b
void fillKeepList(const GroupMap &installedKernels, std::set< sat::Solvable > &keepList, std::set< sat::Solvable > &removeList) const
std::set< size_t > _keepOldestOffsets
Architecture.
Definition: Arch.h:36
Filter solvables according to their status.
Definition: Filter.h:141
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition: StrMatcher.h:297
static const Rel EQ
Definition: Rel.h:50
std::map< Arch, EditionToSolvableMap > ArchToEditionMap
Definition: PurgeKernels.cc:41
ResStatus & status() const
Returns the current status.
Definition: PoolItem.cc:211
const Arch Arch_empty(IdString::Empty)
std::string unameR() const
bool doMatch(const char *string_r) const
Return whether string matches.
Definition: StrMatcher.cc:298
Definition: Arch.h:363
void addDependency(const sat::SolvAttr &attr, const std::string &name, const Rel &op, const Edition &edition)
Query "name|global op edition".
Definition: PoolQuery.cc:887
Access to the sat-pools string space.
Definition: IdString.h:42
void addKind(const ResKind &kind)
Filter by selectable kind.
Definition: PoolQuery.cc:871
std::string::size_type end(unsigned i) const
End index of subexpression i in match_str (or std::string::npos)
Definition: Regex.cc:100
Edition represents [epoch:]version[-release]
Definition: Edition.h:60
std::string::size_type begin(unsigned i) const
Begin index of subexpression i in match_str (or std::string::npos)
Definition: Regex.cc:97
RW_pointer< Impl > _pimpl
Definition: PurgeKernels.h:66
Match newline.
Definition: Regex.h:102
#define ERR
Definition: Logger.h:98
std::set< Edition > _keepSpecificEditions
std::string Flavour
Definition: PurgeKernels.cc:38
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.
Definition: String.h:531
ResStatus & statusReset() const
Resets status to the default state (KEEP_STATE bySOLVER; clears any lock!).
Definition: PoolItem.cc:212
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.
Definition: Edition.cc:110
#define WAR
Definition: Logger.h:97
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)
Definition: PurgeKernels.cc:82
std::string keepSpec() const
bool matches(const char *s, str::smatch &matches, int flags=none) const
Definition: Regex.cc:57
void setInstalledOnly()
Return only repo packages.
Definition: PoolQuery.cc:974
bool setToBeUninstalled(TransactByValue causer)
Definition: ResStatus.h:544
std::unordered_map< std::string, GroupInfo > GroupMap
Definition: PurgeKernels.cc:57
void setKeepSpec(const std::string &val)
Regular expression match result.
Definition: Regex.h:167
Arch kernelArch() const
std::list< sat::Solvable > SolvableList
Definition: PurgeKernels.cc:39
std::set< Edition > _runningKernelEditionVariants
Meta-data query API.
Definition: PoolQuery.h:90
Use POSIX Extended Regular Expression syntax when interpreting regex.
Definition: Regex.h:101
bool isToBeUninstalled() const
Definition: ResStatus.h:261
bool isLocked() const
Definition: ResStatus.h:264
void setMatchExact()
Set to match exact string instead of substring.
Definition: PoolQuery.cc:963
bool any_of(const Container &c, Fnc &&cb)
Definition: Algorithm.h:76
GroupInfo(const GroupType type=None, std::string flav="")
Definition: PurgeKernels.cc:52
A sat capability.
Definition: Capability.h:62
static const SolvAttr provides
Definition: SolvAttr.h:60
std::string multiversionKernels() const
Definition: ZConfig.cc:1324
unsigned short a
enum zypp::GroupInfo::GroupType groupType
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
regex ZYPP_STR_REGEX regex ZYPP_STR_REGEX
Definition: Regex.h:70
Combining sat::Solvable and ResStatus.
Definition: PoolItem.h:50
std::string groupFlavour
Definition: PurgeKernels.cc:55
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
static const Edition noedition
Value representing noedition ("") This is in fact a valid Edition.
Definition: Edition.h:73
static ResPool instance()
Singleton ctor.
Definition: ResPool.cc:37