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