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 {
1093 _rpm.closeDatabase();
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 }
1275
1276 void TargetImpl::load( bool force )
1277 {
1278 bool newCache = buildCache();
1279 MIL << "New cache built: " << (newCache?"true":"false") <<
1280 ", force loading: " << (force?"true":"false") << endl;
1281
1282 // now add the repos to the pool
1283 sat::Pool satpool( sat::Pool::instance() );
1284 Pathname rpmsolv( solvfilesPath() / "solv" );
1285 MIL << "adding " << rpmsolv << " to pool(" << satpool.systemRepoAlias() << ")" << endl;
1286
1287 // Providing an empty system repo, unload any old content
1288 Repository system( sat::Pool::instance().findSystemRepo() );
1289
1290 bool systemFromSnapshot = false;
1291 if ( system && ! system.solvablesEmpty() )
1292 {
1293 if ( newCache || ( force && ! sat::Pool::snapshotMapped() ) )
1294 {
1295 system.eraseFromPool(); // invalidates system
1296 }
1297 else if ( ! _loaded )
1298 {
1299 // A mapped pool snapshot supplied the system repo and its cookie
1300 // asserted the rpmdb cache is unchanged, so there is nothing to
1301 // read. The settings below must still be applied to it.
1302 systemFromSnapshot = true;
1303 }
1304 else
1305 {
1306 return; // nothing to do
1307 }
1308 }
1309
1310 if ( ! system )
1311 {
1312 system = satpool.systemRepo();
1313 }
1314
1315 if ( ! systemFromSnapshot )
1316 {
1317 try
1318 {
1319 MIL << "adding " << rpmsolv << " to system" << endl;
1320 system.addSolv( rpmsolv );
1321 }
1322 catch ( const Exception & exp )
1323 {
1324 ZYPP_CAUGHT( exp );
1325 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
1326 clearCache();
1327 buildCache();
1328
1329 system.addSolv( rpmsolv );
1330 }
1331 }
1332 satpool.rootDir( _root );
1333
1334 // (Re)Load the requested locales et al.
1335 // If the requested locales are empty, we leave the pool untouched
1336 // to avoid undoing changes the application applied. We expect this
1337 // to happen on a bare metal installation only. An already existing
1338 // target should be loaded before its settings are changed.
1339 {
1341 if ( ! requestedLocales.empty() )
1342 {
1344 }
1345 }
1346 {
1347 if ( ! PathInfo( _autoInstalledFile.file() ).isExist() )
1348 {
1349 // Initialize from history, if it does not exist
1350 Pathname historyFile( Pathname::assertprefix( _root, ZConfig::instance().historyLogFile() ) );
1351 if ( PathInfo( historyFile ).isExist() )
1352 {
1353 SolvIdentFile::Data onSystemByUser( getUserInstalledFromHistory( historyFile ) );
1354 SolvIdentFile::Data onSystemByAuto;
1355 for_( it, system.solvablesBegin(), system.solvablesEnd() )
1356 {
1357 IdString ident( (*it).ident() );
1358 if ( onSystemByUser.find( ident ) == onSystemByUser.end() )
1359 onSystemByAuto.insert( ident );
1360 }
1361 _autoInstalledFile.setData( onSystemByAuto );
1362 }
1363 // on the fly removed any obsolete SoftLocks file
1364 filesystem::unlink( home() / "SoftLocks" );
1365 }
1366 // read from AutoInstalled file
1368 for ( const auto & idstr : _autoInstalledFile.data() )
1369 q.push( idstr.id() );
1370 satpool.setAutoInstalled( q );
1371 }
1372
1373 // Load the needreboot package specs
1374 {
1375 sat::SolvableSpec needrebootSpec;
1376 needrebootSpec.addProvides( Capability("installhint(reboot-needed)") );
1377 needrebootSpec.addProvides( Capability("kernel") );
1378
1379 Pathname needrebootFile { Pathname::assertprefix( root(), ZConfig::instance().needrebootFile() ) };
1380 if ( PathInfo( needrebootFile ).isFile() )
1381 needrebootSpec.parseFrom( needrebootFile );
1382
1383 Pathname needrebootDir { Pathname::assertprefix( root(), ZConfig::instance().needrebootPath() ) };
1384 if ( PathInfo( needrebootDir ).isDir() )
1385 {
1386 static const StrMatcher isRpmConfigBackup( "\\.rpm(new|save|orig)$", Match::REGEX );
1387
1389 [&]( const Pathname & dir_r, const char *const str_r )->bool
1390 {
1391 if ( ! isRpmConfigBackup( str_r ) )
1392 {
1393 Pathname needrebootFile { needrebootDir / str_r };
1394 if ( PathInfo( needrebootFile ).isFile() )
1395 needrebootSpec.parseFrom( needrebootFile );
1396 }
1397 return true;
1398 });
1399 }
1400 satpool.setNeedrebootSpec( std::move(needrebootSpec) );
1401 }
1402
1403 if ( ZConfig::instance().apply_locks_file() )
1404 {
1405 const HardLocksFile::Data & hardLocks( _hardLocksFile.data() );
1406 if ( ! hardLocks.empty() )
1407 {
1409 }
1410 }
1411
1412 // now that the target is loaded, we can cache the flavor
1414
1415 _loaded = true;
1416 MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl;
1417 }
1418
1420 //
1421 // COMMIT
1422 //
1425 {
1426 // ----------------------------------------------------------------- //
1427 ZYppCommitPolicy policy_r( policy_rX );
1428 bool explicitDryRun = policy_r.dryRun(); // explicit dry run will trigger a fileconflict check, implicit (download-only) not.
1429
1430 ShutdownLockCommit lck("zypp");
1431
1432 // Fake outstanding YCP fix: Honour restriction to media 1
1433 // at installation, but install all remaining packages if post-boot.
1434 if ( policy_r.restrictToMedia() > 1 )
1435 policy_r.allMedia();
1436
1437 if ( policy_r.downloadMode() == DownloadDefault ) {
1438 if ( root() == "/" )
1439 policy_r.downloadMode(DownloadInHeaps);
1440 else {
1441 if ( policy_r.singleTransModeEnabled() )
1443 else
1445 }
1446 }
1447 // DownloadOnly implies dry-run.
1448 else if ( policy_r.downloadMode() == DownloadOnly )
1449 policy_r.dryRun( true );
1450 // ----------------------------------------------------------------- //
1451
1452 MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
1453
1455 // Compute transaction:
1457 ZYppCommitResult result( root() );
1458 result.rTransaction() = pool_r.resolver().getTransaction();
1459 result.rTransaction().order();
1460 // steps: this is our todo-list
1462 if ( policy_r.restrictToMedia() )
1463 {
1464 // Collect until the 1st package from an unwanted media occurs.
1465 // Further collection could violate install order.
1466 MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
1467 for_( it, result.transaction().begin(), result.transaction().end() )
1468 {
1469 if ( makeResObject( *it )->mediaNr() > 1 )
1470 break;
1471 steps.push_back( *it );
1472 }
1473 }
1474 else
1475 {
1476 result.rTransactionStepList().insert( steps.end(), result.transaction().begin(), result.transaction().end() );
1477 }
1478
1479 MIL << "Todo: " << result << endl;
1480
1482 // Write out a testcase if we're in dist upgrade mode.
1484 if ( pool_r.resolver().upgradeMode() || pool_r.resolver().upgradingRepos() )
1485 {
1486 if ( ! policy_r.dryRun() )
1487 {
1489 }
1490 else
1491 {
1492 DBG << "dryRun: Not writing upgrade testcase." << endl;
1493 }
1494 }
1495
1497 // First collect and display all messages
1498 // associated with patches to be installed.
1500 if ( ! policy_r.dryRun() )
1501 {
1502 for_( it, steps.begin(), steps.end() )
1503 {
1504 if ( ! it->satSolvable().isKind<Patch>() )
1505 continue;
1506
1507 PoolItem pi( *it );
1508 if ( ! pi.status().isToBeInstalled() )
1509 continue;
1510
1512 if ( ! patch ||patch->message().empty() )
1513 continue;
1514
1515 MIL << "Show message for " << patch << endl;
1517 if ( ! report->show( patch ) )
1518 {
1519 WAR << "commit aborted by the user" << endl;
1521 }
1522 }
1523 }
1524 else
1525 {
1526 DBG << "dryRun: Not checking patch messages." << endl;
1527 }
1528
1530 // Remove/install packages.
1532
1533 DBG << "commit log file is set to: " << HistoryLog::fname() << endl;
1534 if ( ! policy_r.dryRun() || policy_r.downloadMode() == DownloadOnly )
1535 {
1536 // Prepare the package cache. Pass all items requiring download.
1537 CommitPackageCache packageCache;
1538 packageCache.setCommitList( steps.begin(), steps.end() );
1539
1540 bool miss = false;
1541 std::unique_ptr<CommitPackagePreloader> preloader;
1542 if ( policy_r.downloadMode() != DownloadAsNeeded )
1543 {
1544 {
1545 // concurrently preload the download cache as a workaround until we have
1546 // migration to full async workflows ready
1547 preloader = std::make_unique<CommitPackagePreloader>();
1548 preloader->preloadTransaction( steps );
1549 miss = preloader->missed ();
1550 }
1551
1552 if ( !miss ) {
1553 // Preload the cache. Until now this means pre-loading all packages.
1554 // Once DownloadInHeaps is fully implemented, this will change and
1555 // we may actually have more than one heap.
1556 for_( it, steps.begin(), steps.end() )
1557 {
1558 switch ( it->stepType() )
1559 {
1562 // proceed: only install actionas may require download.
1563 break;
1564
1565 default:
1566 // next: no download for or non-packages and delete actions.
1567 continue;
1568 break;
1569 }
1570
1571 PoolItem pi( *it );
1572 if ( pi->isKind<Package>() || pi->isKind<SrcPackage>() )
1573 {
1574 ManagedFile localfile;
1575 try
1576 {
1577 localfile = packageCache.get( pi );
1578 localfile.resetDispose(); // keep the package file in the cache
1579 }
1580 catch ( const AbortRequestException & exp )
1581 {
1582 it->stepStage( sat::Transaction::STEP_ERROR );
1583 miss = true;
1584 WAR << "commit cache preload aborted by the user" << endl;
1586 break;
1587 }
1588 catch ( const SkipRequestException & exp )
1589 {
1590 ZYPP_CAUGHT( exp );
1591 it->stepStage( sat::Transaction::STEP_ERROR );
1592 miss = true;
1593 WAR << "Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1594 continue;
1595 }
1596 catch ( const Exception & exp )
1597 {
1598 // bnc #395704: missing catch causes abort.
1599 // TODO see if packageCache fails to handle errors correctly.
1600 ZYPP_CAUGHT( exp );
1601 it->stepStage( sat::Transaction::STEP_ERROR );
1602 miss = true;
1603 INT << "Unexpected Error: Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1604 continue;
1605 }
1606 }
1607 }
1608 packageCache.preloaded( true ); // try to avoid duplicate infoInCache CBs in commit
1609 }
1610 }
1611
1612 if ( miss )
1613 {
1614 ERR << "Some packages could not be provided. Aborting commit."<< endl;
1615 }
1616 else
1617 {
1618 // Commit starts
1620 if ( ! commitActiveReport->start() )
1621 {
1622 WAR << "commit aborted: CommitActiveReport declined" << endl;
1624 }
1625
1627 // Prepare execution of commit plugins:
1629 PluginExecutor commitPlugins;
1630
1631 if ( ( root() == "/" || zypp::env::TRANSACTIONAL_UPDATE() ) && ! policy_r.dryRun() )
1632 {
1633 commitPlugins.load( ZConfig::instance().pluginsPath()/"commit" );
1634 }
1635 if ( commitPlugins )
1636 commitPlugins.send( transactionPluginFrame( "COMMITBEGIN", steps ) );
1637
1639 // Store non-package data:
1641 if ( ! policy_r.dryRun() )
1642 {
1644 // requested locales
1645 _requestedLocalesFile.setLocales( pool_r.getRequestedLocales() );
1646 // autoinstalled
1647 {
1648 SolvIdentFile::Data newdata;
1649 for ( sat::Queue::value_type id : result.rTransaction().autoInstalled() )
1650 newdata.insert( IdString(id) );
1651 _autoInstalledFile.setData( newdata );
1652 }
1653 // hard locks
1654 if ( ZConfig::instance().apply_locks_file() )
1655 {
1656 HardLocksFile::Data newdata;
1657 pool_r.getHardLockQueries( newdata );
1658 _hardLocksFile.setData( newdata );
1659 }
1660 }
1661 else
1662 {
1663 DBG << "dryRun: Not storing non-package data." << endl;
1664 }
1665
1666 if ( ! policy_r.dryRun() )
1667 {
1668 if ( policy_r.singleTransModeEnabled() ) {
1669 commitInSingleTransaction( policy_r, packageCache, result );
1670 } else {
1671 // if cache is preloaded, check for file conflicts
1672 commitFindFileConflicts( policy_r, result );
1673 commit( policy_r, packageCache, result );
1674 }
1675
1676 if ( preloader )
1677 preloader->cleanupCaches ();
1678 }
1679 else
1680 {
1681 DBG << "dryRun/downloadOnly: Not installing/deleting anything." << endl;
1682 if ( explicitDryRun ) {
1683 if ( policy_r.singleTransModeEnabled() ) {
1684 // single trans mode does a test install via rpm
1685 commitInSingleTransaction( policy_r, packageCache, result );
1686 } else {
1687 // if cache is preloaded, check for file conflicts
1688 commitFindFileConflicts( policy_r, result );
1689 }
1690 }
1691 }
1692
1694 // Send result to commit plugins:
1696 if ( commitPlugins )
1697 commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
1698
1700 // Try to rebuild solv file while rpm database is still in cache
1702 if ( ! policy_r.dryRun() )
1703 {
1704 buildCache();
1705 }
1706
1707 commitActiveReport->end();
1708 }
1709 }
1710 else
1711 {
1712 DBG << "dryRun: Not downloading/installing/deleting anything." << endl;
1713 if ( explicitDryRun ) {
1714 // if cache is preloaded, check for file conflicts
1715 commitFindFileConflicts( policy_r, result );
1716 }
1717 }
1718
1719 {
1720 // NOTE: Removing rpm in a transaction, rpm removes the /var/lib/rpm compat symlink.
1721 // We re-create it, in case it was lost to prevent legacy tools from accidentally
1722 // assuming no database is present.
1723 if ( ! PathInfo(_root/"/var/lib/rpm",PathInfo::LSTAT).isExist()
1724 && PathInfo(_root/"/usr/lib/sysimage/rpm").isDir() ) {
1725 WAR << "(rpm removed in commit?) Inject missing /var/lib/rpm compat symlink to /usr/lib/sysimage/rpm" << endl;
1726 filesystem::assert_dir( _root/"/var/lib" );
1727 filesystem::symlink( "../../usr/lib/sysimage/rpm", _root/"/var/lib/rpm" );
1728 }
1729 }
1730
1731 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
1732 return result;
1733 }
1734
1736 //
1737 // COMMIT internal
1738 //
1740 namespace
1741 {
1742 struct NotifyAttemptToModify
1743 {
1744 NotifyAttemptToModify( ZYppCommitResult & result_r ) : _result( result_r ) {}
1745
1746 void operator()()
1747 { if ( _guard ) { _result.attemptToModify( true ); _guard = false; } }
1748
1749 TrueBool _guard;
1750 ZYppCommitResult & _result;
1751 };
1752 } // namespace
1753
1754 void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
1755 CommitPackageCache & packageCache_r,
1756 ZYppCommitResult & result_r )
1757 {
1758 env::ScopedSet envguard[] __attribute__ ((__unused__)) {
1759 { "ZYPP_SINGLE_RPMTRANS", nullptr },
1760 { "ZYPP_CLASSIC_RPMTRANS", "1" },
1761 };
1762
1763 // steps: this is our todo-list
1765 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1766
1768
1769 // Send notification once upon 1st call to rpm
1770 NotifyAttemptToModify attemptToModify( result_r );
1771
1772 bool abort = false;
1773
1774 // bsc#1181328: Some systemd tools require /proc to be mounted
1775 AssertProcMounted assertProcMounted( _root );
1776 AssertDevMounted assertDevMounted( _root ); // also /dev
1777
1778 RpmPostTransCollector postTransCollector( _root );
1779 // bsc#1243279: %posttrans needs to know whether the package was installed or updated.
1780 // we collect the names of obsoleted packages. If %posttrans of an obsoleted package
1781 // was collected, it was an upadte.
1782 IdStringSet obsoletedPackages;
1783 std::vector<sat::Solvable> successfullyInstalledPackages;
1784 TargetImpl::PoolItemList remaining;
1785
1786 for_( step, steps.begin(), steps.end() )
1787 {
1788 PoolItem citem( *step );
1789 if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
1790 {
1791 if ( citem->isKind<Package>() )
1792 {
1793 // for packages this means being obsoleted (by rpm)
1794 // thus no additional action is needed.
1795 obsoletedPackages.insert( citem->ident() );
1796 step->stepStage( sat::Transaction::STEP_DONE );
1797 continue;
1798 }
1799 }
1800
1801 if ( citem->isKind<Package>() )
1802 {
1803 Package::constPtr p = citem->asKind<Package>();
1804 if ( citem.status().isToBeInstalled() )
1805 {
1806 ManagedFile localfile;
1807 try
1808 {
1809 localfile = packageCache_r.get( citem );
1810 }
1811 catch ( const AbortRequestException &e )
1812 {
1813 WAR << "commit aborted by the user" << endl;
1814 abort = true;
1815 step->stepStage( sat::Transaction::STEP_ERROR );
1816 break;
1817 }
1818 catch ( const SkipRequestException &e )
1819 {
1820 ZYPP_CAUGHT( e );
1821 WAR << "Skipping package " << p << " in commit" << endl;
1822 step->stepStage( sat::Transaction::STEP_ERROR );
1823 continue;
1824 }
1825 catch ( const Exception &e )
1826 {
1827 // bnc #395704: missing catch causes abort.
1828 // TODO see if packageCache fails to handle errors correctly.
1829 ZYPP_CAUGHT( e );
1830 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1831 step->stepStage( sat::Transaction::STEP_ERROR );
1832 continue;
1833 }
1834
1835 // create a installation progress report proxy
1836 RpmInstallPackageReceiver progress( citem.resolvable() );
1837 progress.connect(); // disconnected on destruction.
1838
1839 bool success = false;
1840 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1841 // Why force and nodeps?
1842 //
1843 // Because zypp builds the transaction and the resolver asserts that
1844 // everything is fine.
1845 // We use rpm just to unpack and register the package in the database.
1846 // We do this step by step, so rpm is not aware of the bigger context.
1847 // So we turn off rpms internal checks, because we do it inside zypp.
1848 flags |= rpm::RPMINST_NODEPS;
1849 flags |= rpm::RPMINST_FORCE;
1850 //
1851 if (p->multiversionInstall()) flags |= rpm::RPMINST_NOUPGRADE;
1852 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1853 if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
1854 if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
1855
1856 attemptToModify();
1857 try
1858 {
1860 rpm().installPackage( localfile, flags, &postTransCollector );
1861 HistoryLog().install(citem);
1862
1863 if ( progress.aborted() )
1864 {
1865 WAR << "commit aborted by the user" << endl;
1866 localfile.resetDispose(); // keep the package file in the cache
1867 abort = true;
1868 step->stepStage( sat::Transaction::STEP_ERROR );
1869 break;
1870 }
1871 else
1872 {
1873 if ( citem.isNeedreboot() ) {
1874 auto rebootNeededFile = root() / "/run/reboot-needed";
1875 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
1876 filesystem::touch( rebootNeededFile );
1877 }
1878
1879 success = true;
1880 step->stepStage( sat::Transaction::STEP_DONE );
1881 }
1882 }
1883 catch ( Exception & excpt_r )
1884 {
1885 ZYPP_CAUGHT(excpt_r);
1886 localfile.resetDispose(); // keep the package file in the cache
1887
1888 if ( policy_r.dryRun() )
1889 {
1890 WAR << "dry run failed" << endl;
1891 step->stepStage( sat::Transaction::STEP_ERROR );
1892 break;
1893 }
1894 // else
1895 if ( progress.aborted() )
1896 {
1897 WAR << "commit aborted by the user" << endl;
1898 abort = true;
1899 }
1900 else
1901 {
1902 WAR << "Install failed" << endl;
1903 }
1904 step->stepStage( sat::Transaction::STEP_ERROR );
1905 break; // stop
1906 }
1907
1908 if ( success && !policy_r.dryRun() )
1909 {
1911 successfullyInstalledPackages.push_back( citem.satSolvable() );
1912 step->stepStage( sat::Transaction::STEP_DONE );
1913 }
1914 }
1915 else
1916 {
1917 RpmRemovePackageReceiver progress( citem.resolvable() );
1918 progress.connect(); // disconnected on destruction.
1919
1920 bool success = false;
1921 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1922 flags |= rpm::RPMINST_NODEPS;
1923 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1924
1925 attemptToModify();
1926 try
1927 {
1928 rpm().removePackage( p, flags, &postTransCollector );
1929 HistoryLog().remove(citem);
1930
1931 if ( progress.aborted() )
1932 {
1933 WAR << "commit aborted by the user" << endl;
1934 abort = true;
1935 step->stepStage( sat::Transaction::STEP_ERROR );
1936 break;
1937 }
1938 else
1939 {
1940 success = true;
1941 step->stepStage( sat::Transaction::STEP_DONE );
1942 }
1943 }
1944 catch (Exception & excpt_r)
1945 {
1946 ZYPP_CAUGHT( excpt_r );
1947 if ( progress.aborted() )
1948 {
1949 WAR << "commit aborted by the user" << endl;
1950 abort = true;
1951 step->stepStage( sat::Transaction::STEP_ERROR );
1952 break;
1953 }
1954 // else
1955 WAR << "removal of " << p << " failed";
1956 step->stepStage( sat::Transaction::STEP_ERROR );
1957 }
1958 if ( success && !policy_r.dryRun() )
1959 {
1961 step->stepStage( sat::Transaction::STEP_DONE );
1962 }
1963 }
1964 }
1965 else if ( ! policy_r.dryRun() ) // other resolvables (non-Package)
1966 {
1967 // Status is changed as the buddy package buddy
1968 // gets installed/deleted. Handle non-buddies only.
1969 if ( ! citem.buddy() )
1970 {
1971 if ( citem->isKind<Product>() )
1972 {
1973 Product::constPtr p = citem->asKind<Product>();
1974 if ( citem.status().isToBeInstalled() )
1975 {
1976 ERR << "Can't install orphan product without release-package! " << citem << endl;
1977 }
1978 else
1979 {
1980 // Deleting the corresponding product entry is all we con do.
1981 // So the product will no longer be visible as installed.
1982 std::string referenceFilename( p->referenceFilename() );
1983 if ( referenceFilename.empty() )
1984 {
1985 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
1986 }
1987 else
1988 {
1989 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
1990 if ( ! rpm().hasFile( referencePath.asString() ) )
1991 {
1992 // If it's not owned by a package, we can delete it.
1993 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
1994 if ( filesystem::unlink( referencePath ) != 0 )
1995 ERR << "Delete orphan product failed: " << referencePath << endl;
1996 }
1997 else
1998 {
1999 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2000 }
2001 }
2002 }
2003 }
2004 else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() )
2005 {
2006 // SrcPackage is install-only
2007 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
2009 }
2010
2012 step->stepStage( sat::Transaction::STEP_DONE );
2013 }
2014
2015 } // other resolvables
2016
2017 } // for
2018
2019 // Process any remembered %posttrans and/or %transfiletrigger(postun|in)
2020 // scripts. If aborting, at least log if scripts were omitted.
2021 if ( not abort )
2022 postTransCollector.executeScripts( rpm(), obsoletedPackages );
2023 else
2024 postTransCollector.discardScripts();
2025
2026 // Check presence of update scripts/messages. If aborting,
2027 // at least log omitted scripts.
2028 if ( ! successfullyInstalledPackages.empty() )
2029 {
2030 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2031 successfullyInstalledPackages, abort ) )
2032 {
2033 WAR << "Commit aborted by the user" << endl;
2034 abort = true;
2035 }
2036 // send messages after scripts in case some script generates output,
2037 // that should be kept in t %ghost message file.
2038 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2039 successfullyInstalledPackages,
2040 result_r );
2041 }
2042
2043 // jsc#SLE-5116: Log patch status changes to history
2044 // NOTE: Should be the last action as it may need to reload
2045 // the Target in case of an incomplete transaction.
2046 logPatchStatusChanges( result_r.transaction(), *this );
2047
2048 if ( abort )
2049 {
2050 HistoryLog().comment( "Commit was aborted." );
2052 }
2053 }
2054
2055
2062 struct SendSingleTransReport : public callback::SendReport<rpm::SingleTransReport>
2063 {
2065 void sendLogline( const std::string & line_r, ReportType::loglevel level_r = ReportType::loglevel::msg )
2066 {
2067 callback::UserData data { ReportType::contentLogline };
2068 data.set( "line", std::cref(line_r) );
2069 data.set( "level", level_r );
2070 report( data );
2071 }
2072
2073 void sendLoglineRpm( const std::string & line_r, unsigned rpmlevel_r )
2074 {
2075 auto u2rpmlevel = []( unsigned rpmlevel_r ) -> ReportType::loglevel {
2076 switch ( rpmlevel_r ) {
2077 case RPMLOG_EMERG: [[fallthrough]]; // system is unusable
2078 case RPMLOG_ALERT: [[fallthrough]]; // action must be taken immediately
2079 case RPMLOG_CRIT: // critical conditions
2080 return ReportType::loglevel::crt;
2081 case RPMLOG_ERR: // error conditions
2082 return ReportType::loglevel::err;
2083 case RPMLOG_WARNING: // warning conditions
2084 return ReportType::loglevel::war;
2085 default: [[fallthrough]];
2086 case RPMLOG_NOTICE: [[fallthrough]]; // normal but significant condition
2087 case RPMLOG_INFO: // informational
2088 return ReportType::loglevel::msg;
2089 case RPMLOG_DEBUG:
2090 return ReportType::loglevel::dbg;
2091 }
2092 };
2093 sendLogline( line_r, u2rpmlevel( rpmlevel_r ) );
2094 }
2095
2096 private:
2097 void report( const callback::UserData & userData_r )
2098 { (*this)->report( userData_r ); }
2099 };
2100
2102 {
2103 env::ScopedSet envguard[] __attribute__ ((__unused__)) {
2104 { "ZYPP_SINGLE_RPMTRANS", "1" },
2105 { "ZYPP_CLASSIC_RPMTRANS", nullptr },
2106 };
2107
2108 SingleTransReportLegacyWrapper _legacyWrapper; // just in case nobody listens on the SendSingleTransReports
2109 SendSingleTransReport report; // active throughout the whole rpm transaction
2110
2111 // steps: this is our todo-list
2113 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
2114
2116
2117 // Send notification once upon calling rpm
2118 NotifyAttemptToModify attemptToModify( result_r );
2119
2120 // let zypper know we executed in one big transaction so in case of failures it can show extended error information
2121 result_r.setSingleTransactionMode( true );
2122
2123 // bsc#1181328: Some systemd tools require /proc to be mounted
2124 AssertProcMounted assertProcMounted( _root );
2125 AssertDevMounted assertDevMounted( _root ); // also /dev
2126
2127 // Why nodeps?
2128 //
2129 // Because zypp builds the transaction and the resolver asserts that
2130 // everything is fine, or the user decided to ignore problems.
2131 rpm::RpmInstFlags flags( policy_r.rpmInstFlags()
2133 // skip signature checks, we did that already
2136 // ignore untrusted keys since we already checked those earlier
2138
2139 proto::target::Commit commit;
2140 commit.flags = flags;
2141 commit.ignoreArch = ( !ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) );
2143 commit.dbPath = rpm().dbPath().asString();
2144 commit.root = rpm().root().asString();
2145 commit.lockFilePath = ZYppFactory::lockfileDir().asString();
2146
2147 bool abort = false;
2148 zypp::AutoDispose<std::unordered_map<int, ManagedFile>> locCache([]( std::unordered_map<int, ManagedFile> &data ){
2149 for ( auto &[_, value] : data ) {
2150 (void)_; // unsused; for older g++ versions
2151 value.resetDispose();
2152 }
2153 data.clear();
2154 });
2155
2156 // fill the transaction
2157 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; ++stepId ) {
2158 auto &step = steps[stepId];
2159 PoolItem citem( step );
2160 if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
2161 if ( citem->isKind<Package>() )
2162 {
2163 // for packages this means being obsoleted (by rpm)
2164 // thius no additional action is needed.
2165 step.stepStage( sat::Transaction::STEP_DONE );
2166 continue;
2167 }
2168 }
2169
2170 if ( citem->isKind<Package>() ) {
2171 Package::constPtr p = citem->asKind<Package>();
2172 if ( citem.status().isToBeInstalled() )
2173 {
2174 try {
2175 locCache.value()[stepId] = packageCache_r.get( citem );
2176
2177 proto::target::InstallStep tStep;
2178 tStep.stepId = stepId;
2179 tStep.pathname = locCache.value()[stepId]->asString();
2180 tStep.multiversion = p->multiversionInstall() ;
2181
2182 commit.transactionSteps.push_back( std::move(tStep) );
2183 }
2184 catch ( const AbortRequestException &e )
2185 {
2186 WAR << "commit aborted by the user" << endl;
2187 abort = true;
2188 step.stepStage( sat::Transaction::STEP_ERROR );
2189 break;
2190 }
2191 catch ( const SkipRequestException &e )
2192 {
2193 ZYPP_CAUGHT( e );
2194 WAR << "Skipping package " << p << " in commit" << endl;
2195 step.stepStage( sat::Transaction::STEP_ERROR );
2196 continue;
2197 }
2198 catch ( const Exception &e )
2199 {
2200 // bnc #395704: missing catch causes abort.
2201 // TODO see if packageCache fails to handle errors correctly.
2202 ZYPP_CAUGHT( e );
2203 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2204 step.stepStage( sat::Transaction::STEP_ERROR );
2205 continue;
2206 }
2207 } else {
2208
2209 proto::target::RemoveStep tStep;
2210 tStep.stepId = stepId;
2211 tStep.name = p->name();
2212 tStep.version = p->edition().version();
2213 tStep.release = p->edition().release();
2214 tStep.arch = p->arch().asString();
2215 commit.transactionSteps.push_back(std::move(tStep));
2216
2217 }
2218 } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
2219 // SrcPackage is install-only
2220 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
2221
2222 try {
2223 // provide on local disk
2224 locCache.value()[stepId] = provideSrcPackage( p );
2225
2226 proto::target::InstallStep tStep;
2227 tStep.stepId = stepId;
2228 tStep.pathname = locCache.value()[stepId]->asString();
2229 tStep.multiversion = false;
2230 commit.transactionSteps.push_back(std::move(tStep));
2231
2232 } catch ( const Exception &e ) {
2233 ZYPP_CAUGHT( e );
2234 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2235 step.stepStage( sat::Transaction::STEP_ERROR );
2236 continue;
2237 }
2238 }
2239 }
2240
2241 std::vector<sat::Solvable> successfullyInstalledPackages;
2242
2243 if ( commit.transactionSteps.size() ) {
2244
2245 // create the event loop early
2246 auto loop = zyppng::EventLoop::create();
2247
2248 attemptToModify();
2249
2250 const std::vector<int> interceptedSignals {
2251 SIGINT,
2252 SIGTERM,
2253 SIGHUP,
2254 SIGQUIT
2255 };
2256
2257 auto unixSignals = loop->eventDispatcher()->unixSignalSource();
2258 unixSignals->sigReceived ().connect ([]( int signum ){
2259 // translator: %1% is the received unix signal name, %2% is the numerical value of the received signal
2260 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 );
2261 });
2262 for( const auto &sig : interceptedSignals )
2263 unixSignals->addSignal ( sig );
2264
2265 Deferred cleanupSigs([&](){
2266 for( const auto &sig : interceptedSignals )
2267 unixSignals->removeSignal ( sig );
2268 });
2269
2270 // transaction related variables:
2271 //
2272 // the index of the step in the transaction list that we currenty execute.
2273 // this can be -1
2274 int currentStepId = -1;
2275
2276 // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
2277 // the script fd, once we receive it we set this flag to true and ignore all output
2278 // that is written to the pipe ( aside from buffering it ) until we finalize the current report
2279 // and start a new one
2280 bool gotEndOfScript = false;
2281
2282 // the possible reports we emit during the transaction
2283 std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
2284 std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2285 std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2286 std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2287 std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2288
2289 // this will be set if we receive a transaction error description
2290 std::optional<proto::target::TransactionError> transactionError;
2291
2292 // infos about the currently executed script, empty if no script is currently executed
2293 std::string currentScriptType;
2294 std::string currentScriptPackage;
2295
2296 // buffer to collect rpm output per report, this will be written to the log once the
2297 // report ends
2298 std::string rpmmsg;
2299
2300 // maximum number of lines that we are buffering in rpmmsg
2301 constexpr auto MAXRPMMESSAGELINES = 10000;
2302
2303 // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2304 unsigned lineno = 0;
2305
2306 // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2307 auto msgSource = zyppng::AsyncDataSource::create();
2308 auto scriptSource = zyppng::AsyncDataSource::create();
2309
2310 // this will be the communication channel, will be created once the process starts and
2311 // we can receive data
2312 zyppng::StompFrameStreamRef msgStream;
2313
2314
2315 // helper function that sends RPM output to the currently active report, writing a warning to the log
2316 // if there is none
2317 const auto &sendRpmLineToReport = [&]( const std::string &line ){
2318
2319 const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2320 callback::UserData cmdout(cType);
2321 if ( currentStepId >= 0 )
2322 cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2323 cmdout.set( "line", line );
2324 report->report(cmdout);
2325 };
2326
2327 if ( installreport ) {
2328 sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2329 } else if ( uninstallreport ) {
2330 sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2331 } else if ( scriptreport ) {
2332 sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2333 } else if ( transactionreport ) {
2334 sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2335 } else if ( cleanupreport ) {
2336 sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2337 } else {
2338 WAR << "Got rpm output without active report " << line; // no endl! - readLine does not trim
2339 }
2340
2341 // remember rpm output
2342 if ( lineno >= MAXRPMMESSAGELINES ) {
2343 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2344 return;
2345 }
2346 rpmmsg += line;
2347 if ( line.back() != '\n' )
2348 rpmmsg += '\n';
2349 };
2350
2351
2352 // callback and helper function to process data that is received on the script FD
2353 const auto &processDataFromScriptFd = [&](){
2354
2355 while ( scriptSource->canReadLine() ) {
2356
2357 if ( gotEndOfScript )
2358 return;
2359
2360 std::string l = scriptSource->readLine().asString();
2361 if( str::endsWith( l, endOfScriptTag ) ) {
2362 gotEndOfScript = true;
2363 std::string::size_type rawsize { l.size() - endOfScriptTag.size() };
2364 if ( not rawsize )
2365 return;
2366 l = l.substr( 0, rawsize );
2367 }
2368 L_DBG("zypp-rpm") << "[rpm> " << l; // no endl! - readLine does not trim
2369 sendRpmLineToReport( l );
2370 }
2371 };
2372 scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2373
2374 // helper function that just waits until the end of script tag was received on the scriptSource
2375 const auto &waitForScriptEnd = [&]() {
2376
2377 // nothing to wait for
2378 if ( gotEndOfScript )
2379 return;
2380
2381 // we process all available data
2382 processDataFromScriptFd();
2383
2384 // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2385 while ( scriptSource->readFdOpen() && scriptSource->canRead() && !gotEndOfScript ) {
2386 // readyRead will trigger processDataFromScriptFd so no need to call it again
2387 // we still got nothing, lets wait for more
2388 scriptSource->waitForReadyRead( 100 );
2389 }
2390 };
2391
2392 const auto &aboutToStartNewReport = [&](){
2393
2394 if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2395 ERR << "There is still a running report, this is a bug" << std::endl;
2396 assert(false);
2397 }
2398
2399 gotEndOfScript = false;
2400 };
2401
2402 const auto &writeRpmMsgToHistory = [&](){
2403 if ( rpmmsg.size() == 0 )
2404 return;
2405
2406 if ( lineno >= MAXRPMMESSAGELINES )
2407 rpmmsg += "[truncated]\n";
2408
2409 std::ostringstream sstr;
2410 sstr << "rpm output:" << endl << rpmmsg << endl;
2411 HistoryLog().comment(sstr.str());
2412 };
2413
2414 // helper function that closes the current report and cleans up the ressources
2415 const auto &finalizeCurrentReport = [&]() {
2416 sat::Transaction::Step *step = nullptr;
2417 Resolvable::constPtr resObj;
2418 if ( currentStepId >= 0 ) {
2419 step = &steps.at(currentStepId);
2420 resObj = makeResObject( step->satSolvable() );
2421 }
2422
2423 if ( installreport ) {
2424 waitForScriptEnd();
2425 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2426
2428 str::form("%s install failed", step->ident().c_str()),
2429 true /*timestamp*/);
2430
2431 writeRpmMsgToHistory();
2432
2433 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2434 } else {
2435 ( *installreport)->progress( 100, resObj );
2436 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2437
2438 if ( currentStepId >= 0 )
2439 locCache.value().erase( currentStepId );
2440 successfullyInstalledPackages.push_back( step->satSolvable() );
2441
2442 PoolItem citem( *step );
2443 if ( !( flags & rpm::RPMINST_TEST ) ) {
2444 // @TODO are we really doing this just for install?
2445 if ( citem.isNeedreboot() ) {
2446 auto rebootNeededFile = root() / "/run/reboot-needed";
2447 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2448 filesystem::touch( rebootNeededFile );
2449 }
2451 HistoryLog().install(citem);
2452 }
2453
2455 str::form("%s installed ok", step->ident().c_str()),
2456 true /*timestamp*/);
2457
2458 writeRpmMsgToHistory();
2459 }
2460 }
2461 if ( uninstallreport ) {
2462 waitForScriptEnd();
2463 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2464
2466 str::form("%s uninstall failed", step->ident().c_str()),
2467 true /*timestamp*/);
2468
2469 writeRpmMsgToHistory();
2470
2471 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2472 } else {
2473 ( *uninstallreport)->progress( 100, resObj );
2474 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2475
2476 PoolItem citem( *step );
2477 HistoryLog().remove(citem);
2478
2480 str::form("%s removed ok", step->ident().c_str()),
2481 true /*timestamp*/);
2482
2483 writeRpmMsgToHistory();
2484 }
2485 }
2486 if ( scriptreport ) {
2487 waitForScriptEnd();
2488 ( *scriptreport)->progress( 100, resObj );
2489 ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2490 }
2491 if ( transactionreport ) {
2492 waitForScriptEnd();
2493 ( *transactionreport)->progress( 100 );
2494 ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2495 }
2496 if ( cleanupreport ) {
2497 waitForScriptEnd();
2498 ( *cleanupreport)->progress( 100 );
2499 ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2500 }
2501 currentStepId = -1;
2502 lineno = 0;
2503 rpmmsg.clear();
2504 currentScriptType.clear();
2505 currentScriptPackage.clear();
2506 installreport.reset();
2507 uninstallreport.reset();
2508 scriptreport.reset();
2509 transactionreport.reset();
2510 cleanupreport.reset();
2511 };
2512
2513 // This sets up the process and pushes the required transactions steps to it
2514 // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2515 //
2516 // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2517 // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2518 // 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
2519
2520 constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2521
2522 const char *argv[] = {
2523 //"gdbserver",
2524 //"localhost:10001",
2525 zyppRpmBinary.data(),
2526 nullptr
2527 };
2528 auto prog = zyppng::Process::create();
2529
2530 // we set up a pipe to communicate with the process, it is too dangerous to use stdout since librpm
2531 // might print to it.
2532 auto messagePipe = zyppng::Pipe::create();
2533 if ( !messagePipe )
2534 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2535
2536 // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2537 // way than a FD to redirect that output
2538 auto scriptPipe = zyppng::Pipe::create();
2539 if ( !scriptPipe )
2540 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2541
2542 prog->addFd( messagePipe->writeFd );
2543 prog->addFd( scriptPipe->writeFd );
2544
2545 // set up the AsyncDataSource to read script output
2546 if ( !scriptSource->openFds( std::vector<int>{ scriptPipe->readFd } ) )
2547 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2548
2549 const auto &processMessages = [&] ( ) {
2550
2551 // lambda function that parses the passed message type and checks if the stepId is a valid offset
2552 // in the steps list.
2553 const auto &checkMsgWithStepId = [&steps]( auto &p ){
2554 if ( !p ) {
2555 ERR << "Failed to parse message from zypp-rpm." << std::endl;
2556 return false;
2557 }
2558
2559 auto id = p->stepId;
2560 if ( id < 0 || id >= steps.size() ) {
2561 ERR << "Received invalid stepId: " << id << " in " << p->typeName << " message from zypp-rpm, ignoring." << std::endl;
2562 return false;
2563 }
2564 return true;
2565 };
2566
2567 while ( const auto &m = msgStream->nextMessage() ) {
2568
2569 // due to librpm behaviour we need to make sense of the order of messages we receive
2570 // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2571 // Script related messages. What we do is remember the current step we are in and only close
2572 // the step when we get the start of the next one
2573 const auto &mName = m->command();
2574 if ( mName == proto::target::RpmLog::typeName ) {
2575
2576 const auto &p = proto::target::RpmLog::fromStompMessage (*m);
2577 if ( !p ) {
2578 ERR << "Failed to parse " << proto::target::RpmLog::typeName << " message from zypp-rpm." << std::endl;
2579 continue;
2580 }
2581 ( p->level >= RPMLOG_ERR ? L_ERR("zypp-rpm")
2582 : p->level >= RPMLOG_WARNING ? L_WAR("zypp-rpm")
2583 : L_DBG("zypp-rpm") ) << "[rpm " << p->level << "> " << p->line; // no endl! - readLine does not trim
2584 report.sendLoglineRpm( p->line, p->level );
2585
2586 } else if ( mName == proto::target::PackageBegin::typeName ) {
2587 finalizeCurrentReport();
2588
2589 const auto &p = proto::target::PackageBegin::fromStompMessage(*m);
2590 if ( !checkMsgWithStepId( p ) )
2591 continue;
2592
2593 aboutToStartNewReport();
2594
2595 auto & step = steps.at( p->stepId );
2596 currentStepId = p->stepId;
2597 if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2598 uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2599 ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2600 } else {
2601 installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2602 ( *installreport )->start( makeResObject( step.satSolvable() ) );
2603 }
2604
2605 } else if ( mName == proto::target::PackageFinished::typeName ) {
2606 const auto &p = proto::target::PackageFinished::fromStompMessage(*m);
2607 if ( !checkMsgWithStepId( p ) )
2608 continue;
2609
2610 // here we only set the step stage to done, we however need to wait for the next start in order to send
2611 // the finished report since there might be a error pending to be reported
2612 steps[ p->stepId ].stepStage( sat::Transaction::STEP_DONE );
2613
2614 } else if ( mName == proto::target::PackageProgress::typeName ) {
2615 const auto &p = proto::target::PackageProgress::fromStompMessage(*m);
2616 if ( !checkMsgWithStepId( p ) )
2617 continue;
2618
2619 if ( uninstallreport )
2620 (*uninstallreport)->progress( p->amount, makeResObject( steps.at( p->stepId ) ));
2621 else if ( installreport )
2622 (*installreport)->progress( p->amount, makeResObject( steps.at( p->stepId ) ));
2623 else
2624 ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2625
2626 } else if ( mName == proto::target::PackageError::typeName ) {
2627 const auto &p = proto::target::PackageError::fromStompMessage(*m);
2628 if ( !checkMsgWithStepId( p ) )
2629 continue;
2630
2631 if ( p->stepId >= 0 && p->stepId < steps.size() )
2632 steps[ p->stepId ].stepStage( sat::Transaction::STEP_ERROR );
2633
2634 finalizeCurrentReport();
2635
2636 } else if ( mName == proto::target::ScriptBegin::typeName ) {
2637 finalizeCurrentReport();
2638
2639 const auto &p = proto::target::ScriptBegin::fromStompMessage(*m);
2640 if ( !p ) {
2641 ERR << "Failed to parse " << proto::target::ScriptBegin::typeName << " message from zypp-rpm." << std::endl;
2642 continue;
2643 }
2644
2645 aboutToStartNewReport();
2646
2647 Resolvable::constPtr resPtr;
2648 const auto stepId = p->stepId;
2649 if ( stepId >= 0 && static_cast<size_t>(stepId) < steps.size() ) {
2650 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2651 }
2652
2653 currentStepId = p->stepId;
2654 scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2655 currentScriptType = p->scriptType;
2656 currentScriptPackage = p->scriptPackage;
2657 (*scriptreport)->start( currentScriptType, currentScriptPackage, resPtr );
2658
2659 } else if ( mName == proto::target::ScriptFinished::typeName ) {
2660
2661 // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2662
2663 } else if ( mName == proto::target::ScriptError::typeName ) {
2664
2665 const auto &p = proto::target::ScriptError::fromStompMessage(*m);
2666 if ( !p ) {
2667 ERR << "Failed to parse " << proto::target::ScriptError::typeName << " message from zypp-rpm." << std::endl;
2668 continue;
2669 }
2670
2671 Resolvable::constPtr resPtr;
2672 const auto stepId = p->stepId;
2673 if ( stepId >= 0 && static_cast<size_t>(stepId) < steps.size() ) {
2674 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2675
2676 if ( p->fatal ) {
2677 steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2678 }
2679
2680 }
2681
2683 str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2684 true /*timestamp*/);
2685
2686 writeRpmMsgToHistory();
2687
2688 if ( !scriptreport ) {
2689 ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2690 continue;
2691 }
2692
2693 // before killing the report we need to wait for the script end tag
2694 waitForScriptEnd();
2695 (*scriptreport)->finish( resPtr, p->fatal ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2696
2697 // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2698 scriptreport.reset();
2699 currentStepId = -1;
2700
2701 } else if ( mName == proto::target::CleanupBegin::typeName ) {
2702 finalizeCurrentReport();
2703
2704 const auto &beg = proto::target::CleanupBegin::fromStompMessage(*m);
2705 if ( !beg ) {
2706 ERR << "Failed to parse " << proto::target::CleanupBegin::typeName << " message from zypp-rpm." << std::endl;
2707 continue;
2708 }
2709
2710 aboutToStartNewReport();
2711 cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2712 (*cleanupreport)->start( beg->nvra );
2713 } else if ( mName == proto::target::CleanupFinished::typeName ) {
2714
2715 finalizeCurrentReport();
2716
2717 } else if ( mName == proto::target::CleanupProgress::typeName ) {
2718 const auto &prog = proto::target::CleanupProgress::fromStompMessage(*m);
2719 if ( !prog ) {
2720 ERR << "Failed to parse " << proto::target::CleanupProgress::typeName << " message from zypp-rpm." << std::endl;
2721 continue;
2722 }
2723
2724 if ( !cleanupreport ) {
2725 ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2726 continue;
2727 }
2728
2729 (*cleanupreport)->progress( prog->amount );
2730
2731 } else if ( mName == proto::target::TransBegin::typeName ) {
2732 finalizeCurrentReport();
2733
2734 const auto &beg = proto::target::TransBegin::fromStompMessage(*m);
2735 if ( !beg ) {
2736 ERR << "Failed to parse " << proto::target::TransBegin::typeName << " message from zypp-rpm." << std::endl;
2737 continue;
2738 }
2739
2740 aboutToStartNewReport();
2741 transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2742 (*transactionreport)->start( beg->name );
2743 } else if ( mName == proto::target::TransFinished::typeName ) {
2744
2745 finalizeCurrentReport();
2746
2747 } else if ( mName == proto::target::TransProgress::typeName ) {
2748 const auto &prog = proto::target::TransProgress::fromStompMessage(*m);
2749 if ( !prog ) {
2750 ERR << "Failed to parse " << proto::target::TransProgress::typeName << " message from zypp-rpm." << std::endl;
2751 continue;
2752 }
2753
2754 if ( !transactionreport ) {
2755 ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2756 continue;
2757 }
2758
2759 (*transactionreport)->progress( prog->amount );
2760 } else if ( mName == proto::target::TransactionError::typeName ) {
2761
2762 const auto &error = proto::target::TransactionError::fromStompMessage(*m);
2763 if ( !error ) {
2764 ERR << "Failed to parse " << proto::target::TransactionError::typeName << " message from zypp-rpm." << std::endl;
2765 continue;
2766 }
2767
2768 // this value is checked later
2769 transactionError = std::move(*error);
2770
2771 } else {
2772 ERR << "Received unexpected message from zypp-rpm: "<< m->command() << ", ignoring" << std::endl;
2773 return;
2774 }
2775
2776 }
2777 };
2778
2779 // setup the rest when zypp-rpm is running
2780 prog->sigStarted().connect( [&](){
2781
2782 // close the ends of the pipes we do not care about
2783 messagePipe->unrefWrite();
2784 scriptPipe->unrefWrite();
2785
2786 // read the stdout and stderr and forward it to our log
2787 prog->connectFunc( &zyppng::IODevice::sigChannelReadyRead, [&]( int channel ){
2788 while( prog->canReadLine( channel ) ) {
2789 L_ERR("zypp-rpm") << ( channel == zyppng::Process::StdOut ? "<stdout> " : "<stderr> " ) << prog->channelReadLine( channel ).asStringView(); // no endl! - readLine does not trim
2790 }
2791 });
2792
2793 // this is the source for control messages from zypp-rpm , we will get structured data information
2794 // in form of STOMP messages
2795 if ( !msgSource->openFds( std::vector<int>{ messagePipe->readFd }, prog->stdinFd() ) )
2796 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2797
2798 msgStream = zyppng::StompFrameStream::create(msgSource);
2799 msgStream->connectFunc( &zyppng::StompFrameStream::sigMessageReceived, processMessages );
2800
2801 const auto &msg = commit.toStompMessage();
2802 if ( !msg )
2803 std::rethrow_exception ( msg.error() );
2804
2805 if ( !msgStream->sendMessage( *msg ) ) {
2806 prog->stop( SIGKILL );
2807 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2808 }
2809 });
2810
2811 // track the childs lifetime
2812 int zyppRpmExitCode = -1;
2813 prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2814 zyppRpmExitCode = code;
2815 loop->quit();
2816 });
2817
2818 if ( !prog->start( argv ) ) {
2819 HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2820 ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2821 }
2822
2823 loop->run();
2824
2825 if ( msgStream ) {
2826 // pull all messages from the IO device
2827 msgStream->readAllMessages();
2828
2829 // make sure to read ALL available messages
2830 processMessages();
2831 }
2832
2833 // we will not receive a new start message , so we need to manually finalize the last report
2834 finalizeCurrentReport();
2835
2836 // make sure to read all data from the log source
2837 bool readMsgs = false;
2838 while( prog->canReadLine( zyppng::Process::StdErr ) ) {
2839 readMsgs = true;
2840 MIL << "zypp-rpm: " << prog->channelReadLine( zyppng::Process::StdErr ).asStringView();
2841 }
2842 while( prog->canReadLine( zyppng::Process::StdOut ) ) {
2843 readMsgs = true;
2844 MIL << "zypp-rpm: " << prog->channelReadLine( zyppng::Process::StdOut ).asStringView();
2845 }
2846
2847 while ( scriptSource->canReadLine() ) {
2848 readMsgs = true;
2849 MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2850 }
2851 if ( scriptSource->bytesAvailable() > 0 ) {
2852 readMsgs = true;
2853 MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2854 }
2855 if ( readMsgs )
2856 MIL << std::endl;
2857
2858 switch ( zyppRpmExitCode ) {
2859 // we need to look at the summary, handle finishedwitherrors like no error here
2860 case zypprpm::NoError:
2861 case zypprpm::RpmFinishedWithError:
2862 break;
2863 case zypprpm::RpmFinishedWithTransactionError: {
2864 // here zypp-rpm sent us a error description
2865 if ( transactionError ) {
2866
2867 std::ostringstream sstr;
2868 sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2869 for ( const auto & err : transactionError->problems ) {
2870 sstr << " " << err << "\n";
2871 }
2872 sstr << std::endl;
2874
2875 } else {
2876 ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more information.") );
2877 }
2878 break;
2879 }
2880 case zypprpm::FailedToOpenDb:
2881 ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2882 break;
2883 case zypprpm::WrongHeaderSize:
2884 case zypprpm::WrongMessageFormat:
2885 ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2886 break;
2887 case zypprpm::RpmInitFailed:
2888 ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2889 break;
2890 case zypprpm::FailedToReadPackage:
2891 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more information.") );
2892 break;
2893 case zypprpm::FailedToAddStepToTransaction:
2894 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more information.") );
2895 break;
2896 case zypprpm::RpmOrderFailed:
2897 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to order the transaction, check the logs for more information.") );
2898 break;
2899 case zypprpm::FailedToCreateLock:
2900 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to create its lockfile, check the logs for more information.") );
2901 break;
2902 }
2903
2904 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort; ++stepId ) {
2905 auto &step = steps[stepId];
2906 PoolItem citem( step );
2907
2908 if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2909 // other resolvables (non-Package) that are not handled by zypp-rpm
2910 if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2911 // Status is changed as the buddy package buddy
2912 // gets installed/deleted. Handle non-buddies only.
2913 if ( ! citem.buddy() && citem->isKind<Product>() ) {
2914 Product::constPtr p = citem->asKind<Product>();
2915
2916 if ( citem.status().isToBeInstalled() ) {
2917 ERR << "Can't install orphan product without release-package! " << citem << endl;
2918 } else {
2919 // Deleting the corresponding product entry is all we con do.
2920 // So the product will no longer be visible as installed.
2921 std::string referenceFilename( p->referenceFilename() );
2922
2923 if ( referenceFilename.empty() ) {
2924 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2925 } else {
2926 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2927
2928 if ( ! rpm().hasFile( referencePath.asString() ) ) {
2929 // If it's not owned by a package, we can delete it.
2930 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2931 if ( filesystem::unlink( referencePath ) != 0 )
2932 ERR << "Delete orphan product failed: " << referencePath << endl;
2933 } else {
2934 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2935 }
2936 }
2937 }
2939 step.stepStage( sat::Transaction::STEP_DONE );
2940 }
2941 }
2942 }
2943 }
2944 }
2945
2946 // Check presence of update scripts/messages. If aborting,
2947 // at least log omitted scripts.
2948 if ( ! successfullyInstalledPackages.empty() )
2949 {
2950 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2951 successfullyInstalledPackages, abort ) )
2952 {
2953 WAR << "Commit aborted by the user" << endl;
2954 abort = true;
2955 }
2956 // send messages after scripts in case some script generates output,
2957 // that should be kept in t %ghost message file.
2958 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2959 successfullyInstalledPackages,
2960 result_r );
2961 }
2962
2963 // jsc#SLE-5116: Log patch status changes to history
2964 // NOTE: Should be the last action as it may need to reload
2965 // the Target in case of an incomplete transaction.
2966 logPatchStatusChanges( result_r.transaction(), *this );
2967
2968 if ( abort ) {
2969 HistoryLog().comment( "Commit was aborted." );
2971 }
2972 }
2973
2975
2977 {
2978 return _rpm;
2979 }
2980
2981 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2982 {
2983 return _rpm.hasFile(path_str, name_str);
2984 }
2985
2987 namespace
2988 {
2989 parser::ProductFileData baseproductdata( const Pathname & root_r )
2990 {
2992 PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2993
2994 if ( baseproduct.isFile() )
2995 {
2996 try
2997 {
2998 ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2999 }
3000 catch ( const Exception & excpt )
3001 {
3002 ZYPP_CAUGHT( excpt );
3003 }
3004 }
3005 else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
3006 {
3007 ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
3008 }
3009 return ret;
3010 }
3011
3013 const parser::ProductFileData & cachedBaseproductdata( const Pathname & root_r )
3014 {
3015 struct CachedEntry {
3016 WatchFile watcher;
3017 parser::ProductFileData data;
3018 };
3019 static std::map<Pathname, CachedEntry> cache;
3020 auto & entry = cache[root_r];
3021 if ( entry.watcher.path().empty() )
3022 entry.watcher = WatchFile( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ), WatchFile::NO_INIT );
3023 if ( entry.watcher.hasChanged() )
3024 entry.data = baseproductdata( root_r );
3025 return entry.data;
3026 }
3027
3028 inline Pathname staticGuessRoot( const Pathname & root_r )
3029 {
3030 if ( root_r.empty() )
3031 {
3032 // empty root: use existing Target or assume "/"
3033 Pathname ret ( ZConfig::instance().systemRoot() );
3034 if ( ret.empty() )
3035 return Pathname("/");
3036 return ret;
3037 }
3038 return root_r;
3039 }
3040
3041 inline std::string firstNonEmptyLineIn( const Pathname & file_r )
3042 {
3043 std::ifstream idfile( file_r.c_str() );
3044 for( iostr::EachLine in( idfile ); in; in.next() )
3045 {
3046 std::string line( str::trim( *in ) );
3047 if ( ! line.empty() )
3048 return line;
3049 }
3050 return std::string();
3051 }
3052 } // namespace
3054
3056 {
3058 for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
3059 {
3060 Product::constPtr p = (*it)->asKind<Product>();
3061 if ( p->isTargetDistribution() )
3062 return p;
3063 }
3064 return nullptr;
3065 }
3066
3068 {
3069 const Pathname needroot( staticGuessRoot(root_r) );
3070 const Target_constPtr target( getZYpp()->getTarget() );
3071 if ( target && target->root() == needroot )
3072 return target->requestedLocales();
3073 return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
3074 }
3075
3077 {
3078 MIL << "updateAutoInstalled if changed..." << endl;
3079 SolvIdentFile::Data newdata;
3080 for ( auto id : sat::Pool::instance().autoInstalled() )
3081 newdata.insert( IdString(id) ); // explicit ctor!
3082 _autoInstalledFile.setData( std::move(newdata) );
3083 }
3084
3086 { return baseproductdata( _root ).registerTarget(); }
3087 // static version:
3088 std::string TargetImpl::targetDistribution( const Pathname & root_r )
3089 { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
3090
3092 { return baseproductdata( _root ).registerRelease(); }
3093 // static version:
3095 { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
3096
3098 { return baseproductdata( _root ).registerFlavor(); }
3099 // static version:
3101 { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
3102
3104 {
3106 parser::ProductFileData pdata( baseproductdata( _root ) );
3107 ret.shortName = pdata.shortName();
3108 ret.summary = pdata.summary();
3109 return ret;
3110 }
3111 // static version:
3113 {
3115 parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
3116 ret.shortName = pdata.shortName();
3117 ret.summary = pdata.summary();
3118 return ret;
3119 }
3120
3122 {
3123 if ( _baseproductWatcher.hasChanged() )
3124 {
3126 MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
3127 }
3128 return _distributionVersion;
3129 }
3130 // static version
3131 std::string TargetImpl::distributionVersion( const Pathname & root_r )
3132 {
3133 const Pathname & needroot = staticGuessRoot(root_r);
3134 std::string distributionVersion = cachedBaseproductdata( needroot ).edition().version();
3135 if ( distributionVersion.empty() )
3136 {
3137 // ...But the baseproduct method is not expected to work on RedHat derivatives.
3138 // On RHEL, Fedora and others the "product version" is determined by the first package
3139 // providing 'system-release'. This value is not hardcoded in YUM and can be configured
3140 // with the $distroverpkg variable.
3141 rpm::librpmDb::db_const_iterator it( needroot );
3142 if ( it.findByProvides( ZConfig::instance().distroverpkg() ) )
3143 distributionVersion = it->tag_version();
3144 }
3145 return distributionVersion;
3146 }
3147
3148
3150 {
3151 return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
3152 }
3153 // static version:
3154 std::string TargetImpl::distributionFlavor( const Pathname & root_r )
3155 {
3156 return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
3157 }
3158
3160 namespace
3161 {
3162 std::string guessAnonymousUniqueId( const Pathname & root_r )
3163 {
3164 // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
3165 std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
3166 if ( ret.empty() && root_r != "/" )
3167 {
3168 // if it has nonoe, use the outer systems one
3169 ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
3170 }
3171 return ret;
3172 }
3173 }
3174
3176 {
3177 return guessAnonymousUniqueId( root() );
3178 }
3179 // static version:
3180 std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
3181 {
3182 return guessAnonymousUniqueId( staticGuessRoot(root_r) );
3183 }
3184
3186
3188 {
3189 MIL << "New VendorAttr: " << vendorAttr_r << endl;
3190 _vendorAttr = std::move(vendorAttr_r);
3191 }
3192
3193
3194 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3195 {
3196 // provide on local disk
3197 ManagedFile localfile = provideSrcPackage(srcPackage_r);
3198 // create a installation progress report proxy
3199 RpmInstallPackageReceiver progress( srcPackage_r );
3200 progress.connect(); // disconnected on destruction.
3201 // install it
3202 rpm().installPackage ( localfile );
3203 }
3204
3205 ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3206 {
3207 // provide on local disk
3208 repo::RepoMediaAccess access_r;
3209 repo::SrcPackageProvider prov( access_r );
3210 return prov.provideSrcPackage( srcPackage_r );
3211 }
3212
3213 } // namespace target
3216} // 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.
A sat capability.
Definition Capability.h:63
Store and operate on date (time_t).
Definition Date.h:33
static Date now()
Return the current time.
Definition Date.h:78
Edition represents [epoch:]version[-release]
Definition Edition.h: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
void remember(const Exception &old_r)
Store an other Exception as history.
Definition Exception.cc:154
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
int close() override
Wait for the progamm to complete.
const std::string & command() const
The command we're executing.
std::vector< std::string > Arguments
Writing the zypp history file.
Definition HistoryLog.h:57
void stampCommand()
Log info about the current process.
static void setRoot(const Pathname &root)
Set new root directory to the default history log file path.
void remove(const PoolItem &pi)
Log removal of a package.
static const Pathname & fname()
Get the current log file path.
void install(const PoolItem &pi)
Log installation (or update) of a package.
void comment(const std::string &comment, bool timestamp=false)
Log a comment (even multiline).
Access to the sat-pools string space.
Definition IdString.h: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
@ REGEX
Regular Expression.
Definition StrMatcher.h:48
Package interface.
Definition Package.h:34
TraitsType::constPtrType constPtr
Definition Package.h:39
Class representing a patch.
Definition Patch.h:38
TraitsType::constPtrType constPtr
Definition Patch.h:43
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
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.
Command frame for communication with PluginScript.
Definition PluginFrame.h:42
Combining sat::Solvable and ResStatus.
Definition PoolItem.h:51
ResObject::constPtr resolvable() const
Returns the ResObject::constPtr.
Definition PoolItem.cc:227
ResStatus & status() const
Returns the current status.
Definition PoolItem.cc:212
sat::Solvable buddy() const
Return the buddy we share our status object with.
Definition PoolItem.cc:215
Product interface.
Definition Product.h:34
TraitsType::constPtrType constPtr
Definition Product.h:39
Track changing files or directories.
Definition RepoStatus.h:41
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
bool solvablesEmpty() const
Whether Repository contains solvables.
SolvableIterator solvablesEnd() const
Iterator behind the last Solvable.
SolvableIterator solvablesBegin() const
Iterator to the first Solvable.
size_type solvablesSize() const
Number of solvables in Repository.
void addSolv(const Pathname &file_r)
Load Solvables from a solv-file.
void eraseFromPool()
Remove this Repository from its Pool.
Global ResObject pool.
Definition ResPool.h:62
static ResPool instance()
Singleton ctor.
Definition ResPool.cc:38
void setHardLockQueries(const HardLockQueries &newLocks_r)
Set a new set of queries.
Definition ResPool.cc:104
Resolver & resolver() const
The Resolver.
Definition ResPool.cc:62
const LocaleSet & getRequestedLocales() const
Return the requested locales.
Definition ResPool.cc:131
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from their initial one.
Definition ResPool.h:350
EstablishedStates establishedStates() const
Factory for EstablishedStates.
Definition ResPool.cc:77
void getHardLockQueries(HardLockQueries &activeLocks_r)
Suggest a new set of queries based on the current selection.
Definition ResPool.cc:107
EstablishedStates::ChangedPseudoInstalled ChangedPseudoInstalled
Map holding pseudo installed items where current and established status differ.
Definition ResPool.h:342
bool isToBeInstalled() const
Definition ResStatus.h:259
bool resetTransact(TransactByValue causer_r)
Not the same as setTransact( false ).
Definition ResStatus.h:490
TraitsType::constPtrType constPtr
Definition Resolvable.h:59
sat::Transaction getTransaction()
Return the Transaction computed by the last solver run.
Definition Resolver.cc:77
bool upgradeMode() const
Definition Resolver.cc:100
bool upgradingRepos() const
Whether there is at least one UpgradeRepo request pending.
Definition Resolver.cc:149
SrcPackage interface.
Definition SrcPackage.h:30
TraitsType::constPtrType constPtr
Definition SrcPackage.h:36
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition StrMatcher.h:298
Definition of vendor equivalence.
Definition VendorAttr.h:61
Remember a files attributes to detect content changes.
Definition watchfile.h:50
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:329
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:331
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:315
detail::IdType value_type
Definition Queue.h:39
void push(value_type val_r)
Push a value to the end off the Queue.
Definition Queue.cc:103
A Solvable object within the sat Pool.
Definition Solvable.h:54
A single step within a Transaction.
StepType stepType() const
Type of action to perform in this step.
StepStage stepStage() const
Step action result.
Solvable satSolvable() const
Return the corresponding Solvable.
Libsolv transaction wrapper.
Definition Transaction.h:52
const_iterator end() const
Iterator behind the last TransactionStep.
StringQueue autoInstalled() const
Return the ident strings of all packages that would be auto-installed after the transaction is run.
const_iterator begin() const
Iterator to the first TransactionStep.
bool order()
Order transaction steps for commit.
@ TRANSACTION_MULTIINSTALL
[M] Install(multiversion) item (
Definition Transaction.h:67
@ TRANSACTION_INSTALL
[+] Install(update) item
Definition Transaction.h:66
@ TRANSACTION_IGNORE
[ ] Nothing (includes implicit deletes due to obsoletes and non-package actions)
Definition Transaction.h:64
@ TRANSACTION_ERASE
[-] Delete item
Definition Transaction.h:65
@ STEP_DONE
[OK] success
Definition Transaction.h:74
@ STEP_TODO
[__] unprocessed
Definition Transaction.h:73
Target::commit helper optimizing package provision.
void setCommitList(std::vector< sat::Solvable > commitList_r)
Download(commit) sequence of solvables to compute read ahead.
bool preloaded() const
Whether preloaded hint is set.
ManagedFile get(const PoolItem &citem_r)
Provide a package.
pool::PoolTraits::HardLockQueries Data
Save and restore locale set from file.
const LocaleSet & locales() const
Return the loacale set.
void tryLevel(target::rpm::InstallResolvableReport::RpmLevel level_r)
Extract and remember posttrans scripts for later execution.
void executeScripts(rpm::RpmDb &rpm_r, const IdStringSet &obsoletedPackages_r)
Execute the remembered scripts and/or or dump_posttrans lines.
void discardScripts()
Discard all remembered scripts and/or or dump_posttrans lines.
bool aborted() const
Returns true if removing is aborted during progress.
std::unordered_set< IdString > Data
Base class for concrete Target implementations.
Definition TargetImpl.h: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
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
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)
nullptr CURL handle void p CURLversion nullptr struct curl_slist list CURLM CURLM_INTERNAL_ERROR CURLM CURL CURLM_INTERNAL_ERROR CURLM curl_socket_t s
Definition curl_dl.cc:111
nullptr CURL handle void * p
Definition curl_dl.cc:99
void
Definition curl_dl.cc:94
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:350
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition String.h:1097
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition String.cc:180
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition String.h:1155
bool endsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasSuffix
Definition String.h:1162
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:39
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition String.h:500
unsigned splitEscaped(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", bool withEmpty=false)
Split line_r into words with respect to escape delimeters.
Definition String.h:665
std::string trim(const std::string &s, const Trim trim_r)
Definition String.cc:226
void XRunUpdateMessages(const Pathname &root_r, const Pathname &messagesPath_r, const std::vector< sat::Solvable > &checkPackages_r, ZYppCommitResult &result_r)
std::string rpmDbStateHash(const Pathname &root_r)
void writeUpgradeTestcase()
static bool fileMissing(const Pathname &pathname)
helper functor
void updateFileContent(const Pathname &filename, boost::function< bool()> condition, boost::function< std::string()> value)
updates the content of filename if condition is true, setting the content the the value returned by v...
RepoStatus rpmDbRepoStatus(const Pathname &root_r)
static std::string generateRandomId()
generates a random id using uuidgen
Easy-to use interface to the ZYPP dependency resolver.
std::unordered_set< Locale > LocaleSet
Definition Locale.h:29
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)
#define IMPL_PTR_TYPE(NAME)