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