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