libzypp 17.35.1
TargetImpl.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12#include <iostream>
13#include <fstream>
14#include <sstream>
15#include <string>
16#include <list>
17#include <set>
18
19#include <sys/types.h>
20#include <dirent.h>
21
22#include <zypp/base/LogTools.h>
23#include <zypp/base/Exception.h>
24#include <zypp/base/Iterator.h>
25#include <zypp/base/Gettext.h>
26#include <zypp/base/IOStream.h>
28#include <zypp-core/base/UserRequestException>
29#include <zypp/base/Json.h>
30
31#include <zypp/ZConfig.h>
32#include <zypp/ZYppFactory.h>
33#include <zypp/PathInfo.h>
34
35#include <zypp/PoolItem.h>
36#include <zypp/ResObjects.h>
37#include <zypp/Url.h>
38#include <zypp/TmpPath.h>
39#include <zypp/RepoStatus.h>
41#include <zypp/Repository.h>
43
44#include <zypp/ResFilters.h>
45#include <zypp/HistoryLog.h>
51
54
55#include <zypp/sat/Pool.h>
59
62#include <zypp-core/zyppng/base/EventLoop>
63#include <zypp-core/zyppng/base/UnixSignalSource>
64#include <zypp-core/zyppng/io/AsyncDataSource>
65#include <zypp-core/zyppng/io/Process>
69#include <zypp-core/zyppng/base/EventDispatcher>
70
71#include <shared/commit/CommitMessages.h>
72
74
75#include <zypp/PluginExecutor.h>
76
77// include the error codes from zypp-rpm
78#include "tools/zypp-rpm/errorcodes.h"
79#include <rpm/rpmlog.h>
80
81#include <optional>
82
83namespace zypp::env {
85 {
86 static bool val = [](){
87 const char * env = getenv("TRANSACTIONAL_UPDATE");
88 return( env && zypp::str::strToBool( env, true ) );
89 }();
90 return val;
91 }
92} // namespace zypp::env
93
94using std::endl;
95
97extern "C"
98{
99#include <solv/repo_rpmdb.h>
100#include <solv/chksum.h>
101}
102namespace zypp
103{
104 namespace target
105 {
106 inline std::string rpmDbStateHash( const Pathname & root_r )
107 {
108 std::string ret;
109 AutoDispose<void*> state { ::rpm_state_create( sat::Pool::instance().get(), root_r.c_str() ), ::rpm_state_free };
110 AutoDispose<Chksum*> chk { ::solv_chksum_create( REPOKEY_TYPE_SHA1 ), []( Chksum *chk ) -> void {
111 ::solv_chksum_free( chk, nullptr );
112 } };
113 if ( ::rpm_hash_database_state( state, chk ) == 0 )
114 {
115 int md5l;
116 const unsigned char * md5 = ::solv_chksum_get( chk, &md5l );
117 ret = ::pool_bin2hex( sat::Pool::instance().get(), md5, md5l );
118 }
119 else
120 WAR << "rpm_hash_database_state failed" << endl;
121 return ret;
122 }
123
124 inline RepoStatus rpmDbRepoStatus( const Pathname & root_r )
125 { return RepoStatus( rpmDbStateHash( root_r ), Date() ); }
126
127 } // namespace target
128} // namespace
130
132namespace zypp
133{
135 namespace
136 {
137 // HACK for bnc#906096: let pool re-evaluate multiversion spec
138 // if target root changes. ZConfig returns data sensitive to
139 // current target root.
140 inline void sigMultiversionSpecChanged()
141 {
143 }
144 } //namespace
146
148 namespace json
149 {
150 // Lazy via template specialisation / should switch to overloading
151
152 template<>
153 inline std::string toJSON( const ZYppCommitResult::TransactionStepList & steps_r )
154 {
155 using sat::Transaction;
156 json::Array ret;
157
158 for ( const Transaction::Step & step : steps_r )
159 // ignore implicit deletes due to obsoletes and non-package actions
160 if ( step.stepType() != Transaction::TRANSACTION_IGNORE )
161 ret.add( step );
162
163 return ret.asJSON();
164 }
165
167 template<>
168 inline std::string toJSON( const sat::Transaction::Step & step_r )
169 {
170 static const std::string strType( "type" );
171 static const std::string strStage( "stage" );
172 static const std::string strSolvable( "solvable" );
173
174 static const std::string strTypeDel( "-" );
175 static const std::string strTypeIns( "+" );
176 static const std::string strTypeMul( "M" );
177
178 static const std::string strStageDone( "ok" );
179 static const std::string strStageFailed( "err" );
180
181 static const std::string strSolvableN( "n" );
182 static const std::string strSolvableE( "e" );
183 static const std::string strSolvableV( "v" );
184 static const std::string strSolvableR( "r" );
185 static const std::string strSolvableA( "a" );
186
187 using sat::Transaction;
188 json::Object ret;
189
190 switch ( step_r.stepType() )
191 {
192 case Transaction::TRANSACTION_IGNORE: /*empty*/ break;
193 case Transaction::TRANSACTION_ERASE: ret.add( strType, strTypeDel ); break;
194 case Transaction::TRANSACTION_INSTALL: ret.add( strType, strTypeIns ); break;
195 case Transaction::TRANSACTION_MULTIINSTALL: ret.add( strType, strTypeMul ); break;
196 }
197
198 switch ( step_r.stepStage() )
199 {
200 case Transaction::STEP_TODO: /*empty*/ break;
201 case Transaction::STEP_DONE: ret.add( strStage, strStageDone ); break;
202 case Transaction::STEP_ERROR: ret.add( strStage, strStageFailed ); break;
203 }
204
205 {
206 IdString ident;
207 Edition ed;
208 Arch arch;
209 if ( sat::Solvable solv = step_r.satSolvable() )
210 {
211 ident = solv.ident();
212 ed = solv.edition();
213 arch = solv.arch();
214 }
215 else
216 {
217 // deleted package; post mortem data stored in Transaction::Step
218 ident = step_r.ident();
219 ed = step_r.edition();
220 arch = step_r.arch();
221 }
222
223 json::Object s {
224 { strSolvableN, ident.asString() },
225 { strSolvableV, ed.version() },
226 { strSolvableR, ed.release() },
227 { strSolvableA, arch.asString() }
228 };
229 if ( Edition::epoch_t epoch = ed.epoch() )
230 s.add( strSolvableE, epoch );
231
232 ret.add( strSolvable, s );
233 }
234
235 return ret.asJSON();
236 }
237 } // namespace json
239
241 namespace target
242 {
244 namespace
245 {
246 class AssertMountedBase
247 {
248 NON_COPYABLE(AssertMountedBase);
249 NON_MOVABLE(AssertMountedBase);
250 protected:
251 AssertMountedBase()
252 {}
253
254 ~AssertMountedBase()
255 {
256 if ( ! _mountpoint.empty() ) {
257 // we mounted it so we unmount...
258 MIL << "We mounted " << _mountpoint << " so we unmount it" << endl;
259 execute({ "umount", "-R", "-l", _mountpoint.asString() });
260 }
261 }
262
263 protected:
264 int execute( ExternalProgram::Arguments && cmd_r ) const
265 {
266 ExternalProgram prog( cmd_r, ExternalProgram::Stderr_To_Stdout );
267 for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
268 { DBG << line; }
269 return prog.close();
270 }
271
272 protected:
273 Pathname _mountpoint;
274
275 };
276
279 class AssertProcMounted : private AssertMountedBase
280 {
281 public:
282 AssertProcMounted( Pathname root_r )
283 {
284 root_r /= "/proc";
285 if ( ! PathInfo(root_r/"self").isDir() ) {
286 MIL << "Try to make sure proc is mounted at" << root_r << endl;
287 if ( filesystem::assert_dir(root_r) == 0
288 && execute({ "mount", "-t", "proc", "/proc", root_r.asString() }) == 0 ) {
289 _mountpoint = std::move(root_r); // so we'll later unmount it
290 }
291 else {
292 WAR << "Mounting proc at " << root_r << " failed" << endl;
293 }
294 }
295 }
296 };
297
300 class AssertDevMounted : private AssertMountedBase
301 {
302 public:
303 AssertDevMounted( Pathname root_r )
304 {
305 root_r /= "/dev";
306 if ( ! PathInfo(root_r/"null").isChr() ) {
307 MIL << "Try to make sure dev is mounted at" << root_r << endl;
308 // https://unix.stackexchange.com/questions/263972/unmount-a-rbind-mount-without-affecting-the-original-mount
309 // Without --make-rslave unmounting <sandbox-root>/dev/pts
310 // may unmount /dev/pts and you're out of ptys.
311 if ( filesystem::assert_dir(root_r) == 0
312 && execute({ "mount", "--rbind", "--make-rslave", "/dev", root_r.asString() }) == 0 ) {
313 _mountpoint = std::move(root_r); // so we'll later unmount it
314 }
315 else {
316 WAR << "Mounting dev at " << root_r << " failed" << endl;
317 }
318 }
319 }
320 };
321
322 } // namespace
324
326 namespace
327 {
328 SolvIdentFile::Data getUserInstalledFromHistory( const Pathname & historyFile_r )
329 {
330 SolvIdentFile::Data onSystemByUserList;
331 // go and parse it: 'who' must constain an '@', then it was installed by user request.
332 // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
333 std::ifstream infile( historyFile_r.c_str() );
334 for( iostr::EachLine in( infile ); in; in.next() )
335 {
336 const char * ch( (*in).c_str() );
337 // start with year
338 if ( *ch < '1' || '9' < *ch )
339 continue;
340 const char * sep1 = ::strchr( ch, '|' ); // | after date
341 if ( !sep1 )
342 continue;
343 ++sep1;
344 // if logs an install or delete
345 bool installs = true;
346 if ( ::strncmp( sep1, "install|", 8 ) )
347 {
348 if ( ::strncmp( sep1, "remove |", 8 ) )
349 continue; // no install and no remove
350 else
351 installs = false; // remove
352 }
353 sep1 += 8; // | after what
354 // get the package name
355 const char * sep2 = ::strchr( sep1, '|' ); // | after name
356 if ( !sep2 || sep1 == sep2 )
357 continue;
358 (*in)[sep2-ch] = '\0';
359 IdString pkg( sep1 );
360 // we're done, if a delete
361 if ( !installs )
362 {
363 onSystemByUserList.erase( pkg );
364 continue;
365 }
366 // now guess whether user installed or not (3rd next field contains 'user@host')
367 if ( (sep1 = ::strchr( sep2+1, '|' )) // | after version
368 && (sep1 = ::strchr( sep1+1, '|' )) // | after arch
369 && (sep2 = ::strchr( sep1+1, '|' )) ) // | after who
370 {
371 (*in)[sep2-ch] = '\0';
372 if ( ::strchr( sep1+1, '@' ) )
373 {
374 // by user
375 onSystemByUserList.insert( pkg );
376 continue;
377 }
378 }
379 }
380 MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
381 return onSystemByUserList;
382 }
383 } // namespace
385
387 namespace
388 {
389 inline PluginFrame transactionPluginFrame( const std::string & command_r, ZYppCommitResult::TransactionStepList & steps_r )
390 {
391 return PluginFrame( command_r, json::Object {
392 { "TransactionStepList", steps_r }
393 }.asJSON() );
394 }
395 } // namespace
397
400 {
401 unsigned toKeep( ZConfig::instance().solver_upgradeTestcasesToKeep() );
402 MIL << "Testcases to keep: " << toKeep << endl;
403 if ( !toKeep )
404 return;
405 Target_Ptr target( getZYpp()->getTarget() );
406 if ( ! target )
407 {
408 WAR << "No Target no Testcase!" << endl;
409 return;
410 }
411
412 std::string stem( "updateTestcase" );
413 Pathname dir( target->assertRootPrefix("/var/log/") );
414 Pathname next( dir / Date::now().form( stem+"-%Y-%m-%d-%H-%M-%S" ) );
415
416 {
417 std::list<std::string> content;
418 filesystem::readdir( content, dir, /*dots*/false );
419 std::set<std::string> cases;
420 for_( c, content.begin(), content.end() )
421 {
422 if ( str::startsWith( *c, stem ) )
423 cases.insert( *c );
424 }
425 if ( cases.size() >= toKeep )
426 {
427 unsigned toDel = cases.size() - toKeep + 1; // +1 for the new one
428 for_( c, cases.begin(), cases.end() )
429 {
430 filesystem::recursive_rmdir( dir/(*c) );
431 if ( ! --toDel )
432 break;
433 }
434 }
435 }
436
437 MIL << "Write new testcase " << next << endl;
438 getZYpp()->resolver()->createSolverTestcase( next.asString(), false/*no solving*/ );
439 }
440
442 namespace
443 {
444
455 std::pair<bool,PatchScriptReport::Action> doExecuteScript( const Pathname & root_r,
456 const Pathname & script_r,
458 {
459 MIL << "Execute script " << PathInfo(Pathname::assertprefix( root_r,script_r)) << endl;
460
461 HistoryLog historylog;
462 historylog.comment(script_r.asString() + _(" executed"), /*timestamp*/true);
463 ExternalProgram prog( script_r.asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
464
465 for ( std::string output = prog.receiveLine(); output.length(); output = prog.receiveLine() )
466 {
467 historylog.comment(output);
468 if ( ! report_r->progress( PatchScriptReport::OUTPUT, output ) )
469 {
470 WAR << "User request to abort script " << script_r << endl;
471 prog.kill();
472 // the rest is handled by exit code evaluation
473 // in case the script has meanwhile finished.
474 }
475 }
476
477 std::pair<bool,PatchScriptReport::Action> ret( std::make_pair( false, PatchScriptReport::ABORT ) );
478
479 if ( prog.close() != 0 )
480 {
481 ret.second = report_r->problem( prog.execError() );
482 WAR << "ACTION" << ret.second << "(" << prog.execError() << ")" << endl;
483 std::ostringstream sstr;
484 sstr << script_r << _(" execution failed") << " (" << prog.execError() << ")" << endl;
485 historylog.comment(sstr.str(), /*timestamp*/true);
486 return ret;
487 }
488
489 report_r->finish();
490 ret.first = true;
491 return ret;
492 }
493
497 bool executeScript( const Pathname & root_r,
498 const Pathname & script_r,
499 callback::SendReport<PatchScriptReport> & report_r )
500 {
501 std::pair<bool,PatchScriptReport::Action> action( std::make_pair( false, PatchScriptReport::ABORT ) );
502
503 do {
504 action = doExecuteScript( root_r, script_r, report_r );
505 if ( action.first )
506 return true; // success
507
508 switch ( action.second )
509 {
511 WAR << "User request to abort at script " << script_r << endl;
512 return false; // requested abort.
513 break;
514
516 WAR << "User request to skip script " << script_r << endl;
517 return true; // requested skip.
518 break;
519
521 break; // again
522 }
523 } while ( action.second == PatchScriptReport::RETRY );
524
525 // THIS is not intended to be reached:
526 INT << "Abort on unknown ACTION request " << action.second << " returned" << endl;
527 return false; // abort.
528 }
529
535 bool RunUpdateScripts( const Pathname & root_r,
536 const Pathname & scriptsPath_r,
537 const std::vector<sat::Solvable> & checkPackages_r,
538 bool aborting_r )
539 {
540 if ( checkPackages_r.empty() )
541 return true; // no installed packages to check
542
543 MIL << "Looking for new update scripts in (" << root_r << ")" << scriptsPath_r << endl;
544 Pathname scriptsDir( Pathname::assertprefix( root_r, scriptsPath_r ) );
545 if ( ! PathInfo( scriptsDir ).isDir() )
546 return true; // no script dir
547
548 std::list<std::string> scripts;
549 filesystem::readdir( scripts, scriptsDir, /*dots*/false );
550 if ( scripts.empty() )
551 return true; // no scripts in script dir
552
553 // Now collect and execute all matching scripts.
554 // On ABORT: at least log all outstanding scripts.
555 // - "name-version-release"
556 // - "name-version-release-*"
557 bool abort = false;
558 std::map<std::string, Pathname> unify; // scripts <md5,path>
559 for_( it, checkPackages_r.begin(), checkPackages_r.end() )
560 {
561 std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) );
562 for_( sit, scripts.begin(), scripts.end() )
563 {
564 if ( ! str::hasPrefix( *sit, prefix ) )
565 continue;
566
567 if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' )
568 continue; // if not exact match it had to continue with '-'
569
570 PathInfo script( scriptsDir / *sit );
571 Pathname localPath( scriptsPath_r/(*sit) ); // without root prefix
572 std::string unifytag; // must not stay empty
573
574 if ( script.isFile() )
575 {
576 // Assert it's set as executable, unify by md5sum.
577 filesystem::addmod( script.path(), 0500 );
578 unifytag = filesystem::md5sum( script.path() );
579 }
580 else if ( ! script.isExist() )
581 {
582 // Might be a dangling symlink, might be ok if we are in
583 // instsys (absolute symlink within the system below /mnt).
584 // readlink will tell....
585 unifytag = filesystem::readlink( script.path() ).asString();
586 }
587
588 if ( unifytag.empty() )
589 continue;
590
591 // Unify scripts
592 if ( unify[unifytag].empty() )
593 {
594 unify[unifytag] = localPath;
595 }
596 else
597 {
598 // translators: We may find the same script content in files with different names.
599 // Only the first occurence is executed, subsequent ones are skipped. It's a one-line
600 // message for a log file. Preferably start translation with "%s"
601 std::string msg( str::form(_("%s already executed as %s)"), localPath.asString().c_str(), unify[unifytag].c_str() ) );
602 MIL << "Skip update script: " << msg << endl;
603 HistoryLog().comment( msg, /*timestamp*/true );
604 continue;
605 }
606
607 if ( abort || aborting_r )
608 {
609 WAR << "Aborting: Skip update script " << *sit << endl;
610 HistoryLog().comment(
611 localPath.asString() + _(" execution skipped while aborting"),
612 /*timestamp*/true);
613 }
614 else
615 {
616 MIL << "Found update script " << *sit << endl;
617 callback::SendReport<PatchScriptReport> report;
618 report->start( make<Package>( *it ), script.path() );
619
620 if ( ! executeScript( root_r, localPath, report ) ) // script path without root prefix!
621 abort = true; // requested abort.
622 }
623 }
624 }
625 return !abort;
626 }
627
629 //
631
632 inline void copyTo( std::ostream & out_r, const Pathname & file_r )
633 {
634 std::ifstream infile( file_r.c_str() );
635 for( iostr::EachLine in( infile ); in; in.next() )
636 {
637 out_r << *in << endl;
638 }
639 }
640
641 inline std::string notificationCmdSubst( const std::string & cmd_r, const UpdateNotificationFile & notification_r )
642 {
643 std::string ret( cmd_r );
644#define SUBST_IF(PAT,VAL) if ( ret.find( PAT ) != std::string::npos ) ret = str::gsub( ret, PAT, VAL )
645 SUBST_IF( "%p", notification_r.solvable().asString() );
646 SUBST_IF( "%P", notification_r.file().asString() );
647#undef SUBST_IF
648 return ret;
649 }
650
651 void sendNotification( const Pathname & root_r,
652 const UpdateNotifications & notifications_r )
653 {
654 if ( notifications_r.empty() )
655 return;
656
657 std::string cmdspec( ZConfig::instance().updateMessagesNotify() );
658 MIL << "Notification command is '" << cmdspec << "'" << endl;
659 if ( cmdspec.empty() )
660 return;
661
662 std::string::size_type pos( cmdspec.find( '|' ) );
663 if ( pos == std::string::npos )
664 {
665 ERR << "Can't send Notification: Missing 'format |' in command spec." << endl;
666 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
667 return;
668 }
669
670 std::string formatStr( str::toLower( str::trim( cmdspec.substr( 0, pos ) ) ) );
671 std::string commandStr( str::trim( cmdspec.substr( pos + 1 ) ) );
672
673 enum Format { UNKNOWN, NONE, SINGLE, DIGEST, BULK };
674 Format format = UNKNOWN;
675 if ( formatStr == "none" )
676 format = NONE;
677 else if ( formatStr == "single" )
678 format = SINGLE;
679 else if ( formatStr == "digest" )
680 format = DIGEST;
681 else if ( formatStr == "bulk" )
682 format = BULK;
683 else
684 {
685 ERR << "Can't send Notification: Unknown format '" << formatStr << " |' in command spec." << endl;
686 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
687 return;
688 }
689
690 // Take care: commands are ececuted chroot(root_r). The message file
691 // pathnames in notifications_r are local to root_r. For physical access
692 // to the file they need to be prefixed.
693
694 if ( format == NONE || format == SINGLE )
695 {
696 for_( it, notifications_r.begin(), notifications_r.end() )
697 {
698 std::vector<std::string> command;
699 if ( format == SINGLE )
700 command.push_back( "<"+Pathname::assertprefix( root_r, it->file() ).asString() );
701 str::splitEscaped( notificationCmdSubst( commandStr, *it ), std::back_inserter( command ) );
702
703 ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
704 if ( true ) // Wait for feedback
705 {
706 for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
707 {
708 DBG << line;
709 }
710 int ret = prog.close();
711 if ( ret != 0 )
712 {
713 ERR << "Notification command returned with error (" << ret << ")." << endl;
714 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
715 return;
716 }
717 }
718 }
719 }
720 else if ( format == DIGEST || format == BULK )
721 {
722 filesystem::TmpFile tmpfile;
723 std::ofstream out( tmpfile.path().c_str() );
724 for_( it, notifications_r.begin(), notifications_r.end() )
725 {
726 if ( format == DIGEST )
727 {
728 out << it->file() << endl;
729 }
730 else if ( format == BULK )
731 {
732 copyTo( out << '\f', Pathname::assertprefix( root_r, it->file() ) );
733 }
734 }
735
736 std::vector<std::string> command;
737 command.push_back( "<"+tmpfile.path().asString() ); // redirect input
738 str::splitEscaped( notificationCmdSubst( commandStr, *notifications_r.begin() ), std::back_inserter( command ) );
739
740 ExternalProgram prog( command, ExternalProgram::Stderr_To_Stdout, false, -1, true, root_r );
741 if ( true ) // Wait for feedback otherwise the TmpFile goes out of scope.
742 {
743 for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
744 {
745 DBG << line;
746 }
747 int ret = prog.close();
748 if ( ret != 0 )
749 {
750 ERR << "Notification command returned with error (" << ret << ")." << endl;
751 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
752 return;
753 }
754 }
755 }
756 else
757 {
758 INT << "Can't send Notification: Missing handler for 'format |' in command spec." << endl;
759 HistoryLog().comment( str::Str() << _("Error sending update message notification."), /*timestamp*/true );
760 return;
761 }
762 }
763
764
770 void RunUpdateMessages( const Pathname & root_r,
771 const Pathname & messagesPath_r,
772 const std::vector<sat::Solvable> & checkPackages_r,
773 ZYppCommitResult & result_r )
774 {
775 if ( checkPackages_r.empty() )
776 return; // no installed packages to check
777
778 MIL << "Looking for new update messages in (" << root_r << ")" << messagesPath_r << endl;
779 Pathname messagesDir( Pathname::assertprefix( root_r, messagesPath_r ) );
780 if ( ! PathInfo( messagesDir ).isDir() )
781 return; // no messages dir
782
783 std::list<std::string> messages;
784 filesystem::readdir( messages, messagesDir, /*dots*/false );
785 if ( messages.empty() )
786 return; // no messages in message dir
787
788 // Now collect all matching messages in result and send them
789 // - "name-version-release"
790 // - "name-version-release-*"
791 HistoryLog historylog;
792 for_( it, checkPackages_r.begin(), checkPackages_r.end() )
793 {
794 std::string prefix( str::form( "%s-%s", it->name().c_str(), it->edition().c_str() ) );
795 for_( sit, messages.begin(), messages.end() )
796 {
797 if ( ! str::hasPrefix( *sit, prefix ) )
798 continue;
799
800 if ( (*sit)[prefix.size()] != '\0' && (*sit)[prefix.size()] != '-' )
801 continue; // if not exact match it had to continue with '-'
802
803 PathInfo message( messagesDir / *sit );
804 if ( ! message.isFile() || message.size() == 0 )
805 continue;
806
807 MIL << "Found update message " << *sit << endl;
808 Pathname localPath( messagesPath_r/(*sit) ); // without root prefix
809 result_r.rUpdateMessages().push_back( UpdateNotificationFile( *it, localPath ) );
810 historylog.comment( str::Str() << _("New update message") << " " << localPath, /*timestamp*/true );
811 }
812 }
813 sendNotification( root_r, result_r.updateMessages() );
814 }
815
819 void logPatchStatusChanges( const sat::Transaction & transaction_r, TargetImpl & target_r )
820 {
822 if ( changedPseudoInstalled.empty() )
823 return;
824
825 if ( ! transaction_r.actionEmpty( ~sat::Transaction::STEP_DONE ) )
826 {
827 // Need to recompute the patch list if commit is incomplete!
828 // We remember the initially established status, then reload the
829 // Target to get the current patch status. Then compare.
830 WAR << "Need to recompute the patch status changes as commit is incomplete!" << endl;
831 ResPool::EstablishedStates establishedStates{ ResPool::instance().establishedStates() };
832 target_r.load();
833 changedPseudoInstalled = establishedStates.changedPseudoInstalled();
834 }
835
836 HistoryLog historylog;
837 for ( const auto & el : changedPseudoInstalled )
838 historylog.patchStateChange( el.first, el.second );
839 }
840
842 } // namespace
844
845 void XRunUpdateMessages( const Pathname & root_r,
846 const Pathname & messagesPath_r,
847 const std::vector<sat::Solvable> & checkPackages_r,
848 ZYppCommitResult & result_r )
849 { RunUpdateMessages( root_r, messagesPath_r, checkPackages_r, result_r ); }
850
852
854
856 //
857 // METHOD NAME : TargetImpl::TargetImpl
858 // METHOD TYPE : Ctor
859 //
860 TargetImpl::TargetImpl( const Pathname & root_r, bool doRebuild_r )
861 : _root( root_r )
862 , _requestedLocalesFile( home() / "RequestedLocales" )
863 , _autoInstalledFile( home() / "AutoInstalled" )
864 , _hardLocksFile( Pathname::assertprefix( _root, ZConfig::instance().locksFile() ) )
865 , _vendorAttr( Pathname::assertprefix( _root, ZConfig::instance().vendorPath() ) )
866 {
867 _rpm.initDatabase( root_r, doRebuild_r );
868
870
872 sigMultiversionSpecChanged(); // HACK: see sigMultiversionSpecChanged
873 MIL << "Initialized target on " << _root << endl;
874 }
875
879 static std::string generateRandomId()
880 {
881 std::ifstream uuidprovider( "/proc/sys/kernel/random/uuid" );
882 return iostr::getline( uuidprovider );
883 }
884
890 void updateFileContent( const Pathname &filename,
891 boost::function<bool ()> condition,
892 boost::function<std::string ()> value )
893 {
894 std::string val = value();
895 // if the value is empty, then just dont
896 // do anything, regardless of the condition
897 if ( val.empty() )
898 return;
899
900 if ( condition() )
901 {
902 MIL << "updating '" << filename << "' content." << endl;
903
904 // if the file does not exist we need to generate the uuid file
905
906 std::ofstream filestr;
907 // make sure the path exists
908 filesystem::assert_dir( filename.dirname() );
909 filestr.open( filename.c_str() );
910
911 if ( filestr.good() )
912 {
913 filestr << val;
914 filestr.close();
915 }
916 else
917 {
918 // FIXME, should we ignore the error?
919 ZYPP_THROW(Exception("Can't openfile '" + filename.asString() + "' for writing"));
920 }
921 }
922 }
923
925 static bool fileMissing( const Pathname &pathname )
926 {
927 return ! PathInfo(pathname).isExist();
928 }
929
931 {
932 // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
933 if ( root() != "/" )
934 return;
935
936 // Create the anonymous unique id, used for download statistics
937 Pathname idpath( home() / "AnonymousUniqueId");
938
939 try
940 {
941 updateFileContent( idpath,
942 std::bind(fileMissing, idpath),
944 }
945 catch ( const Exception &e )
946 {
947 WAR << "Can't create anonymous id file" << endl;
948 }
949
950 }
951
953 {
954 // create the anonymous unique id
955 // this value is used for statistics
956 Pathname flavorpath( home() / "LastDistributionFlavor");
957
958 // is there a product
960 if ( ! p )
961 {
962 WAR << "No base product, I won't create flavor cache" << endl;
963 return;
964 }
965
966 std::string flavor = p->flavor();
967
968 try
969 {
970
971 updateFileContent( flavorpath,
972 // only if flavor is not empty
973 functor::Constant<bool>( ! flavor.empty() ),
975 }
976 catch ( const Exception &e )
977 {
978 WAR << "Can't create flavor cache" << endl;
979 return;
980 }
981 }
982
984 //
985 // METHOD NAME : TargetImpl::~TargetImpl
986 // METHOD TYPE : Dtor
987 //
989 {
991 sigMultiversionSpecChanged(); // HACK: see sigMultiversionSpecChanged
992 MIL << "Closed target on " << _root << endl;
993 }
994
996 //
997 // solv file handling
998 //
1000
1002 {
1003 return Pathname::assertprefix( _root, ZConfig::instance().repoSolvfilesPath() / sat::Pool::instance().systemRepoAlias() );
1004 }
1005
1007 {
1008 Pathname base = solvfilesPath();
1010 }
1011
1013 {
1014 Pathname base = solvfilesPath();
1015 Pathname rpmsolv = base/"solv";
1016 Pathname rpmsolvcookie = base/"cookie";
1017
1018 bool build_rpm_solv = true;
1019 // lets see if the rpm solv cache exists
1020
1021 RepoStatus rpmstatus( rpmDbRepoStatus(_root) && RepoStatus(_root/"etc/products.d") );
1022
1023 bool solvexisted = PathInfo(rpmsolv).isExist();
1024 if ( solvexisted )
1025 {
1026 // see the status of the cache
1027 PathInfo cookie( rpmsolvcookie );
1028 MIL << "Read cookie: " << cookie << endl;
1029 if ( cookie.isExist() )
1030 {
1031 RepoStatus status = RepoStatus::fromCookieFile(rpmsolvcookie);
1032 // now compare it with the rpm database
1033 if ( status == rpmstatus )
1034 build_rpm_solv = false;
1035 MIL << "Read cookie: " << rpmsolvcookie << " says: "
1036 << (build_rpm_solv ? "outdated" : "uptodate") << endl;
1037 }
1038 }
1039
1040 if ( build_rpm_solv )
1041 {
1042 // if the solvfile dir does not exist yet, we better create it
1043 filesystem::assert_dir( base );
1044
1045 Pathname oldSolvFile( solvexisted ? rpmsolv : Pathname() ); // to speedup rpmdb2solv
1046
1048 if ( !tmpsolv )
1049 {
1050 // Can't create temporary solv file, usually due to insufficient permission
1051 // (user query while @System solv needs refresh). If so, try switching
1052 // to a location within zypps temp. space (will be cleaned at application end).
1053
1054 bool switchingToTmpSolvfile = false;
1055 Exception ex("Failed to cache rpm database.");
1056 ex.remember(str::form("Cannot create temporary file under %s.", base.c_str()));
1057
1058 if ( ! solvfilesPathIsTemp() )
1059 {
1060 base = getZYpp()->tmpPath() / sat::Pool::instance().systemRepoAlias();
1061 rpmsolv = base/"solv";
1062 rpmsolvcookie = base/"cookie";
1063
1064 filesystem::assert_dir( base );
1065 tmpsolv = filesystem::TmpFile::makeSibling( rpmsolv );
1066
1067 if ( tmpsolv )
1068 {
1069 WAR << "Using a temporary solv file at " << base << endl;
1070 switchingToTmpSolvfile = true;
1071 _tmpSolvfilesPath = base;
1072 }
1073 else
1074 {
1075 ex.remember(str::form("Cannot create temporary file under %s.", base.c_str()));
1076 }
1077 }
1078
1079 if ( ! switchingToTmpSolvfile )
1080 {
1081 ZYPP_THROW(ex);
1082 }
1083 }
1084
1085 // Take care we unlink the solvfile on exception
1087
1089 cmd.push_back( "rpmdb2solv" );
1090 if ( ! _root.empty() ) {
1091 cmd.push_back( "-r" );
1092 cmd.push_back( _root.asString() );
1093 }
1094 cmd.push_back( "-D" );
1095 cmd.push_back( rpm().dbPath().asString() );
1096 cmd.push_back( "-X" ); // autogenerate pattern/product/... from -package
1097 // bsc#1104415: no more application support // cmd.push_back( "-A" ); // autogenerate application pseudo packages
1098 cmd.push_back( "-p" );
1099 cmd.push_back( Pathname::assertprefix( _root, "/etc/products.d" ).asString() );
1100
1101 if ( ! oldSolvFile.empty() )
1102 cmd.push_back( oldSolvFile.asString() );
1103
1104 cmd.push_back( "-o" );
1105 cmd.push_back( tmpsolv.path().asString() );
1106
1108 std::string errdetail;
1109
1110 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
1111 WAR << " " << output;
1112 if ( errdetail.empty() ) {
1113 errdetail = prog.command();
1114 errdetail += '\n';
1115 }
1116 errdetail += output;
1117 }
1118
1119 int ret = prog.close();
1120 if ( ret != 0 )
1121 {
1122 Exception ex(str::form("Failed to cache rpm database (%d).", ret));
1123 ex.remember( errdetail );
1124 ZYPP_THROW(ex);
1125 }
1126
1127 ret = filesystem::rename( tmpsolv, rpmsolv );
1128 if ( ret != 0 )
1129 ZYPP_THROW(Exception("Failed to move cache to final destination"));
1130 // if this fails, don't bother throwing exceptions
1131 filesystem::chmod( rpmsolv, 0644 );
1132
1133 rpmstatus.saveToCookieFile(rpmsolvcookie);
1134
1135 // We keep it.
1136 guard.resetDispose();
1137 sat::updateSolvFileIndex( rpmsolv ); // content digest for zypper bash completion
1138
1139 // system-hook: Finally send notification to plugins
1140 if ( root() == "/" )
1141 {
1142 PluginExecutor plugins;
1143 plugins.load( ZConfig::instance().pluginsPath()/"system" );
1144 if ( plugins )
1145 plugins.send( PluginFrame( "PACKAGESETCHANGED" ) );
1146 }
1147 }
1148 else
1149 {
1150 // On the fly add missing solv.idx files for bash completion.
1151 if ( ! PathInfo(base/"solv.idx").isExist() )
1152 sat::updateSolvFileIndex( rpmsolv );
1153 }
1154 return build_rpm_solv;
1155 }
1156
1158 {
1159 load( false );
1160 }
1161
1163 {
1164 Repository system( sat::Pool::instance().findSystemRepo() );
1165 if ( system )
1166 system.eraseFromPool();
1167 }
1168
1169 void TargetImpl::load( bool force )
1170 {
1171 bool newCache = buildCache();
1172 MIL << "New cache built: " << (newCache?"true":"false") <<
1173 ", force loading: " << (force?"true":"false") << endl;
1174
1175 // now add the repos to the pool
1176 sat::Pool satpool( sat::Pool::instance() );
1177 Pathname rpmsolv( solvfilesPath() / "solv" );
1178 MIL << "adding " << rpmsolv << " to pool(" << satpool.systemRepoAlias() << ")" << endl;
1179
1180 // Providing an empty system repo, unload any old content
1181 Repository system( sat::Pool::instance().findSystemRepo() );
1182
1183 if ( system && ! system.solvablesEmpty() )
1184 {
1185 if ( newCache || force )
1186 {
1187 system.eraseFromPool(); // invalidates system
1188 }
1189 else
1190 {
1191 return; // nothing to do
1192 }
1193 }
1194
1195 if ( ! system )
1196 {
1197 system = satpool.systemRepo();
1198 }
1199
1200 try
1201 {
1202 MIL << "adding " << rpmsolv << " to system" << endl;
1203 system.addSolv( rpmsolv );
1204 }
1205 catch ( const Exception & exp )
1206 {
1207 ZYPP_CAUGHT( exp );
1208 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
1209 clearCache();
1210 buildCache();
1211
1212 system.addSolv( rpmsolv );
1213 }
1214 satpool.rootDir( _root );
1215
1216 // (Re)Load the requested locales et al.
1217 // If the requested locales are empty, we leave the pool untouched
1218 // to avoid undoing changes the application applied. We expect this
1219 // to happen on a bare metal installation only. An already existing
1220 // target should be loaded before its settings are changed.
1221 {
1223 if ( ! requestedLocales.empty() )
1224 {
1225 satpool.initRequestedLocales( requestedLocales );
1226 }
1227 }
1228 {
1229 if ( ! PathInfo( _autoInstalledFile.file() ).isExist() )
1230 {
1231 // Initialize from history, if it does not exist
1232 Pathname historyFile( Pathname::assertprefix( _root, ZConfig::instance().historyLogFile() ) );
1233 if ( PathInfo( historyFile ).isExist() )
1234 {
1235 SolvIdentFile::Data onSystemByUser( getUserInstalledFromHistory( historyFile ) );
1236 SolvIdentFile::Data onSystemByAuto;
1237 for_( it, system.solvablesBegin(), system.solvablesEnd() )
1238 {
1239 IdString ident( (*it).ident() );
1240 if ( onSystemByUser.find( ident ) == onSystemByUser.end() )
1241 onSystemByAuto.insert( ident );
1242 }
1243 _autoInstalledFile.setData( onSystemByAuto );
1244 }
1245 // on the fly removed any obsolete SoftLocks file
1246 filesystem::unlink( home() / "SoftLocks" );
1247 }
1248 // read from AutoInstalled file
1250 for ( const auto & idstr : _autoInstalledFile.data() )
1251 q.push( idstr.id() );
1252 satpool.setAutoInstalled( q );
1253 }
1254
1255 // Load the needreboot package specs
1256 {
1257 sat::SolvableSpec needrebootSpec;
1258 needrebootSpec.addProvides( Capability("installhint(reboot-needed)") );
1259 needrebootSpec.addProvides( Capability("kernel") );
1260
1261 Pathname needrebootFile { Pathname::assertprefix( root(), ZConfig::instance().needrebootFile() ) };
1262 if ( PathInfo( needrebootFile ).isFile() )
1263 needrebootSpec.parseFrom( needrebootFile );
1264
1265 Pathname needrebootDir { Pathname::assertprefix( root(), ZConfig::instance().needrebootPath() ) };
1266 if ( PathInfo( needrebootDir ).isDir() )
1267 {
1268 static const StrMatcher isRpmConfigBackup( "\\.rpm(new|save|orig)$", Match::REGEX );
1269
1271 [&]( const Pathname & dir_r, const char *const str_r )->bool
1272 {
1273 if ( ! isRpmConfigBackup( str_r ) )
1274 {
1275 Pathname needrebootFile { needrebootDir / str_r };
1276 if ( PathInfo( needrebootFile ).isFile() )
1277 needrebootSpec.parseFrom( needrebootFile );
1278 }
1279 return true;
1280 });
1281 }
1282 satpool.setNeedrebootSpec( std::move(needrebootSpec) );
1283 }
1284
1286 {
1287 const HardLocksFile::Data & hardLocks( _hardLocksFile.data() );
1288 if ( ! hardLocks.empty() )
1289 {
1291 }
1292 }
1293
1294 // now that the target is loaded, we can cache the flavor
1296
1297 MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl;
1298 }
1299
1301 //
1302 // COMMIT
1303 //
1306 {
1307 // ----------------------------------------------------------------- //
1308 ZYppCommitPolicy policy_r( policy_rX );
1309 bool explicitDryRun = policy_r.dryRun(); // explicit dry run will trigger a fileconflict check, implicit (download-only) not.
1310
1311 ShutdownLock lck("zypp", "Zypp commit running.");
1312
1313 // Fake outstanding YCP fix: Honour restriction to media 1
1314 // at installation, but install all remaining packages if post-boot.
1315 if ( policy_r.restrictToMedia() > 1 )
1316 policy_r.allMedia();
1317
1318 if ( policy_r.downloadMode() == DownloadDefault ) {
1319 if ( root() == "/" )
1320 policy_r.downloadMode(DownloadInHeaps);
1321 else {
1322 if ( policy_r.singleTransModeEnabled() )
1324 else
1326 }
1327 }
1328 // DownloadOnly implies dry-run.
1329 else if ( policy_r.downloadMode() == DownloadOnly )
1330 policy_r.dryRun( true );
1331 // ----------------------------------------------------------------- //
1332
1333 MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
1334
1336 // Compute transaction:
1338 ZYppCommitResult result( root() );
1339 result.rTransaction() = pool_r.resolver().getTransaction();
1340 result.rTransaction().order();
1341 // steps: this is our todo-list
1343 if ( policy_r.restrictToMedia() )
1344 {
1345 // Collect until the 1st package from an unwanted media occurs.
1346 // Further collection could violate install order.
1347 MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
1348 for_( it, result.transaction().begin(), result.transaction().end() )
1349 {
1350 if ( makeResObject( *it )->mediaNr() > 1 )
1351 break;
1352 steps.push_back( *it );
1353 }
1354 }
1355 else
1356 {
1357 result.rTransactionStepList().insert( steps.end(), result.transaction().begin(), result.transaction().end() );
1358 }
1359 MIL << "Todo: " << result << endl;
1360
1362 // Prepare execution of commit plugins:
1364 PluginExecutor commitPlugins;
1365
1366 if ( ( root() == "/" || zypp::env::TRANSACTIONAL_UPDATE() ) && ! policy_r.dryRun() )
1367 {
1368 commitPlugins.load( ZConfig::instance().pluginsPath()/"commit" );
1369 }
1370 if ( commitPlugins )
1371 commitPlugins.send( transactionPluginFrame( "COMMITBEGIN", steps ) );
1372
1374 // Write out a testcase if we're in dist upgrade mode.
1376 if ( pool_r.resolver().upgradeMode() || pool_r.resolver().upgradingRepos() )
1377 {
1378 if ( ! policy_r.dryRun() )
1379 {
1381 }
1382 else
1383 {
1384 DBG << "dryRun: Not writing upgrade testcase." << endl;
1385 }
1386 }
1387
1389 // Store non-package data:
1391 if ( ! policy_r.dryRun() )
1392 {
1394 // requested locales
1396 // autoinstalled
1397 {
1398 SolvIdentFile::Data newdata;
1399 for ( sat::Queue::value_type id : result.rTransaction().autoInstalled() )
1400 newdata.insert( IdString(id) );
1401 _autoInstalledFile.setData( newdata );
1402 }
1403 // hard locks
1405 {
1406 HardLocksFile::Data newdata;
1407 pool_r.getHardLockQueries( newdata );
1408 _hardLocksFile.setData( newdata );
1409 }
1410 }
1411 else
1412 {
1413 DBG << "dryRun: Not storing non-package data." << endl;
1414 }
1415
1417 // First collect and display all messages
1418 // associated with patches to be installed.
1420 if ( ! policy_r.dryRun() )
1421 {
1422 for_( it, steps.begin(), steps.end() )
1423 {
1424 if ( ! it->satSolvable().isKind<Patch>() )
1425 continue;
1426
1427 PoolItem pi( *it );
1428 if ( ! pi.status().isToBeInstalled() )
1429 continue;
1430
1432 if ( ! patch ||patch->message().empty() )
1433 continue;
1434
1435 MIL << "Show message for " << patch << endl;
1437 if ( ! report->show( patch ) )
1438 {
1439 WAR << "commit aborted by the user" << endl;
1441 }
1442 }
1443 }
1444 else
1445 {
1446 DBG << "dryRun: Not checking patch messages." << endl;
1447 }
1448
1450 // Remove/install packages.
1452
1453 bool singleTransMode = policy_r.singleTransModeEnabled();
1454
1455 DBG << "commit log file is set to: " << HistoryLog::fname() << endl;
1456 if ( ! policy_r.dryRun() || policy_r.downloadMode() == DownloadOnly )
1457 {
1458 // Prepare the package cache. Pass all items requiring download.
1459 CommitPackageCache packageCache;
1460 packageCache.setCommitList( steps.begin(), steps.end() );
1461
1462 bool miss = false;
1463 if ( policy_r.downloadMode() != DownloadAsNeeded )
1464 {
1465 // Preload the cache. Until now this means pre-loading all packages.
1466 // Once DownloadInHeaps is fully implemented, this will change and
1467 // we may actually have more than one heap.
1468 for_( it, steps.begin(), steps.end() )
1469 {
1470 switch ( it->stepType() )
1471 {
1474 // proceed: only install actionas may require download.
1475 break;
1476
1477 default:
1478 // next: no download for or non-packages and delete actions.
1479 continue;
1480 break;
1481 }
1482
1483 PoolItem pi( *it );
1484 if ( pi->isKind<Package>() || pi->isKind<SrcPackage>() )
1485 {
1486 ManagedFile localfile;
1487 try
1488 {
1489 localfile = packageCache.get( pi );
1490 localfile.resetDispose(); // keep the package file in the cache
1491 }
1492 catch ( const AbortRequestException & exp )
1493 {
1494 it->stepStage( sat::Transaction::STEP_ERROR );
1495 miss = true;
1496 WAR << "commit cache preload aborted by the user" << endl;
1498 break;
1499 }
1500 catch ( const SkipRequestException & exp )
1501 {
1502 ZYPP_CAUGHT( exp );
1503 it->stepStage( sat::Transaction::STEP_ERROR );
1504 miss = true;
1505 WAR << "Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1506 continue;
1507 }
1508 catch ( const Exception & exp )
1509 {
1510 // bnc #395704: missing catch causes abort.
1511 // TODO see if packageCache fails to handle errors correctly.
1512 ZYPP_CAUGHT( exp );
1513 it->stepStage( sat::Transaction::STEP_ERROR );
1514 miss = true;
1515 INT << "Unexpected Error: Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1516 continue;
1517 }
1518 }
1519 }
1520 packageCache.preloaded( true ); // try to avoid duplicate infoInCache CBs in commit
1521 }
1522
1523 if ( miss )
1524 {
1525 ERR << "Some packages could not be provided. Aborting commit."<< endl;
1526 }
1527 else
1528 {
1529 if ( ! policy_r.dryRun() )
1530 {
1531 if ( policy_r.singleTransModeEnabled() ) {
1532 commitInSingleTransaction( policy_r, packageCache, result );
1533 } else {
1534 // if cache is preloaded, check for file conflicts
1535 commitFindFileConflicts( policy_r, result );
1536 commit( policy_r, packageCache, result );
1537 }
1538 }
1539 else
1540 {
1541 DBG << "dryRun/downloadOnly: Not installing/deleting anything." << endl;
1542 if ( explicitDryRun ) {
1543 if ( policy_r.singleTransModeEnabled() ) {
1544 // single trans mode does a test install via rpm
1545 commitInSingleTransaction( policy_r, packageCache, result );
1546 } else {
1547 // if cache is preloaded, check for file conflicts
1548 commitFindFileConflicts( policy_r, result );
1549 }
1550 }
1551 }
1552 }
1553 }
1554 else
1555 {
1556 DBG << "dryRun: Not downloading/installing/deleting anything." << endl;
1557 if ( explicitDryRun ) {
1558 // if cache is preloaded, check for file conflicts
1559 commitFindFileConflicts( policy_r, result );
1560 }
1561 }
1562
1563 {
1564 // NOTE: Removing rpm in a transaction, rpm removes the /var/lib/rpm compat symlink.
1565 // We re-create it, in case it was lost to prevent legacy tools from accidentally
1566 // assuming no database is present.
1567 if ( ! PathInfo(_root/"/var/lib/rpm",PathInfo::LSTAT).isExist()
1568 && PathInfo(_root/"/usr/lib/sysimage/rpm").isDir() ) {
1569 WAR << "(rpm removed in commit?) Inject missing /var/lib/rpm compat symlink to /usr/lib/sysimage/rpm" << endl;
1570 filesystem::assert_dir( _root/"/var/lib" );
1571 filesystem::symlink( "../../usr/lib/sysimage/rpm", _root/"/var/lib/rpm" );
1572 }
1573 }
1574
1576 // Send result to commit plugins:
1578 if ( commitPlugins )
1579 commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
1580
1582 // Try to rebuild solv file while rpm database is still in cache
1584 if ( ! policy_r.dryRun() )
1585 {
1586 buildCache();
1587 }
1588
1589 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
1590 return result;
1591 }
1592
1594 //
1595 // COMMIT internal
1596 //
1598 namespace
1599 {
1600 struct NotifyAttemptToModify
1601 {
1602 NotifyAttemptToModify( ZYppCommitResult & result_r ) : _result( result_r ) {}
1603
1604 void operator()()
1605 { if ( _guard ) { _result.attemptToModify( true ); _guard = false; } }
1606
1607 TrueBool _guard;
1608 ZYppCommitResult & _result;
1609 };
1610 } // namespace
1611
1612 void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
1613 CommitPackageCache & packageCache_r,
1614 ZYppCommitResult & result_r )
1615 {
1616 // steps: this is our todo-list
1618 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1619
1621
1622 // Send notification once upon 1st call to rpm
1623 NotifyAttemptToModify attemptToModify( result_r );
1624
1625 bool abort = false;
1626
1627 // bsc#1181328: Some systemd tools require /proc to be mounted
1628 AssertProcMounted assertProcMounted( _root );
1629 AssertDevMounted assertDevMounted( _root ); // also /dev
1630
1631 RpmPostTransCollector postTransCollector( _root );
1632 std::vector<sat::Solvable> successfullyInstalledPackages;
1633 TargetImpl::PoolItemList remaining;
1634
1635 for_( step, steps.begin(), steps.end() )
1636 {
1637 PoolItem citem( *step );
1638 if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
1639 {
1640 if ( citem->isKind<Package>() )
1641 {
1642 // for packages this means being obsoleted (by rpm)
1643 // thius no additional action is needed.
1644 step->stepStage( sat::Transaction::STEP_DONE );
1645 continue;
1646 }
1647 }
1648
1649 if ( citem->isKind<Package>() )
1650 {
1651 Package::constPtr p = citem->asKind<Package>();
1652 if ( citem.status().isToBeInstalled() )
1653 {
1654 ManagedFile localfile;
1655 try
1656 {
1657 localfile = packageCache_r.get( citem );
1658 }
1659 catch ( const AbortRequestException &e )
1660 {
1661 WAR << "commit aborted by the user" << endl;
1662 abort = true;
1663 step->stepStage( sat::Transaction::STEP_ERROR );
1664 break;
1665 }
1666 catch ( const SkipRequestException &e )
1667 {
1668 ZYPP_CAUGHT( e );
1669 WAR << "Skipping package " << p << " in commit" << endl;
1670 step->stepStage( sat::Transaction::STEP_ERROR );
1671 continue;
1672 }
1673 catch ( const Exception &e )
1674 {
1675 // bnc #395704: missing catch causes abort.
1676 // TODO see if packageCache fails to handle errors correctly.
1677 ZYPP_CAUGHT( e );
1678 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1679 step->stepStage( sat::Transaction::STEP_ERROR );
1680 continue;
1681 }
1682
1683 // create a installation progress report proxy
1684 RpmInstallPackageReceiver progress( citem.resolvable() );
1685 progress.connect(); // disconnected on destruction.
1686
1687 bool success = false;
1688 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1689 // Why force and nodeps?
1690 //
1691 // Because zypp builds the transaction and the resolver asserts that
1692 // everything is fine.
1693 // We use rpm just to unpack and register the package in the database.
1694 // We do this step by step, so rpm is not aware of the bigger context.
1695 // So we turn off rpms internal checks, because we do it inside zypp.
1696 flags |= rpm::RPMINST_NODEPS;
1697 flags |= rpm::RPMINST_FORCE;
1698 //
1699 if (p->multiversionInstall()) flags |= rpm::RPMINST_NOUPGRADE;
1700 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1701 if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
1702 if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
1703
1704 attemptToModify();
1705 try
1706 {
1708 rpm().installPackage( localfile, flags, &postTransCollector );
1709 HistoryLog().install(citem);
1710
1711 if ( progress.aborted() )
1712 {
1713 WAR << "commit aborted by the user" << endl;
1714 localfile.resetDispose(); // keep the package file in the cache
1715 abort = true;
1716 step->stepStage( sat::Transaction::STEP_ERROR );
1717 break;
1718 }
1719 else
1720 {
1721 if ( citem.isNeedreboot() ) {
1722 auto rebootNeededFile = root() / "/run/reboot-needed";
1723 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
1724 filesystem::touch( rebootNeededFile );
1725 }
1726
1727 success = true;
1728 step->stepStage( sat::Transaction::STEP_DONE );
1729 }
1730 }
1731 catch ( Exception & excpt_r )
1732 {
1733 ZYPP_CAUGHT(excpt_r);
1734 localfile.resetDispose(); // keep the package file in the cache
1735
1736 if ( policy_r.dryRun() )
1737 {
1738 WAR << "dry run failed" << endl;
1739 step->stepStage( sat::Transaction::STEP_ERROR );
1740 break;
1741 }
1742 // else
1743 if ( progress.aborted() )
1744 {
1745 WAR << "commit aborted by the user" << endl;
1746 abort = true;
1747 }
1748 else
1749 {
1750 WAR << "Install failed" << endl;
1751 }
1752 step->stepStage( sat::Transaction::STEP_ERROR );
1753 break; // stop
1754 }
1755
1756 if ( success && !policy_r.dryRun() )
1757 {
1759 successfullyInstalledPackages.push_back( citem.satSolvable() );
1760 step->stepStage( sat::Transaction::STEP_DONE );
1761 }
1762 }
1763 else
1764 {
1765 RpmRemovePackageReceiver progress( citem.resolvable() );
1766 progress.connect(); // disconnected on destruction.
1767
1768 bool success = false;
1769 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1770 flags |= rpm::RPMINST_NODEPS;
1771 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1772
1773 attemptToModify();
1774 try
1775 {
1776 rpm().removePackage( p, flags, &postTransCollector );
1777 HistoryLog().remove(citem);
1778
1779 if ( progress.aborted() )
1780 {
1781 WAR << "commit aborted by the user" << endl;
1782 abort = true;
1783 step->stepStage( sat::Transaction::STEP_ERROR );
1784 break;
1785 }
1786 else
1787 {
1788 success = true;
1789 step->stepStage( sat::Transaction::STEP_DONE );
1790 }
1791 }
1792 catch (Exception & excpt_r)
1793 {
1794 ZYPP_CAUGHT( excpt_r );
1795 if ( progress.aborted() )
1796 {
1797 WAR << "commit aborted by the user" << endl;
1798 abort = true;
1799 step->stepStage( sat::Transaction::STEP_ERROR );
1800 break;
1801 }
1802 // else
1803 WAR << "removal of " << p << " failed";
1804 step->stepStage( sat::Transaction::STEP_ERROR );
1805 }
1806 if ( success && !policy_r.dryRun() )
1807 {
1809 step->stepStage( sat::Transaction::STEP_DONE );
1810 }
1811 }
1812 }
1813 else if ( ! policy_r.dryRun() ) // other resolvables (non-Package)
1814 {
1815 // Status is changed as the buddy package buddy
1816 // gets installed/deleted. Handle non-buddies only.
1817 if ( ! citem.buddy() )
1818 {
1819 if ( citem->isKind<Product>() )
1820 {
1821 Product::constPtr p = citem->asKind<Product>();
1822 if ( citem.status().isToBeInstalled() )
1823 {
1824 ERR << "Can't install orphan product without release-package! " << citem << endl;
1825 }
1826 else
1827 {
1828 // Deleting the corresponding product entry is all we con do.
1829 // So the product will no longer be visible as installed.
1830 std::string referenceFilename( p->referenceFilename() );
1831 if ( referenceFilename.empty() )
1832 {
1833 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
1834 }
1835 else
1836 {
1837 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
1838 if ( ! rpm().hasFile( referencePath.asString() ) )
1839 {
1840 // If it's not owned by a package, we can delete it.
1841 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
1842 if ( filesystem::unlink( referencePath ) != 0 )
1843 ERR << "Delete orphan product failed: " << referencePath << endl;
1844 }
1845 else
1846 {
1847 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
1848 }
1849 }
1850 }
1851 }
1852 else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() )
1853 {
1854 // SrcPackage is install-only
1855 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
1856 installSrcPackage( p );
1857 }
1858
1860 step->stepStage( sat::Transaction::STEP_DONE );
1861 }
1862
1863 } // other resolvables
1864
1865 } // for
1866
1867 // Process any remembered %posttrans and/or %transfiletrigger(postun|in)
1868 // scripts. If aborting, at least log if scripts were omitted.
1869 if ( not abort )
1870 postTransCollector.executeScripts( rpm() );
1871 else
1872 postTransCollector.discardScripts();
1873
1874 // Check presence of update scripts/messages. If aborting,
1875 // at least log omitted scripts.
1876 if ( ! successfullyInstalledPackages.empty() )
1877 {
1878 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
1879 successfullyInstalledPackages, abort ) )
1880 {
1881 WAR << "Commit aborted by the user" << endl;
1882 abort = true;
1883 }
1884 // send messages after scripts in case some script generates output,
1885 // that should be kept in t %ghost message file.
1886 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
1887 successfullyInstalledPackages,
1888 result_r );
1889 }
1890
1891 // jsc#SLE-5116: Log patch status changes to history
1892 // NOTE: Should be the last action as it may need to reload
1893 // the Target in case of an incomplete transaction.
1894 logPatchStatusChanges( result_r.transaction(), *this );
1895
1896 if ( abort )
1897 {
1898 HistoryLog().comment( "Commit was aborted." );
1900 }
1901 }
1902
1903
1910 struct SendSingleTransReport : public callback::SendReport<rpm::SingleTransReport>
1911 {
1913 void sendLogline( const std::string & line_r, ReportType::loglevel level_r = ReportType::loglevel::msg )
1914 {
1915 callback::UserData data { ReportType::contentLogline };
1916 data.set( "line", std::cref(line_r) );
1917 data.set( "level", level_r );
1918 report( data );
1919 }
1921 void sendLoglineRpm( const std::string & line_r, unsigned rpmlevel_r )
1922 {
1923 auto u2rpmlevel = []( unsigned rpmlevel_r ) -> ReportType::loglevel {
1924 switch ( rpmlevel_r ) {
1925 case RPMLOG_EMERG: [[fallthrough]]; // system is unusable
1926 case RPMLOG_ALERT: [[fallthrough]]; // action must be taken immediately
1927 case RPMLOG_CRIT: // critical conditions
1928 return ReportType::loglevel::crt;
1929 case RPMLOG_ERR: // error conditions
1930 return ReportType::loglevel::err;
1931 case RPMLOG_WARNING: // warning conditions
1932 return ReportType::loglevel::war;
1933 default: [[fallthrough]];
1934 case RPMLOG_NOTICE: [[fallthrough]]; // normal but significant condition
1935 case RPMLOG_INFO: // informational
1936 return ReportType::loglevel::msg;
1937 case RPMLOG_DEBUG:
1938 return ReportType::loglevel::dbg;
1939 }
1940 };
1941 sendLogline( line_r, u2rpmlevel( rpmlevel_r ) );
1942 }
1943
1944 private:
1945 void report( const callback::UserData & userData_r )
1946 { (*this)->report( userData_r ); }
1947 };
1948
1949 const callback::UserData::ContentType rpm::SingleTransReport::contentLogline { "zypp-rpm", "logline" };
1950
1951 const callback::UserData::ContentType rpm::InstallResolvableReportSA::contentRpmout( "zypp-rpm","installpkgsa" );
1952 const callback::UserData::ContentType rpm::RemoveResolvableReportSA::contentRpmout( "zypp-rpm","removepkgsa" );
1953 const callback::UserData::ContentType rpm::CommitScriptReportSA::contentRpmout( "zypp-rpm","scriptsa" );
1954 const callback::UserData::ContentType rpm::TransactionReportSA::contentRpmout( "zypp-rpm","transactionsa" );
1955 const callback::UserData::ContentType rpm::CleanupPackageReportSA::contentRpmout( "zypp-rpm","cleanupkgsa" );
1956
1958 {
1959 SendSingleTransReport report; // active throughout the whole rpm transaction
1960
1961 // steps: this is our todo-list
1963 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1964
1966
1967 // Send notification once upon calling rpm
1968 NotifyAttemptToModify attemptToModify( result_r );
1969
1970 // let zypper know we executed in one big transaction so in case of failures it can show extended error information
1971 result_r.setSingleTransactionMode( true );
1972
1973 // bsc#1181328: Some systemd tools require /proc to be mounted
1974 AssertProcMounted assertProcMounted( _root );
1975 AssertDevMounted assertDevMounted( _root ); // also /dev
1976
1977 // Why nodeps?
1978 //
1979 // Because zypp builds the transaction and the resolver asserts that
1980 // everything is fine, or the user decided to ignore problems.
1981 rpm::RpmInstFlags flags( policy_r.rpmInstFlags()
1983 // skip signature checks, we did that already
1986 // ignore untrusted keys since we already checked those earlier
1988
1989 proto::target::Commit commit;
1990 commit.flags = flags;
1991 commit.ignoreArch = ( !ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) );
1993 commit.dbPath = rpm().dbPath().asString();
1994 commit.root = rpm().root().asString();
1995 commit.lockFilePath = ZYppFactory::lockfileDir().asString();
1996
1997 bool abort = false;
1998 zypp::AutoDispose<std::unordered_map<int, ManagedFile>> locCache([]( std::unordered_map<int, ManagedFile> &data ){
1999 for ( auto &[_, value] : data ) {
2000 (void)_; // unsused; for older g++ versions
2001 value.resetDispose();
2002 }
2003 data.clear();
2004 });
2005
2006 // fill the transaction
2007 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; ++stepId ) {
2008 auto &step = steps[stepId];
2009 PoolItem citem( step );
2010 if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
2011 if ( citem->isKind<Package>() )
2012 {
2013 // for packages this means being obsoleted (by rpm)
2014 // thius no additional action is needed.
2015 step.stepStage( sat::Transaction::STEP_DONE );
2016 continue;
2017 }
2018 }
2019
2020 if ( citem->isKind<Package>() ) {
2021 Package::constPtr p = citem->asKind<Package>();
2022 if ( citem.status().isToBeInstalled() )
2023 {
2024 try {
2025 locCache.value()[stepId] = packageCache_r.get( citem );
2026
2027 proto::target::InstallStep tStep;
2028 tStep.stepId = stepId;
2029 tStep.pathname = locCache.value()[stepId]->asString();
2030 tStep.multiversion = p->multiversionInstall() ;
2031
2032 commit.transactionSteps.push_back( std::move(tStep) );
2033 }
2034 catch ( const AbortRequestException &e )
2035 {
2036 WAR << "commit aborted by the user" << endl;
2037 abort = true;
2038 step.stepStage( sat::Transaction::STEP_ERROR );
2039 break;
2040 }
2041 catch ( const SkipRequestException &e )
2042 {
2043 ZYPP_CAUGHT( e );
2044 WAR << "Skipping package " << p << " in commit" << endl;
2045 step.stepStage( sat::Transaction::STEP_ERROR );
2046 continue;
2047 }
2048 catch ( const Exception &e )
2049 {
2050 // bnc #395704: missing catch causes abort.
2051 // TODO see if packageCache fails to handle errors correctly.
2052 ZYPP_CAUGHT( e );
2053 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2054 step.stepStage( sat::Transaction::STEP_ERROR );
2055 continue;
2056 }
2057 } else {
2058
2059 proto::target::RemoveStep tStep;
2060 tStep.stepId = stepId;
2061 tStep.name = p->name();
2062 tStep.version = p->edition().version();
2063 tStep.release = p->edition().release();
2064 tStep.arch = p->arch().asString();
2065 commit.transactionSteps.push_back(std::move(tStep));
2066
2067 }
2068 } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
2069 // SrcPackage is install-only
2070 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
2071
2072 try {
2073 // provide on local disk
2074 locCache.value()[stepId] = provideSrcPackage( p );
2075
2076 proto::target::InstallStep tStep;
2077 tStep.stepId = stepId;
2078 tStep.pathname = locCache.value()[stepId]->asString();
2079 tStep.multiversion = false;
2080 commit.transactionSteps.push_back(std::move(tStep));
2081
2082 } catch ( const Exception &e ) {
2083 ZYPP_CAUGHT( e );
2084 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2085 step.stepStage( sat::Transaction::STEP_ERROR );
2086 continue;
2087 }
2088 }
2089 }
2090
2091 std::vector<sat::Solvable> successfullyInstalledPackages;
2092
2093 if ( commit.transactionSteps.size() ) {
2094
2095 // create the event loop early
2096 auto loop = zyppng::EventLoop::create();
2097
2098 attemptToModify();
2099
2100 const std::vector<int> interceptedSignals {
2101 SIGINT,
2102 SIGTERM,
2103 SIGHUP,
2104 SIGQUIT
2105 };
2106
2107 auto unixSignals = loop->eventDispatcher()->unixSignalSource();
2108 unixSignals->sigReceived ().connect ([]( int signum ){
2109 // translator: %1% is the received unix signal name, %2% is the numerical value of the received signal
2110 JobReport::error ( str::Format(_("Received signal :\"%1% (%2%)\", to ensure the consistency of the system it is not possible to cancel a running rpm transaction.") ) % strsignal(signum) % signum );
2111 });
2112 for( const auto &sig : interceptedSignals )
2113 unixSignals->addSignal ( sig );
2114
2115 Deferred cleanupSigs([&](){
2116 for( const auto &sig : interceptedSignals )
2117 unixSignals->removeSignal ( sig );
2118 });
2119
2120 // transaction related variables:
2121 //
2122 // the index of the step in the transaction list that we currenty execute.
2123 // this can be -1
2124 int currentStepId = -1;
2125
2126 // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
2127 // the script fd, once we receive it we set this flag to true and ignore all output
2128 // that is written to the pipe ( aside from buffering it ) until we finalize the current report
2129 // and start a new one
2130 bool gotEndOfScript = false;
2131
2132 // the possible reports we emit during the transaction
2133 std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
2134 std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2135 std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2136 std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2137 std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2138
2139 // this will be set if we receive a transaction error description
2140 std::optional<proto::target::TransactionError> transactionError;
2141
2142 // infos about the currently executed script, empty if no script is currently executed
2143 std::string currentScriptType;
2144 std::string currentScriptPackage;
2145
2146 // buffer to collect rpm output per report, this will be written to the log once the
2147 // report ends
2148 std::string rpmmsg;
2149
2150 // maximum number of lines that we are buffering in rpmmsg
2151 constexpr auto MAXRPMMESSAGELINES = 10000;
2152
2153 // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2154 unsigned lineno = 0;
2155
2156 // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2157 auto msgSource = zyppng::AsyncDataSource::create();
2158 auto scriptSource = zyppng::AsyncDataSource::create();
2159
2160 // this will be the communication channel, will be created once the process starts and
2161 // we can receive data
2162 zyppng::StompFrameStreamRef msgStream;
2163
2164
2165 // helper function that sends RPM output to the currently active report, writing a warning to the log
2166 // if there is none
2167 const auto &sendRpmLineToReport = [&]( const std::string &line ){
2168
2169 const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2170 callback::UserData cmdout(cType);
2171 if ( currentStepId >= 0 )
2172 cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2173 cmdout.set( "line", line );
2174 report->report(cmdout);
2175 };
2176
2177 if ( installreport ) {
2178 sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2179 } else if ( uninstallreport ) {
2180 sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2181 } else if ( scriptreport ) {
2182 sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2183 } else if ( transactionreport ) {
2184 sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2185 } else if ( cleanupreport ) {
2186 sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2187 } else {
2188 WAR << "Got rpm output without active report " << line; // no endl! - readLine does not trim
2189 }
2190
2191 // remember rpm output
2192 if ( lineno >= MAXRPMMESSAGELINES ) {
2193 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2194 return;
2195 }
2196 rpmmsg += line;
2197 if ( line.back() != '\n' )
2198 rpmmsg += '\n';
2199 };
2200
2201
2202 // callback and helper function to process data that is received on the script FD
2203 const auto &processDataFromScriptFd = [&](){
2204
2205 while ( scriptSource->canReadLine() ) {
2206
2207 if ( gotEndOfScript )
2208 return;
2209
2210 std::string l = scriptSource->readLine().asString();
2211 if( str::endsWith( l, endOfScriptTag ) ) {
2212 gotEndOfScript = true;
2213 std::string::size_type rawsize { l.size() - endOfScriptTag.size() };
2214 if ( not rawsize )
2215 return;
2216 l = l.substr( 0, rawsize );
2217 }
2218 L_DBG("zypp-rpm") << "[rpm> " << l; // no endl! - readLine does not trim
2219 sendRpmLineToReport( l );
2220 }
2221 };
2222 scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2223
2224 // helper function that just waits until the end of script tag was received on the scriptSource
2225 const auto &waitForScriptEnd = [&]() {
2226
2227 // nothing to wait for
2228 if ( gotEndOfScript )
2229 return;
2230
2231 // we process all available data
2232 processDataFromScriptFd();
2233
2234 // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2235 while ( scriptSource->readFdOpen() && scriptSource->canRead() && !gotEndOfScript ) {
2236 // readyRead will trigger processDataFromScriptFd so no need to call it again
2237 // we still got nothing, lets wait for more
2238 scriptSource->waitForReadyRead( 100 );
2239 }
2240 };
2241
2242 const auto &aboutToStartNewReport = [&](){
2243
2244 if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2245 ERR << "There is still a running report, this is a bug" << std::endl;
2246 assert(false);
2247 }
2248
2249 gotEndOfScript = false;
2250 };
2251
2252 const auto &writeRpmMsgToHistory = [&](){
2253 if ( rpmmsg.size() == 0 )
2254 return;
2255
2256 if ( lineno >= MAXRPMMESSAGELINES )
2257 rpmmsg += "[truncated]\n";
2258
2259 std::ostringstream sstr;
2260 sstr << "rpm output:" << endl << rpmmsg << endl;
2261 HistoryLog().comment(sstr.str());
2262 };
2263
2264 // helper function that closes the current report and cleans up the ressources
2265 const auto &finalizeCurrentReport = [&]() {
2266 sat::Transaction::Step *step = nullptr;
2267 Resolvable::constPtr resObj;
2268 if ( currentStepId >= 0 ) {
2269 step = &steps.at(currentStepId);
2270 resObj = makeResObject( step->satSolvable() );
2271 }
2272
2273 if ( installreport ) {
2274 waitForScriptEnd();
2275 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2276
2278 str::form("%s install failed", step->ident().c_str()),
2279 true /*timestamp*/);
2280
2281 writeRpmMsgToHistory();
2282
2283 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2284 } else {
2285 ( *installreport)->progress( 100, resObj );
2286 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2287
2288 if ( currentStepId >= 0 )
2289 locCache.value().erase( currentStepId );
2290 successfullyInstalledPackages.push_back( step->satSolvable() );
2291
2292 PoolItem citem( *step );
2293 if ( !( flags & rpm::RPMINST_TEST ) ) {
2294 // @TODO are we really doing this just for install?
2295 if ( citem.isNeedreboot() ) {
2296 auto rebootNeededFile = root() / "/run/reboot-needed";
2297 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2298 filesystem::touch( rebootNeededFile );
2299 }
2301 HistoryLog().install(citem);
2302 }
2303
2305 str::form("%s installed ok", step->ident().c_str()),
2306 true /*timestamp*/);
2307
2308 writeRpmMsgToHistory();
2309 }
2310 }
2311 if ( uninstallreport ) {
2312 waitForScriptEnd();
2313 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2314
2316 str::form("%s uninstall failed", step->ident().c_str()),
2317 true /*timestamp*/);
2318
2319 writeRpmMsgToHistory();
2320
2321 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2322 } else {
2323 ( *uninstallreport)->progress( 100, resObj );
2324 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2325
2326 PoolItem citem( *step );
2327 HistoryLog().remove(citem);
2328
2330 str::form("%s removed ok", step->ident().c_str()),
2331 true /*timestamp*/);
2332
2333 writeRpmMsgToHistory();
2334 }
2335 }
2336 if ( scriptreport ) {
2337 waitForScriptEnd();
2338 ( *scriptreport)->progress( 100, resObj );
2339 ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2340 }
2341 if ( transactionreport ) {
2342 waitForScriptEnd();
2343 ( *transactionreport)->progress( 100 );
2344 ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2345 }
2346 if ( cleanupreport ) {
2347 waitForScriptEnd();
2348 ( *cleanupreport)->progress( 100 );
2349 ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2350 }
2351 currentStepId = -1;
2352 lineno = 0;
2353 rpmmsg.clear();
2354 currentScriptType.clear();
2355 currentScriptPackage.clear();
2356 installreport.reset();
2357 uninstallreport.reset();
2358 scriptreport.reset();
2359 transactionreport.reset();
2360 cleanupreport.reset();
2361 };
2362
2363 // This sets up the process and pushes the required transactions steps to it
2364 // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2365 //
2366 // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2367 // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2368 // 3) 2 writeable FDs that are set up by the parent Process when forking. The first FD is to be used for message sending, the second one for script output
2369
2370 constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2371
2372 const char *argv[] = {
2373 //"gdbserver",
2374 //"localhost:10001",
2375 zyppRpmBinary.data(),
2376 nullptr
2377 };
2378 auto prog = zyppng::Process::create();
2379
2380 // we set up a pipe to communicate with the process, it is too dangerous to use stdout since librpm
2381 // might print to it.
2382 auto messagePipe = zyppng::Pipe::create();
2383 if ( !messagePipe )
2384 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2385
2386 // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2387 // way than a FD to redirect that output
2388 auto scriptPipe = zyppng::Pipe::create();
2389 if ( !scriptPipe )
2390 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2391
2392 prog->addFd( messagePipe->writeFd );
2393 prog->addFd( scriptPipe->writeFd );
2394
2395 // set up the AsyncDataSource to read script output
2396 if ( !scriptSource->openFds( std::vector<int>{ scriptPipe->readFd } ) )
2397 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2398
2399 const auto &processMessages = [&] ( ) {
2400
2401 // lambda function that parses the passed message type and checks if the stepId is a valid offset
2402 // in the steps list.
2403 const auto &checkMsgWithStepId = [&steps]( auto &p ){
2404 if ( !p ) {
2405 ERR << "Failed to parse message from zypp-rpm." << std::endl;
2406 return false;
2407 }
2408
2409 auto id = p->stepId;
2410 if ( id < 0 || id >= steps.size() ) {
2411 ERR << "Received invalid stepId: " << id << " in " << p->typeName << " message from zypp-rpm, ignoring." << std::endl;
2412 return false;
2413 }
2414 return true;
2415 };
2416
2417 while ( const auto &m = msgStream->nextMessage() ) {
2418
2419 // due to librpm behaviour we need to make sense of the order of messages we receive
2420 // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2421 // Script related messages. What we do is remember the current step we are in and only close
2422 // the step when we get the start of the next one
2423 const auto &mName = m->command();
2424 if ( mName == proto::target::RpmLog::typeName ) {
2425
2426 const auto &p = proto::target::RpmLog::fromStompMessage (*m);
2427 if ( !p ) {
2428 ERR << "Failed to parse " << proto::target::RpmLog::typeName << " message from zypp-rpm." << std::endl;
2429 continue;
2430 }
2431 ( p->level >= RPMLOG_ERR ? L_ERR("zypp-rpm")
2432 : p->level >= RPMLOG_WARNING ? L_WAR("zypp-rpm")
2433 : L_DBG("zypp-rpm") ) << "[rpm " << p->level << "> " << p->line; // no endl! - readLine does not trim
2434 report.sendLoglineRpm( p->line, p->level );
2435
2436 } else if ( mName == proto::target::PackageBegin::typeName ) {
2437 finalizeCurrentReport();
2438
2439 const auto &p = proto::target::PackageBegin::fromStompMessage(*m);
2440 if ( !checkMsgWithStepId( p ) )
2441 continue;
2442
2443 aboutToStartNewReport();
2444
2445 auto & step = steps.at( p->stepId );
2446 currentStepId = p->stepId;
2447 if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2448 uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2449 ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2450 } else {
2451 installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2452 ( *installreport )->start( makeResObject( step.satSolvable() ) );
2453 }
2454
2455 } else if ( mName == proto::target::PackageFinished::typeName ) {
2456 const auto &p = proto::target::PackageFinished::fromStompMessage(*m);
2457 if ( !checkMsgWithStepId( p ) )
2458 continue;
2459
2460 // here we only set the step stage to done, we however need to wait for the next start in order to send
2461 // the finished report since there might be a error pending to be reported
2462 steps[ p->stepId ].stepStage( sat::Transaction::STEP_DONE );
2463
2464 } else if ( mName == proto::target::PackageProgress::typeName ) {
2465 const auto &p = proto::target::PackageProgress::fromStompMessage(*m);
2466 if ( !checkMsgWithStepId( p ) )
2467 continue;
2468
2469 if ( uninstallreport )
2470 (*uninstallreport)->progress( p->amount, makeResObject( steps.at( p->stepId ) ));
2471 else if ( installreport )
2472 (*installreport)->progress( p->amount, makeResObject( steps.at( p->stepId ) ));
2473 else
2474 ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2475
2476 } else if ( mName == proto::target::PackageError::typeName ) {
2477 const auto &p = proto::target::PackageError::fromStompMessage(*m);
2478 if ( !checkMsgWithStepId( p ) )
2479 continue;
2480
2481 if ( p->stepId >= 0 && p->stepId < steps.size() )
2482 steps[ p->stepId ].stepStage( sat::Transaction::STEP_ERROR );
2483
2484 finalizeCurrentReport();
2485
2486 } else if ( mName == proto::target::ScriptBegin::typeName ) {
2487 finalizeCurrentReport();
2488
2489 const auto &p = proto::target::ScriptBegin::fromStompMessage(*m);
2490 if ( !p ) {
2491 ERR << "Failed to parse " << proto::target::ScriptBegin::typeName << " message from zypp-rpm." << std::endl;
2492 continue;
2493 }
2494
2495 aboutToStartNewReport();
2496
2497 Resolvable::constPtr resPtr;
2498 const auto stepId = p->stepId;
2499 if ( stepId >= 0 && stepId < steps.size() ) {
2500 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2501 }
2502
2503 currentStepId = p->stepId;
2504 scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2505 currentScriptType = p->scriptType;
2506 currentScriptPackage = p->scriptPackage;
2507 (*scriptreport)->start( currentScriptType, currentScriptPackage, resPtr );
2508
2509 } else if ( mName == proto::target::ScriptFinished::typeName ) {
2510
2511 // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2512
2513 } else if ( mName == proto::target::ScriptError::typeName ) {
2514
2515 const auto &p = proto::target::ScriptError::fromStompMessage(*m);
2516 if ( !p ) {
2517 ERR << "Failed to parse " << proto::target::ScriptError::typeName << " message from zypp-rpm." << std::endl;
2518 continue;
2519 }
2520
2521 Resolvable::constPtr resPtr;
2522 const auto stepId = p->stepId;
2523 if ( stepId >= 0 && stepId < steps.size() ) {
2524 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2525
2526 if ( p->fatal ) {
2527 steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2528 }
2529
2530 }
2531
2533 str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2534 true /*timestamp*/);
2535
2536 writeRpmMsgToHistory();
2537
2538 if ( !scriptreport ) {
2539 ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2540 continue;
2541 }
2542
2543 // before killing the report we need to wait for the script end tag
2544 waitForScriptEnd();
2545 (*scriptreport)->finish( resPtr, p->fatal ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2546
2547 // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2548 scriptreport.reset();
2549 currentStepId = -1;
2550
2551 } else if ( mName == proto::target::CleanupBegin::typeName ) {
2552 finalizeCurrentReport();
2553
2554 const auto &beg = proto::target::CleanupBegin::fromStompMessage(*m);
2555 if ( !beg ) {
2556 ERR << "Failed to parse " << proto::target::CleanupBegin::typeName << " message from zypp-rpm." << std::endl;
2557 continue;
2558 }
2559
2560 aboutToStartNewReport();
2561 cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2562 (*cleanupreport)->start( beg->nvra );
2563 } else if ( mName == proto::target::CleanupFinished::typeName ) {
2564
2565 finalizeCurrentReport();
2566
2567 } else if ( mName == proto::target::CleanupProgress::typeName ) {
2568 const auto &prog = proto::target::CleanupProgress::fromStompMessage(*m);
2569 if ( !prog ) {
2570 ERR << "Failed to parse " << proto::target::CleanupProgress::typeName << " message from zypp-rpm." << std::endl;
2571 continue;
2572 }
2573
2574 if ( !cleanupreport ) {
2575 ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2576 continue;
2577 }
2578
2579 (*cleanupreport)->progress( prog->amount );
2580
2581 } else if ( mName == proto::target::TransBegin::typeName ) {
2582 finalizeCurrentReport();
2583
2584 const auto &beg = proto::target::TransBegin::fromStompMessage(*m);
2585 if ( !beg ) {
2586 ERR << "Failed to parse " << proto::target::TransBegin::typeName << " message from zypp-rpm." << std::endl;
2587 continue;
2588 }
2589
2590 aboutToStartNewReport();
2591 transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2592 (*transactionreport)->start( beg->name );
2593 } else if ( mName == proto::target::TransFinished::typeName ) {
2594
2595 finalizeCurrentReport();
2596
2597 } else if ( mName == proto::target::TransProgress::typeName ) {
2598 const auto &prog = proto::target::TransProgress::fromStompMessage(*m);
2599 if ( !prog ) {
2600 ERR << "Failed to parse " << proto::target::TransProgress::typeName << " message from zypp-rpm." << std::endl;
2601 continue;
2602 }
2603
2604 if ( !transactionreport ) {
2605 ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2606 continue;
2607 }
2608
2609 (*transactionreport)->progress( prog->amount );
2610 } else if ( mName == proto::target::TransactionError::typeName ) {
2611
2612 const auto &error = proto::target::TransactionError::fromStompMessage(*m);
2613 if ( !error ) {
2614 ERR << "Failed to parse " << proto::target::TransactionError::typeName << " message from zypp-rpm." << std::endl;
2615 continue;
2616 }
2617
2618 // this value is checked later
2619 transactionError = std::move(*error);
2620
2621 } else {
2622 ERR << "Received unexpected message from zypp-rpm: "<< m->command() << ", ignoring" << std::endl;
2623 return;
2624 }
2625
2626 }
2627 };
2628
2629 // setup the rest when zypp-rpm is running
2630 prog->sigStarted().connect( [&](){
2631
2632 // close the ends of the pipes we do not care about
2633 messagePipe->unrefWrite();
2634 scriptPipe->unrefWrite();
2635
2636 // read the stdout and stderr and forward it to our log
2637 prog->connectFunc( &zyppng::IODevice::sigChannelReadyRead, [&]( int channel ){
2638 while( prog->canReadLine( channel ) ) {
2639 L_ERR("zypp-rpm") << ( channel == zyppng::Process::StdOut ? "<stdout> " : "<stderr> " ) << prog->channelReadLine( channel ).asStringView(); // no endl! - readLine does not trim
2640 }
2641 });
2642
2643 // this is the source for control messages from zypp-rpm , we will get structured data information
2644 // in form of STOMP messages
2645 if ( !msgSource->openFds( std::vector<int>{ messagePipe->readFd }, prog->stdinFd() ) )
2646 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2647
2648 msgStream = zyppng::StompFrameStream::create(msgSource);
2649 msgStream->connectFunc( &zyppng::StompFrameStream::sigMessageReceived, processMessages );
2650
2651 const auto &msg = commit.toStompMessage();
2652 if ( !msg )
2653 std::rethrow_exception ( msg.error() );
2654
2655 if ( !msgStream->sendMessage( *msg ) ) {
2656 prog->stop( SIGKILL );
2657 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2658 }
2659 });
2660
2661 // track the childs lifetime
2662 int zyppRpmExitCode = -1;
2663 prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2664 zyppRpmExitCode = code;
2665 loop->quit();
2666 });
2667
2668 if ( !prog->start( argv ) ) {
2669 HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2670 ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2671 }
2672
2673 loop->run();
2674
2675 if ( msgStream ) {
2676 // pull all messages from the IO device
2677 msgStream->readAllMessages();
2678
2679 // make sure to read ALL available messages
2680 processMessages();
2681 }
2682
2683 // we will not receive a new start message , so we need to manually finalize the last report
2684 finalizeCurrentReport();
2685
2686 // make sure to read all data from the log source
2687 bool readMsgs = false;
2688 while( prog->canReadLine( zyppng::Process::StdErr ) ) {
2689 readMsgs = true;
2690 MIL << "zypp-rpm: " << prog->channelReadLine( zyppng::Process::StdErr ).asStringView();
2691 }
2692 while( prog->canReadLine( zyppng::Process::StdOut ) ) {
2693 readMsgs = true;
2694 MIL << "zypp-rpm: " << prog->channelReadLine( zyppng::Process::StdOut ).asStringView();
2695 }
2696
2697 while ( scriptSource->canReadLine() ) {
2698 readMsgs = true;
2699 MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2700 }
2701 if ( scriptSource->bytesAvailable() > 0 ) {
2702 readMsgs = true;
2703 MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2704 }
2705 if ( readMsgs )
2706 MIL << std::endl;
2707
2708 switch ( zyppRpmExitCode ) {
2709 // we need to look at the summary, handle finishedwitherrors like no error here
2710 case zypprpm::NoError:
2711 case zypprpm::RpmFinishedWithError:
2712 break;
2713 case zypprpm::RpmFinishedWithTransactionError: {
2714 // here zypp-rpm sent us a error description
2715 if ( transactionError ) {
2716
2717 std::ostringstream sstr;
2718 sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2719 for ( const auto & err : transactionError->problems ) {
2720 sstr << " " << err << "\n";
2721 }
2722 sstr << std::endl;
2724
2725 } else {
2726 ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more information.") );
2727 }
2728 break;
2729 }
2730 case zypprpm::FailedToOpenDb:
2731 ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2732 break;
2733 case zypprpm::WrongHeaderSize:
2734 case zypprpm::WrongMessageFormat:
2735 ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2736 break;
2737 case zypprpm::RpmInitFailed:
2738 ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2739 break;
2740 case zypprpm::FailedToReadPackage:
2741 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more information.") );
2742 break;
2743 case zypprpm::FailedToAddStepToTransaction:
2744 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more information.") );
2745 break;
2746 case zypprpm::RpmOrderFailed:
2747 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to order the transaction, check the logs for more information.") );
2748 break;
2749 case zypprpm::FailedToCreateLock:
2750 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to create its lockfile, check the logs for more information.") );
2751 break;
2752 }
2753
2754 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort; ++stepId ) {
2755 auto &step = steps[stepId];
2756 PoolItem citem( step );
2757
2758 if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2759 // other resolvables (non-Package) that are not handled by zypp-rpm
2760 if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2761 // Status is changed as the buddy package buddy
2762 // gets installed/deleted. Handle non-buddies only.
2763 if ( ! citem.buddy() && citem->isKind<Product>() ) {
2764 Product::constPtr p = citem->asKind<Product>();
2765
2766 if ( citem.status().isToBeInstalled() ) {
2767 ERR << "Can't install orphan product without release-package! " << citem << endl;
2768 } else {
2769 // Deleting the corresponding product entry is all we con do.
2770 // So the product will no longer be visible as installed.
2771 std::string referenceFilename( p->referenceFilename() );
2772
2773 if ( referenceFilename.empty() ) {
2774 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2775 } else {
2776 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2777
2778 if ( ! rpm().hasFile( referencePath.asString() ) ) {
2779 // If it's not owned by a package, we can delete it.
2780 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2781 if ( filesystem::unlink( referencePath ) != 0 )
2782 ERR << "Delete orphan product failed: " << referencePath << endl;
2783 } else {
2784 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2785 }
2786 }
2787 }
2789 step.stepStage( sat::Transaction::STEP_DONE );
2790 }
2791 }
2792 }
2793 }
2794 }
2795
2796 // Check presence of update scripts/messages. If aborting,
2797 // at least log omitted scripts.
2798 if ( ! successfullyInstalledPackages.empty() )
2799 {
2800 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2801 successfullyInstalledPackages, abort ) )
2802 {
2803 WAR << "Commit aborted by the user" << endl;
2804 abort = true;
2805 }
2806 // send messages after scripts in case some script generates output,
2807 // that should be kept in t %ghost message file.
2808 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2809 successfullyInstalledPackages,
2810 result_r );
2811 }
2812
2813 // jsc#SLE-5116: Log patch status changes to history
2814 // NOTE: Should be the last action as it may need to reload
2815 // the Target in case of an incomplete transaction.
2816 logPatchStatusChanges( result_r.transaction(), *this );
2817
2818 if ( abort ) {
2819 HistoryLog().comment( "Commit was aborted." );
2821 }
2822 }
2823
2825
2827 {
2828 return _rpm;
2829 }
2830
2831 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2832 {
2833 return _rpm.hasFile(path_str, name_str);
2834 }
2835
2837 namespace
2838 {
2839 parser::ProductFileData baseproductdata( const Pathname & root_r )
2840 {
2842 PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2843
2844 if ( baseproduct.isFile() )
2845 {
2846 try
2847 {
2848 ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2849 }
2850 catch ( const Exception & excpt )
2851 {
2852 ZYPP_CAUGHT( excpt );
2853 }
2854 }
2855 else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
2856 {
2857 ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
2858 }
2859 return ret;
2860 }
2861
2862 inline Pathname staticGuessRoot( const Pathname & root_r )
2863 {
2864 if ( root_r.empty() )
2865 {
2866 // empty root: use existing Target or assume "/"
2867 Pathname ret ( ZConfig::instance().systemRoot() );
2868 if ( ret.empty() )
2869 return Pathname("/");
2870 return ret;
2871 }
2872 return root_r;
2873 }
2874
2875 inline std::string firstNonEmptyLineIn( const Pathname & file_r )
2876 {
2877 std::ifstream idfile( file_r.c_str() );
2878 for( iostr::EachLine in( idfile ); in; in.next() )
2879 {
2880 std::string line( str::trim( *in ) );
2881 if ( ! line.empty() )
2882 return line;
2883 }
2884 return std::string();
2885 }
2886 } // namespace
2888
2890 {
2891 ResPool pool(ResPool::instance());
2892 for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
2893 {
2894 Product::constPtr p = (*it)->asKind<Product>();
2895 if ( p->isTargetDistribution() )
2896 return p;
2897 }
2898 return nullptr;
2899 }
2900
2902 {
2903 const Pathname needroot( staticGuessRoot(root_r) );
2904 const Target_constPtr target( getZYpp()->getTarget() );
2905 if ( target && target->root() == needroot )
2906 return target->requestedLocales();
2907 return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
2908 }
2909
2911 {
2912 MIL << "updateAutoInstalled if changed..." << endl;
2913 SolvIdentFile::Data newdata;
2914 for ( auto id : sat::Pool::instance().autoInstalled() )
2915 newdata.insert( IdString(id) ); // explicit ctor!
2916 _autoInstalledFile.setData( std::move(newdata) );
2917 }
2918
2920 { return baseproductdata( _root ).registerTarget(); }
2921 // static version:
2922 std::string TargetImpl::targetDistribution( const Pathname & root_r )
2923 { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
2924
2926 { return baseproductdata( _root ).registerRelease(); }
2927 // static version:
2929 { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
2930
2932 { return baseproductdata( _root ).registerFlavor(); }
2933 // static version:
2935 { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
2936
2938 {
2940 parser::ProductFileData pdata( baseproductdata( _root ) );
2941 ret.shortName = pdata.shortName();
2942 ret.summary = pdata.summary();
2943 return ret;
2944 }
2945 // static version:
2947 {
2949 parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
2950 ret.shortName = pdata.shortName();
2951 ret.summary = pdata.summary();
2952 return ret;
2953 }
2954
2956 {
2957 if ( _distributionVersion.empty() )
2958 {
2960 if ( !_distributionVersion.empty() )
2961 MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
2962 }
2963 return _distributionVersion;
2964 }
2965 // static version
2966 std::string TargetImpl::distributionVersion( const Pathname & root_r )
2967 {
2968 std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
2969 if ( distributionVersion.empty() )
2970 {
2971 // ...But the baseproduct method is not expected to work on RedHat derivatives.
2972 // On RHEL, Fedora and others the "product version" is determined by the first package
2973 // providing 'system-release'. This value is not hardcoded in YUM and can be configured
2974 // with the $distroverpkg variable.
2975 scoped_ptr<rpm::RpmDb> tmprpmdb;
2976 if ( ZConfig::instance().systemRoot() == Pathname() )
2977 {
2978 try
2979 {
2980 tmprpmdb.reset( new rpm::RpmDb );
2981 tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
2982 }
2983 catch( ... )
2984 {
2985 return "";
2986 }
2987 }
2990 distributionVersion = it->tag_version();
2991 }
2992 return distributionVersion;
2993 }
2994
2995
2997 {
2998 return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
2999 }
3000 // static version:
3001 std::string TargetImpl::distributionFlavor( const Pathname & root_r )
3002 {
3003 return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
3004 }
3005
3007 namespace
3008 {
3009 std::string guessAnonymousUniqueId( const Pathname & root_r )
3010 {
3011 // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
3012 std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
3013 if ( ret.empty() && root_r != "/" )
3014 {
3015 // if it has nonoe, use the outer systems one
3016 ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
3017 }
3018 return ret;
3019 }
3020 }
3021
3023 {
3024 return guessAnonymousUniqueId( root() );
3025 }
3026 // static version:
3027 std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
3028 {
3029 return guessAnonymousUniqueId( staticGuessRoot(root_r) );
3030 }
3031
3033
3035 {
3036 MIL << "New VendorAttr: " << vendorAttr_r << endl;
3037 _vendorAttr = std::move(vendorAttr_r);
3038 }
3040
3041 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3042 {
3043 // provide on local disk
3044 ManagedFile localfile = provideSrcPackage(srcPackage_r);
3045 // create a installation progress report proxy
3046 RpmInstallPackageReceiver progress( srcPackage_r );
3047 progress.connect(); // disconnected on destruction.
3048 // install it
3049 rpm().installPackage ( localfile );
3050 }
3051
3052 ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3053 {
3054 // provide on local disk
3055 repo::RepoMediaAccess access_r;
3056 repo::SrcPackageProvider prov( access_r );
3057 return prov.provideSrcPackage( srcPackage_r );
3058 }
3060 } // namespace target
3063} // namespace zypp
#define idstr(V)
#define MAXRPMMESSAGELINES
Definition RpmDb.cc:65
Pathname _mountpoint
#define SUBST_IF(PAT, VAL)
ZYppCommitResult & _result
TrueBool _guard
Architecture.
Definition Arch.h:37
const std::string & asString() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Arch.cc:499
bool compatibleWith(const Arch &targetArch_r) const
Compatibility relation.
Definition Arch.cc:515
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
reference value() const
Reference to the Tp object.
void resetDispose()
Set no dispose function.
A sat capability.
Definition Capability.h:63
Mime type like 'type/subtype' classification of content.
Definition ContentType.h:30
Store and operate on date (time_t).
Definition Date.h:33
static Date now()
Return the current time.
Definition Date.h:78
Edition represents [epoch:]version[-release]
Definition Edition.h:61
std::string version() const
Version.
Definition Edition.cc:94
unsigned int epoch_t
Type of an epoch.
Definition Edition.h:64
std::string release() const
Release.
Definition Edition.cc:110
epoch_t epoch() const
Epoch.
Definition Edition.cc:82
Base class for Exception.
Definition Exception.h:147
void remember(const Exception &old_r)
Store an other Exception as history.
Definition Exception.cc:141
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
int close() override
Wait for the progamm to complete.
const std::string & command() const
The command we're executing.
std::vector< std::string > Arguments
Writing the zypp history file.
Definition HistoryLog.h:57
void stampCommand()
Log info about the current process.
static void setRoot(const Pathname &root)
Set new root directory to the default history log file path.
void remove(const PoolItem &pi)
Log removal of a package.
static const Pathname & fname()
Get the current log file path.
void install(const PoolItem &pi)
Log installation (or update) of a package.
void comment(const std::string &comment, bool timestamp=false)
Log a comment (even multiline).
Access to the sat-pools string space.
Definition IdString.h:44
const char * c_str() const
Conversion to const char *
Definition IdString.cc:50
std::string asString() const
Conversion to std::string
Definition IdString.h:99
@ REGEX
Regular Expression.
Definition StrMatcher.h:48
Package interface.
Definition Package.h:34
TraitsType::constPtrType constPtr
Definition Package.h:39
Class representing a patch.
Definition Patch.h:38
TraitsType::constPtrType constPtr
Definition Patch.h:43
Parallel execution of stateful PluginScripts.
void load(const Pathname &path_r)
Find and launch plugins sending PLUGINBEGIN.
void send(const PluginFrame &frame_r)
Send PluginFrame to all open plugins.
Command frame for communication with PluginScript.
Definition PluginFrame.h:42
Combining sat::Solvable and ResStatus.
Definition PoolItem.h:51
ResObject::constPtr resolvable() const
Returns the ResObject::constPtr.
Definition PoolItem.cc:227
ResStatus & status() const
Returns the current status.
Definition PoolItem.cc:212
sat::Solvable buddy() const
Return the buddy we share our status object with.
Definition PoolItem.cc:215
Product interface.
Definition Product.h:34
TraitsType::constPtrType constPtr
Definition Product.h:39
Track changing files or directories.
Definition RepoStatus.h:41
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
bool solvablesEmpty() const
Whether Repository contains solvables.
SolvableIterator solvablesEnd() const
Iterator behind the last Solvable.
SolvableIterator solvablesBegin() const
Iterator to the first Solvable.
size_type solvablesSize() const
Number of solvables in Repository.
void addSolv(const Pathname &file_r)
Load Solvables from a solv-file.
void eraseFromPool()
Remove this Repository from its Pool.
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from the established one.
Definition PoolImpl.cc:26
Global ResObject pool.
Definition ResPool.h:62
static ResPool instance()
Singleton ctor.
Definition ResPool.cc:38
void setHardLockQueries(const HardLockQueries &newLocks_r)
Set a new set of queries.
Definition ResPool.cc:104
Resolver & resolver() const
The Resolver.
Definition ResPool.cc:62
const LocaleSet & getRequestedLocales() const
Return the requested locales.
Definition ResPool.cc:131
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from their initial one.
Definition ResPool.h:350
byKind_iterator byKindEnd(const ResKind &kind_r) const
Definition ResPool.h:269
EstablishedStates establishedStates() const
Factory for EstablishedStates.
Definition ResPool.cc:77
byKind_iterator byKindBegin(const ResKind &kind_r) const
Definition ResPool.h:262
void getHardLockQueries(HardLockQueries &activeLocks_r)
Suggest a new set of queries based on the current selection.
Definition ResPool.cc:107
EstablishedStates::ChangedPseudoInstalled ChangedPseudoInstalled
Map holding pseudo installed items where current and established status differ.
Definition ResPool.h:342
bool isToBeInstalled() const
Definition ResStatus.h:259
bool resetTransact(TransactByValue causer_r)
Not the same as setTransact( false ).
Definition ResStatus.h:490
TraitsType::constPtrType constPtr
Definition Resolvable.h:59
sat::Transaction getTransaction()
Return the Transaction computed by the last solver run.
Definition Resolver.cc:77
bool upgradeMode() const
Definition Resolver.cc:100
bool upgradingRepos() const
Whether there is at least one UpgradeRepo request pending.
Definition Resolver.cc:145
Attempts to create a lock to prevent the system from going into hibernate/shutdown.
SrcPackage interface.
Definition SrcPackage.h:30
TraitsType::constPtrType constPtr
Definition SrcPackage.h:36
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition StrMatcher.h:298
Definition of vendor equivalence.
Definition VendorAttr.h:61
Interim helper class to collect global options and settings.
Definition ZConfig.h:69
Arch systemArchitecture() const
The system architecture zypp uses.
Definition ZConfig.cc:977
bool apply_locks_file() const
Whether locks file should be read and applied after start (true)
Definition ZConfig.cc:1265
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:925
std::string distroverpkg() const
Package telling the "product version" on systems not using /etc/product.d/baseproduct.
Definition ZConfig.cc:1319
Options and policies for ZYpp::commit.
ZYppCommitPolicy & rpmInstFlags(target::rpm::RpmInstFlags newFlags_r)
The default target::rpm::RpmInstFlags.
bool singleTransModeEnabled() const
ZYppCommitPolicy & rpmExcludeDocs(bool yesNo_r)
Use rpm option –excludedocs (default: false)
ZYppCommitPolicy & dryRun(bool yesNo_r)
Set dry run (default: false).
ZYppCommitPolicy & restrictToMedia(unsigned mediaNr_r)
Restrict commit to media 1.
ZYppCommitPolicy & downloadMode(DownloadMode val_r)
Commit download policy to use.
ZYppCommitPolicy & allMedia()
Process all media (default)
ZYppCommitPolicy & rpmNoSignature(bool yesNo_r)
Use rpm option –nosignature (default: false)
Result returned from ZYpp::commit.
const Pathname & root() const
Remembered root directory of the target.
TransactionStepList & rTransactionStepList()
Manipulate transactionStepList.
void setSingleTransactionMode(bool yesno_r)
std::vector< sat::Transaction::Step > TransactionStepList
const sat::Transaction & transaction() const
The full transaction list.
sat::Transaction & rTransaction()
Manipulate transaction.
static zypp::Pathname lockfileDir()
Typesafe passing of user data via callbacks.
Definition UserData.h:40
bool set(const std::string &key_r, AnyType val_r)
Set the value for key (nonconst version always returns true).
Definition UserData.h:119
zypp::ContentType ContentType
Definition UserData.h:51
std::string receiveLine()
Read one line from the input stream.
Wrapper class for stat/lstat.
Definition PathInfo.h:222
bool isExist() const
Return whether valid stat info exists.
Definition PathInfo.h:282
Pathname dirname() const
Return all but the last component od this path.
Definition Pathname.h:126
const char * c_str() const
String representation.
Definition Pathname.h:112
const std::string & asString() const
String representation.
Definition Pathname.h:93
bool empty() const
Test for an empty path.
Definition Pathname.h:116
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r prefixed with root_r, unless it is already prefixed.
Definition Pathname.cc:272
Provide a new empty temporary file and delete it when no longer needed.
Definition TmpPath.h:128
static TmpFile makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition TmpPath.cc:220
Data returned by ProductFileReader.
bool empty() const
Whether this is an empty object without valid data.
static ProductFileData scanFile(const Pathname &file_r)
Parse one file (or symlink) and return the ProductFileData parsed.
Provides files from different repos.
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r) const
Provide SrcPackage in a local file.
Global sat-pool.
Definition Pool.h:47
Queue autoInstalled() const
Get ident list of all autoinstalled solvables.
Definition Pool.cc:264
static Pool instance()
Singleton ctor.
Definition Pool.h:55
static const std::string & systemRepoAlias()
Reserved system repository alias @System .
Definition Pool.cc:46
Libsolv Id queue wrapper.
Definition Queue.h:36
detail::IdType value_type
Definition Queue.h:39
void push(value_type val_r)
Push a value to the end off the Queue.
Definition Queue.cc:103
A Solvable object within the sat Pool.
Definition Solvable.h:54
A single step within a Transaction.
StepType stepType() const
Type of action to perform in this step.
StepStage stepStage() const
Step action result.
Solvable satSolvable() const
Return the corresponding Solvable.
Libsolv transaction wrapper.
Definition Transaction.h:52
const_iterator end() const
Iterator behind the last TransactionStep.
StringQueue autoInstalled() const
Return the ident strings of all packages that would be auto-installed after the transaction is run.
const_iterator begin() const
Iterator to the first TransactionStep.
bool order()
Order transaction steps for commit.
@ TRANSACTION_MULTIINSTALL
[M] Install(multiversion) item (
Definition Transaction.h:67
@ TRANSACTION_INSTALL
[+] Install(update) item
Definition Transaction.h:66
@ TRANSACTION_IGNORE
[ ] Nothing (includes implicit deletes due to obsoletes and non-package actions)
Definition Transaction.h:64
@ TRANSACTION_ERASE
[-] Delete item
Definition Transaction.h:65
@ STEP_DONE
[OK] success
Definition Transaction.h:74
@ STEP_TODO
[__] unprocessed
Definition Transaction.h:73
Target::commit helper optimizing package provision.
void setCommitList(std::vector< sat::Solvable > commitList_r)
Download(commit) sequence of solvables to compute read ahead.
bool preloaded() const
Whether preloaded hint is set.
ManagedFile get(const PoolItem &citem_r)
Provide a package.
void setData(const Data &data_r)
Store new Data.
const Data & data() const
Return the data.
pool::PoolTraits::HardLockQueries Data
Save and restore locale set from file.
const LocaleSet & locales() const
Return the loacale set.
void setLocales(const LocaleSet &locales_r)
Store a new locale set.
void tryLevel(target::rpm::InstallResolvableReport::RpmLevel level_r)
Extract and remember posttrans scripts for later execution.
void executeScripts(rpm::RpmDb &rpm_r)
Execute the remembered scripts and/or or dump_posttrans lines.
void discardScripts()
Discard all remembered scripts and/or or dump_posttrans lines.
bool aborted() const
Returns true if removing is aborted during progress.
const Data & data() const
Return the data.
std::unordered_set< IdString > Data
void setData(const Data &data_r)
Store new Data.
const Pathname & file() const
Return the file path.
Base class for concrete Target implementations.
Definition TargetImpl.h:54
std::string targetDistributionRelease() const
This is register.release attribute of the installed base product.
const VendorAttr & vendorAttr() const
The targets current vendor equivalence settings.
Definition TargetImpl.h:199
std::string targetDistribution() const
This is register.target attribute of the installed base product.
std::list< PoolItem > PoolItemList
list of pool items
Definition TargetImpl.h:59
LocaleSet requestedLocales() const
Languages to be supported by the system.
Definition TargetImpl.h:155
void updateAutoInstalled()
Update the database of autoinstalled packages.
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Provides a source package on the Target.
Pathname _root
Path to the target.
Definition TargetImpl.h:222
RequestedLocalesFile _requestedLocalesFile
Requested Locales database.
Definition TargetImpl.h:226
void createLastDistributionFlavorCache() const
generates a cache of the last product flavor
std::string _distributionVersion
Cache distributionVersion.
Definition TargetImpl.h:232
rpm::RpmDb _rpm
RPM database.
Definition TargetImpl.h:224
~TargetImpl() override
Dtor.
rpm::RpmDb & rpm()
The RPM database.
Pathname solvfilesPath() const
The solv file location actually in use (default or temp).
Definition TargetImpl.h:92
std::string distributionVersion() const
This is version attribute of the installed base product.
void createAnonymousId() const
generates the unique anonymous id which is called when creating the target
SolvIdentFile _autoInstalledFile
user/auto installed database
Definition TargetImpl.h:228
Product::constPtr baseProduct() const
returns the target base installed product, also known as the distribution or platform.
Target::DistributionLabel distributionLabel() const
This is shortName and summary attribute of the installed base product.
bool providesFile(const std::string &path_str, const std::string &name_str) const
If the package is installed and provides the file Needed to evaluate split provides during Resolver::...
HardLocksFile _hardLocksFile
Hard-Locks database.
Definition TargetImpl.h:230
Pathname root() const
The root set for this target.
Definition TargetImpl.h:116
void load(bool force=true)
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
void commitInSingleTransaction(const ZYppCommitPolicy &policy_r, CommitPackageCache &packageCache_r, ZYppCommitResult &result_r)
Commit ordered changes (internal helper)
void installSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Install a source package on the Target.
ZYppCommitResult commit(ResPool pool_r, const ZYppCommitPolicy &policy_r)
Commit changes in the pool.
VendorAttr _vendorAttr
vendor equivalence settings.
Definition TargetImpl.h:234
Pathname home() const
The directory to store things.
Definition TargetImpl.h:120
void commitFindFileConflicts(const ZYppCommitPolicy &policy_r, ZYppCommitResult &result_r)
Commit helper checking for file conflicts after download.
Pathname defaultSolvfilesPath() const
The systems default solv file location.
std::string anonymousUniqueId() const
anonymous unique id
TargetImpl(const Pathname &root_r="/", bool doRebuild_r=false)
Ctor.
bool solvfilesPathIsTemp() const
Whether we're using a temp.
Definition TargetImpl.h:96
std::string targetDistributionFlavor() const
This is register.flavor attribute of the installed base product.
Interface to the rpm program.
Definition RpmDb.h:50
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition RpmDb.cc:1664
void initDatabase(Pathname root_r=Pathname(), bool doRebuild_r=false)
Prepare access to the rpm database below root_r.
Definition RpmDb.cc:273
const Pathname & root() const
Definition RpmDb.h:91
void removePackage(const std::string &name_r, RpmInstFlags flags=RPMINST_NONE)
remove rpm package
Definition RpmDb.cc:1870
const Pathname & dbPath() const
Definition RpmDb.h:99
void closeDatabase()
Block further access to the rpm database and go back to uninitialized state.
Definition RpmDb.cc:363
bool hasFile(const std::string &file_r, const std::string &name_r="") const
Return true if at least one package owns a certain file (name_r empty) Return true if package name_r ...
Definition RpmDb.cc:974
Subclass to retrieve database content.
Definition librpmDb.h:344
bool findByProvides(const std::string &tag_r)
Reset to iterate all packages that provide a certain tag.
Definition librpmDb.cc:742
static Ptr create()
SignalProxy< void(uint)> sigChannelReadyRead()
Definition iodevice.cc:373
static Ptr create()
Definition process.cpp:49
SignalProxy< void(int)> sigFinished()
Definition process.cpp:294
SignalProxy< void()> sigMessageReceived()
static Ptr create(IODevice::Ptr iostr)
@ UNKNOWN
Definition richtext.cc:49
Namespace intended to collect all environment variables we use.
Definition Env.h:23
bool TRANSACTIONAL_UPDATE()
Definition TargetImpl.cc:84
int chmod(const Pathname &path, mode_t mode)
Like 'chmod'.
Definition PathInfo.cc:1097
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like 'symlink'.
Definition PathInfo.cc:860
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition PathInfo.cc:26
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
Definition PathInfo.cc:1191
int recursive_rmdir(const Pathname &path)
Like 'rm -r DIR'.
Definition PathInfo.cc:417
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:705
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition PathInfo.cc:610
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition PathInfo.cc:32
int addmod(const Pathname &path, mode_t mode)
Add the mode bits to the file given by path.
Definition PathInfo.cc:1109
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition PathInfo.cc:324
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like 'readlink'.
Definition PathInfo.cc:929
std::string md5sum(const Pathname &file)
Compute a files md5sum.
Definition PathInfo.cc:1029
int rename(const Pathname &oldpath, const Pathname &newpath)
Like 'rename'.
Definition PathInfo.cc:747
int touch(const Pathname &path)
Change file's modification and access times.
Definition PathInfo.cc:1242
std::string getline(std::istream &str)
Read one line from stream.
Definition IOStream.cc:33
std::string toJSON(void)
Definition Json.h:136
bool empty() const
Whether neither idents nor provides are set.
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition Pool.cc:286
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition String.h:1026
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition String.cc:178
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition String.h:1084
bool endsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasSuffix
Definition String.h:1091
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:37
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition String.h:429
unsigned splitEscaped(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", bool withEmpty=false)
Split line_r into words with respect to escape delimeters.
Definition String.h:594
std::string trim(const std::string &s, const Trim trim_r)
Definition String.cc:224
void XRunUpdateMessages(const Pathname &root_r, const Pathname &messagesPath_r, const std::vector< sat::Solvable > &checkPackages_r, ZYppCommitResult &result_r)
std::string rpmDbStateHash(const Pathname &root_r)
void writeUpgradeTestcase()
static bool fileMissing(const Pathname &pathname)
helper functor
void updateFileContent(const Pathname &filename, boost::function< bool()> condition, boost::function< std::string()> value)
updates the content of filename if condition is true, setting the content the the value returned by v...
RepoStatus rpmDbRepoStatus(const Pathname &root_r)
static std::string generateRandomId()
generates a random id using uuidgen
Easy-to use interface to the ZYPP dependency resolver.
std::unordered_set< Locale > LocaleSet
Definition Locale.h:29
std::list< UpdateNotificationFile > UpdateNotifications
ResTraits< TRes >::PtrType make(const sat::Solvable &solvable_r)
Directly create a certain kind of ResObject from sat::Solvable.
Definition ResObject.h:118
ResObject::Ptr makeResObject(const sat::Solvable &solvable_r)
Create ResObject from sat::Solvable.
Definition ResObject.cc:43
std::string asString(const Patch::Category &obj)
Definition Patch.cc:122
ResTraits< TRes >::PtrType asKind(const sat::Solvable &solvable_r)
Directly create a certain kind of ResObject from sat::Solvable.
Definition ResObject.h:127
@ DownloadInHeaps
Similar to DownloadInAdvance, but try to split the transaction into heaps, where at the end of each h...
@ DownloadOnly
Just download all packages to the local cache.
@ DownloadAsNeeded
Alternating download and install.
@ DownloadInAdvance
First download all packages to the local cache.
@ DownloadDefault
libzypp will decide what to do.
zypp::IdString IdString
Definition idstring.h:16
static bool error(const std::string &msg_r, const UserData &userData_r=UserData())
send error text
JSON array.
Definition Json.h:257
std::string asJSON() const
JSON representation.
Definition Json.h:279
void add(const Value &val_r)
Push JSON Value to Array.
Definition Json.h:271
JSON object.
Definition Json.h:322
void add(const String &key_r, const Value &val_r)
Add key/value pair.
Definition Json.h:336
std::string asJSON() const
JSON representation.
Definition Json.h:344
Solvable satSolvable() const
Return the corresponding sat::Solvable.
bool isNeedreboot() const
static PoolImpl & myPool()
Definition PoolImpl.cc:184
Convenient building of std::string with boost::format.
Definition String.h:253
Convenience SendReport<rpm::SingleTransReport> wrapper.
void report(const callback::UserData &userData_r)
void sendLoglineRpm(const std::string &line_r, unsigned rpmlevel_r)
Convenience to send a contentLogline translating a rpm loglevel.
void sendLogline(const std::string &line_r, ReportType::loglevel level_r=ReportType::loglevel::msg)
Convenience to send a contentLogline.
static std::optional< Pipe > create(int flags=0)
#define NON_COPYABLE(CLASS)
Delete copy ctor and copy assign.
Definition Easy.h:59
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:28
#define NON_MOVABLE(CLASS)
Delete move ctor and move assign.
Definition Easy.h:69
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:440
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:424
#define _(MSG)
Definition Gettext.h:39
#define L_ERR(GROUP)
Definition Logger.h:109
#define DBG
Definition Logger.h:97
#define MIL
Definition Logger.h:98
#define ERR
Definition Logger.h:100
#define L_WAR(GROUP)
Definition Logger.h:108
#define WAR
Definition Logger.h:99
#define L_DBG(GROUP)
Definition Logger.h:106
#define INT
Definition Logger.h:102
#define IMPL_PTR_TYPE(NAME)
Interface to gettext.