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