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