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