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