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