libzypp 17.29.1
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 // single trans mode does a test install via rpm
1487 if ( policy_r.singleTransModeEnabled() ) {
1488 commitInSingleTransaction( policy_r, packageCache, result );
1489 } else {
1490 if ( ! policy_r.dryRun() )
1491 {
1492 // if cache is preloaded, check for file conflicts
1493 commitFindFileConflicts( policy_r, result );
1494 commit( policy_r, packageCache, result );
1495 }
1496 else
1497 {
1498 DBG << "dryRun/downloadOnly: Not installing/deleting anything." << endl;
1499 if ( explicitDryRun ) {
1500 // if cache is preloaded, check for file conflicts
1501 commitFindFileConflicts( policy_r, result );
1502 }
1503 }
1504 }
1505 }
1506 }
1507 else
1508 {
1509 DBG << "dryRun: Not downloading/installing/deleting anything." << endl;
1510 if ( explicitDryRun ) {
1511 // if cache is preloaded, check for file conflicts
1512 commitFindFileConflicts( policy_r, result );
1513 }
1514 }
1515
1516 {
1517 // NOTE: Removing rpm in a transaction, rpm removes the /var/lib/rpm compat symlink.
1518 // We re-create it, in case it was lost to prevent legacy tools from accidentally
1519 // assuming no database is present.
1520 if ( ! PathInfo(_root/"/var/lib/rpm",PathInfo::LSTAT).isExist()
1521 && PathInfo(_root/"/usr/lib/sysimage/rpm").isDir() ) {
1522 WAR << "(rpm removed in commit?) Inject missing /var/lib/rpm compat symlink to /usr/lib/sysimage/rpm" << endl;
1523 filesystem::assert_dir( _root/"/var/lib" );
1524 filesystem::symlink( "../../usr/lib/sysimage/rpm", _root/"/var/lib/rpm" );
1525 }
1526 }
1527
1529 // Send result to commit plugins:
1531 if ( commitPlugins )
1532 commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
1533
1535 // Try to rebuild solv file while rpm database is still in cache
1537 if ( ! policy_r.dryRun() )
1538 {
1539 buildCache();
1540 }
1541
1542 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
1543 return result;
1544 }
1545
1547 //
1548 // COMMIT internal
1549 //
1551 namespace
1552 {
1553 struct NotifyAttemptToModify
1554 {
1555 NotifyAttemptToModify( ZYppCommitResult & result_r ) : _result( result_r ) {}
1556
1557 void operator()()
1558 { if ( _guard ) { _result.attemptToModify( true ); _guard = false; } }
1559
1560 TrueBool _guard;
1561 ZYppCommitResult & _result;
1562 };
1563 } // namespace
1564
1565 void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
1566 CommitPackageCache & packageCache_r,
1567 ZYppCommitResult & result_r )
1568 {
1569 // steps: this is our todo-list
1571 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1572
1574
1575 // Send notification once upon 1st call to rpm
1576 NotifyAttemptToModify attemptToModify( result_r );
1577
1578 bool abort = false;
1579
1580 // bsc#1181328: Some systemd tools require /proc to be mounted
1581 AssertProcMounted assertProcMounted( _root );
1582
1583 RpmPostTransCollector postTransCollector( _root );
1584 std::vector<sat::Solvable> successfullyInstalledPackages;
1585 TargetImpl::PoolItemList remaining;
1586
1587 for_( step, steps.begin(), steps.end() )
1588 {
1589 PoolItem citem( *step );
1590 if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
1591 {
1592 if ( citem->isKind<Package>() )
1593 {
1594 // for packages this means being obsoleted (by rpm)
1595 // thius no additional action is needed.
1596 step->stepStage( sat::Transaction::STEP_DONE );
1597 continue;
1598 }
1599 }
1600
1601 if ( citem->isKind<Package>() )
1602 {
1603 Package::constPtr p = citem->asKind<Package>();
1604 if ( citem.status().isToBeInstalled() )
1605 {
1606 ManagedFile localfile;
1607 try
1608 {
1609 localfile = packageCache_r.get( citem );
1610 }
1611 catch ( const AbortRequestException &e )
1612 {
1613 WAR << "commit aborted by the user" << endl;
1614 abort = true;
1615 step->stepStage( sat::Transaction::STEP_ERROR );
1616 break;
1617 }
1618 catch ( const SkipRequestException &e )
1619 {
1620 ZYPP_CAUGHT( e );
1621 WAR << "Skipping package " << p << " in commit" << endl;
1622 step->stepStage( sat::Transaction::STEP_ERROR );
1623 continue;
1624 }
1625 catch ( const Exception &e )
1626 {
1627 // bnc #395704: missing catch causes abort.
1628 // TODO see if packageCache fails to handle errors correctly.
1629 ZYPP_CAUGHT( e );
1630 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1631 step->stepStage( sat::Transaction::STEP_ERROR );
1632 continue;
1633 }
1634
1635 // create a installation progress report proxy
1636 RpmInstallPackageReceiver progress( citem.resolvable() );
1637 progress.connect(); // disconnected on destruction.
1638
1639 bool success = false;
1640 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1641 // Why force and nodeps?
1642 //
1643 // Because zypp builds the transaction and the resolver asserts that
1644 // everything is fine.
1645 // We use rpm just to unpack and register the package in the database.
1646 // We do this step by step, so rpm is not aware of the bigger context.
1647 // So we turn off rpms internal checks, because we do it inside zypp.
1648 flags |= rpm::RPMINST_NODEPS;
1649 flags |= rpm::RPMINST_FORCE;
1650 //
1651 if (p->multiversionInstall()) flags |= rpm::RPMINST_NOUPGRADE;
1652 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1653 if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
1654 if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
1655
1656 attemptToModify();
1657 try
1658 {
1660 if ( postTransCollector.collectScriptFromPackage( localfile ) )
1661 flags |= rpm::RPMINST_NOPOSTTRANS;
1662 rpm().installPackage( localfile, flags );
1663 HistoryLog().install(citem);
1664
1665 if ( progress.aborted() )
1666 {
1667 WAR << "commit aborted by the user" << endl;
1668 localfile.resetDispose(); // keep the package file in the cache
1669 abort = true;
1670 step->stepStage( sat::Transaction::STEP_ERROR );
1671 break;
1672 }
1673 else
1674 {
1675 if ( citem.isNeedreboot() ) {
1676 auto rebootNeededFile = root() / "/run/reboot-needed";
1677 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
1678 filesystem::touch( rebootNeededFile );
1679 }
1680
1681 success = true;
1682 step->stepStage( sat::Transaction::STEP_DONE );
1683 }
1684 }
1685 catch ( Exception & excpt_r )
1686 {
1687 ZYPP_CAUGHT(excpt_r);
1688 localfile.resetDispose(); // keep the package file in the cache
1689
1690 if ( policy_r.dryRun() )
1691 {
1692 WAR << "dry run failed" << endl;
1693 step->stepStage( sat::Transaction::STEP_ERROR );
1694 break;
1695 }
1696 // else
1697 if ( progress.aborted() )
1698 {
1699 WAR << "commit aborted by the user" << endl;
1700 abort = true;
1701 }
1702 else
1703 {
1704 WAR << "Install failed" << endl;
1705 }
1706 step->stepStage( sat::Transaction::STEP_ERROR );
1707 break; // stop
1708 }
1709
1710 if ( success && !policy_r.dryRun() )
1711 {
1713 successfullyInstalledPackages.push_back( citem.satSolvable() );
1714 step->stepStage( sat::Transaction::STEP_DONE );
1715 }
1716 }
1717 else
1718 {
1719 RpmRemovePackageReceiver progress( citem.resolvable() );
1720 progress.connect(); // disconnected on destruction.
1721
1722 bool success = false;
1723 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1724 flags |= rpm::RPMINST_NODEPS;
1725 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1726
1727 attemptToModify();
1728 try
1729 {
1730 rpm().removePackage( p, flags );
1731 HistoryLog().remove(citem);
1732
1733 if ( progress.aborted() )
1734 {
1735 WAR << "commit aborted by the user" << endl;
1736 abort = true;
1737 step->stepStage( sat::Transaction::STEP_ERROR );
1738 break;
1739 }
1740 else
1741 {
1742 success = true;
1743 step->stepStage( sat::Transaction::STEP_DONE );
1744 }
1745 }
1746 catch (Exception & excpt_r)
1747 {
1748 ZYPP_CAUGHT( excpt_r );
1749 if ( progress.aborted() )
1750 {
1751 WAR << "commit aborted by the user" << endl;
1752 abort = true;
1753 step->stepStage( sat::Transaction::STEP_ERROR );
1754 break;
1755 }
1756 // else
1757 WAR << "removal of " << p << " failed";
1758 step->stepStage( sat::Transaction::STEP_ERROR );
1759 }
1760 if ( success && !policy_r.dryRun() )
1761 {
1763 step->stepStage( sat::Transaction::STEP_DONE );
1764 }
1765 }
1766 }
1767 else if ( ! policy_r.dryRun() ) // other resolvables (non-Package)
1768 {
1769 // Status is changed as the buddy package buddy
1770 // gets installed/deleted. Handle non-buddies only.
1771 if ( ! citem.buddy() )
1772 {
1773 if ( citem->isKind<Product>() )
1774 {
1775 Product::constPtr p = citem->asKind<Product>();
1776 if ( citem.status().isToBeInstalled() )
1777 {
1778 ERR << "Can't install orphan product without release-package! " << citem << endl;
1779 }
1780 else
1781 {
1782 // Deleting the corresponding product entry is all we con do.
1783 // So the product will no longer be visible as installed.
1784 std::string referenceFilename( p->referenceFilename() );
1785 if ( referenceFilename.empty() )
1786 {
1787 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
1788 }
1789 else
1790 {
1791 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
1792 if ( ! rpm().hasFile( referencePath.asString() ) )
1793 {
1794 // If it's not owned by a package, we can delete it.
1795 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
1796 if ( filesystem::unlink( referencePath ) != 0 )
1797 ERR << "Delete orphan product failed: " << referencePath << endl;
1798 }
1799 else
1800 {
1801 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
1802 }
1803 }
1804 }
1805 }
1806 else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() )
1807 {
1808 // SrcPackage is install-only
1809 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
1810 installSrcPackage( p );
1811 }
1812
1814 step->stepStage( sat::Transaction::STEP_DONE );
1815 }
1816
1817 } // other resolvables
1818
1819 } // for
1820
1821 // process all remembered posttrans scripts. If aborting,
1822 // at least log omitted scripts.
1823 if ( abort || (abort = !postTransCollector.executeScripts()) )
1824 postTransCollector.discardScripts();
1825
1826 // Check presence of update scripts/messages. If aborting,
1827 // at least log omitted scripts.
1828 if ( ! successfullyInstalledPackages.empty() )
1829 {
1830 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
1831 successfullyInstalledPackages, abort ) )
1832 {
1833 WAR << "Commit aborted by the user" << endl;
1834 abort = true;
1835 }
1836 // send messages after scripts in case some script generates output,
1837 // that should be kept in t %ghost message file.
1838 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
1839 successfullyInstalledPackages,
1840 result_r );
1841 }
1842
1843 // jsc#SLE-5116: Log patch status changes to history
1844 // NOTE: Should be the last action as it may need to reload
1845 // the Target in case of an incomplete transaction.
1846 logPatchStatusChanges( result_r.transaction(), *this );
1847
1848 if ( abort )
1849 {
1850 HistoryLog().comment( "Commit was aborted." );
1852 }
1853 }
1854
1855
1862 struct SendSingleTransReport : public callback::SendReport<rpm::SingleTransReport>
1863 {
1865 void sendLogline( const std::string & line_r, ReportType::loglevel level_r = ReportType::loglevel::msg )
1866 {
1867 callback::UserData data { ReportType::contentLogline };
1868 data.set( "line", std::cref(line_r) );
1869 data.set( "level", level_r );
1870 report( data );
1871 }
1873 void sendLoglineRpm( const std::string & line_r, unsigned rpmlevel_r )
1874 {
1875 auto u2rpmlevel = []( unsigned rpmlevel_r ) -> ReportType::loglevel {
1876 switch ( rpmlevel_r ) {
1877 case RPMLOG_EMERG: [[fallthrough]]; // system is unusable
1878 case RPMLOG_ALERT: [[fallthrough]]; // action must be taken immediately
1879 case RPMLOG_CRIT: // critical conditions
1880 return ReportType::loglevel::crt;
1881 case RPMLOG_ERR: // error conditions
1882 return ReportType::loglevel::err;
1883 case RPMLOG_WARNING: // warning conditions
1884 return ReportType::loglevel::war;
1885 default: [[fallthrough]];
1886 case RPMLOG_NOTICE: [[fallthrough]]; // normal but significant condition
1887 case RPMLOG_INFO: // informational
1888 return ReportType::loglevel::msg;
1889 case RPMLOG_DEBUG:
1890 return ReportType::loglevel::dbg;
1891 }
1892 };
1893 sendLogline( line_r, u2rpmlevel( rpmlevel_r ) );
1894 }
1895
1896 private:
1897 void report( const callback::UserData & userData_r )
1898 { (*this)->report( userData_r ); }
1899 };
1900
1902
1908
1910 {
1911 namespace zpt = zypp::proto::target;
1912
1913 SendSingleTransReport report; // active throughout the whole rpm transaction
1914
1915 // steps: this is our todo-list
1917 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1918
1920
1921 // Send notification once upon calling rpm
1922 NotifyAttemptToModify attemptToModify( result_r );
1923
1924 // let zypper know we executed in one big transaction so in case of failures it can show extended error information
1925 result_r.setSingleTransactionMode( true );
1926
1927 // bsc#1181328: Some systemd tools require /proc to be mounted
1928 AssertProcMounted assertProcMounted( _root );
1929
1930 // Why nodeps?
1931 //
1932 // Because zypp builds the transaction and the resolver asserts that
1933 // everything is fine, or the user decided to ignore problems.
1934 rpm::RpmInstFlags flags( policy_r.rpmInstFlags()
1936 // skip signature checks, we did that already
1939 // ignore untrusted keys since we already checked those earlier
1941
1942 zpt::Commit commit;
1943 commit.set_flags( flags );
1944 commit.set_ignorearch( !ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) );
1945 commit.set_arch( ZConfig::instance().systemArchitecture().asString() );
1946 commit.set_dbpath( rpm().dbPath().asString() );
1947 commit.set_root( rpm().root().asString() );
1948
1949 bool abort = false;
1950 zypp::AutoDispose<std::unordered_map<int, ManagedFile>> locCache([]( std::unordered_map<int, ManagedFile> &data ){
1951 for ( auto &[_, value] : data ) {
1952 (void)_; // unsused; for older g++ versions
1953 value.resetDispose();
1954 }
1955 data.clear();
1956 });
1957
1958 // fill the transaction
1959 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; ++stepId ) {
1960 auto &step = steps[stepId];
1961 PoolItem citem( step );
1962 if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
1963 if ( citem->isKind<Package>() )
1964 {
1965 // for packages this means being obsoleted (by rpm)
1966 // thius no additional action is needed.
1967 step.stepStage( sat::Transaction::STEP_DONE );
1968 continue;
1969 }
1970 }
1971
1972 if ( citem->isKind<Package>() ) {
1973 Package::constPtr p = citem->asKind<Package>();
1974 if ( citem.status().isToBeInstalled() )
1975 {
1976 try {
1977 locCache.value()[stepId] = packageCache_r.get( citem );
1978
1979 zpt::TransactionStep tStep;
1980 tStep.set_stepid( stepId );
1981 tStep.mutable_install()->set_pathname( locCache.value()[stepId]->asString() );
1982 tStep.mutable_install()->set_multiversion( p->multiversionInstall() );
1983
1984 *commit.mutable_steps()->Add( ) = std::move(tStep);
1985 }
1986 catch ( const AbortRequestException &e )
1987 {
1988 WAR << "commit aborted by the user" << endl;
1989 abort = true;
1990 step.stepStage( sat::Transaction::STEP_ERROR );
1991 break;
1992 }
1993 catch ( const SkipRequestException &e )
1994 {
1995 ZYPP_CAUGHT( e );
1996 WAR << "Skipping package " << p << " in commit" << endl;
1997 step.stepStage( sat::Transaction::STEP_ERROR );
1998 continue;
1999 }
2000 catch ( const Exception &e )
2001 {
2002 // bnc #395704: missing catch causes abort.
2003 // TODO see if packageCache fails to handle errors correctly.
2004 ZYPP_CAUGHT( e );
2005 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2006 step.stepStage( sat::Transaction::STEP_ERROR );
2007 continue;
2008 }
2009 } else {
2010
2011 zpt::TransactionStep tStep;
2012 tStep.set_stepid( stepId );
2013 tStep.mutable_remove()->set_name( p->name() );
2014 tStep.mutable_remove()->set_version( p->edition().version() );
2015 tStep.mutable_remove()->set_release( p->edition().release() );
2016 tStep.mutable_remove()->set_arch( p->arch().asString() );
2017
2018 *commit.mutable_steps()->Add() = std::move(tStep);
2019
2020 }
2021 } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
2022 // SrcPackage is install-only
2023 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
2024
2025 try {
2026 // provide on local disk
2027 locCache.value()[stepId] = provideSrcPackage( p );
2028
2029 zpt::TransactionStep tStep;
2030 tStep.set_stepid( stepId );
2031 tStep.mutable_install()->set_pathname( locCache.value()[stepId]->asString() );
2032 tStep.mutable_install()->set_multiversion( false );
2033 *commit.mutable_steps()->Add() = std::move(tStep);
2034
2035 } catch ( const Exception &e ) {
2036 ZYPP_CAUGHT( e );
2037 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2038 step.stepStage( sat::Transaction::STEP_ERROR );
2039 continue;
2040 }
2041 }
2042 }
2043
2044 std::vector<sat::Solvable> successfullyInstalledPackages;
2045
2046 if ( commit.steps_size() ) {
2047
2048 // create the event loop early
2049 auto loop = zyppng::EventLoop::create();
2050
2051 attemptToModify();
2052
2053
2054 // transaction related variables:
2055 //
2056 // the index of the step in the transaction list that we currenty execute.
2057 // this can be -1
2058 int currentStepId = -1;
2059
2060 // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
2061 // the script fd, once we receive it we set this flag to true and ignore all output
2062 // that is written to the pipe ( aside from buffering it ) until we finalize the current report
2063 // and start a new one
2064 bool gotEndOfScript = false;
2065
2066 // the possible reports we emit during the transaction
2067 std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
2068 std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2069 std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2070 std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2071 std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2072
2073 // this will be set if we receive a transaction error description
2074 std::optional<zpt::TransactionError> transactionError;
2075
2076 // infos about the currently executed script, empty if no script is currently executed
2077 std::string currentScriptType;
2078 std::string currentScriptPackage;
2079
2080 // buffer to collect rpm output per report, this will be written to the log once the
2081 // report ends
2082 std::string rpmmsg;
2083
2084 // maximum number of lines that we are buffering in rpmmsg
2085 constexpr auto MAXRPMMESSAGELINES = 10000;
2086
2087 // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2088 unsigned lineno = 0;
2089
2090 // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2091 auto msgSource = zyppng::AsyncDataSource::create();
2092 auto scriptSource = zyppng::AsyncDataSource::create();
2093
2094
2095 // helper function that sends RPM output to the currently active report, writing a warning to the log
2096 // if there is none
2097 const auto &sendRpmLineToReport = [&]( const std::string &line ){
2098
2099 const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2100 callback::UserData cmdout(cType);
2101 if ( currentStepId >= 0 )
2102 cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2103 cmdout.set( "line", line );
2104 report->report(cmdout);
2105 };
2106
2107 if ( installreport ) {
2108 sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2109 } else if ( uninstallreport ) {
2110 sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2111 } else if ( scriptreport ) {
2112 sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2113 } else if ( transactionreport ) {
2114 sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2115 } else if ( cleanupreport ) {
2116 sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2117 } else {
2118 WAR << "Got rpm output without active report " << line; // no endl! - readLine does not trim
2119 }
2120
2121 // remember rpm output
2122 if ( lineno >= MAXRPMMESSAGELINES ) {
2123 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2124 return;
2125 }
2126 rpmmsg += line;
2127 if ( line.back() != '\n' )
2128 rpmmsg += '\n';
2129 };
2130
2131
2132 // callback and helper function to process data that is received on the script FD
2133 const auto &processDataFromScriptFd = [&](){
2134
2135 while ( scriptSource->canReadLine() ) {
2136
2137 if ( gotEndOfScript )
2138 return;
2139
2140 std::string l = scriptSource->readLine().asString();
2141 if( str::endsWith( l, endOfScriptTag ) ) {
2142 gotEndOfScript = true;
2143 std::string::size_type rawsize { l.size() - endOfScriptTag.size() };
2144 if ( not rawsize )
2145 return;
2146 l = l.substr( 0, rawsize );
2147 }
2148 L_DBG("zypp-rpm") << "[rpm> " << l; // no endl! - readLine does not trim
2149 sendRpmLineToReport( l );
2150 }
2151 };
2152 scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2153
2154 // helper function that just waits until the end of script tag was received on the scriptSource
2155 const auto &waitForScriptEnd = [&]() {
2156
2157 // nothing to wait for
2158 if ( gotEndOfScript )
2159 return;
2160
2161 // we process all available data
2162 processDataFromScriptFd();
2163
2164 // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2165 while ( scriptSource->canRead() && !gotEndOfScript ) {
2166 // readyRead will trigger processDataFromScriptFd so no need to call it again
2167 // we still got nothing, lets wait for more
2168 scriptSource->waitForReadyRead( 100 );
2169 }
2170 };
2171
2172 const auto &aboutToStartNewReport = [&](){
2173
2174 if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2175 ERR << "There is still a running report, this is a bug" << std::endl;
2176 assert(false);
2177 }
2178
2179 gotEndOfScript = false;
2180 };
2181
2182 const auto &writeRpmMsgToHistory = [&](){
2183 if ( rpmmsg.size() == 0 )
2184 return;
2185
2186 if ( lineno >= MAXRPMMESSAGELINES )
2187 rpmmsg += "[truncated]\n";
2188
2189 std::ostringstream sstr;
2190 sstr << "rpm output:" << endl << rpmmsg << endl;
2191 HistoryLog().comment(sstr.str());
2192 };
2193
2194 // helper function that closes the current report and cleans up the ressources
2195 const auto &finalizeCurrentReport = [&]() {
2196 sat::Transaction::Step *step = nullptr;
2197 Resolvable::constPtr resObj;
2198 if ( currentStepId >= 0 ) {
2199 step = &steps.at(currentStepId);
2200 resObj = makeResObject( step->satSolvable() );
2201 }
2202
2203 if ( installreport ) {
2204 waitForScriptEnd();
2205 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2206
2208 str::form("%s install failed", step->ident().c_str()),
2209 true /*timestamp*/);
2210
2211 writeRpmMsgToHistory();
2212
2213 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2214 } else {
2215 ( *installreport)->progress( 100, resObj );
2216 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2217
2218 if ( currentStepId >= 0 )
2219 locCache.value().erase( currentStepId );
2220 successfullyInstalledPackages.push_back( step->satSolvable() );
2221
2222 PoolItem citem( *step );
2223 if ( !( flags & rpm::RPMINST_TEST ) ) {
2224 // @TODO are we really doing this just for install?
2225 if ( citem.isNeedreboot() ) {
2226 auto rebootNeededFile = root() / "/run/reboot-needed";
2227 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2228 filesystem::touch( rebootNeededFile );
2229 }
2231 HistoryLog().install(citem);
2232 }
2233
2235 str::form("%s installed ok", step->ident().c_str()),
2236 true /*timestamp*/);
2237
2238 writeRpmMsgToHistory();
2239 }
2240 }
2241 if ( uninstallreport ) {
2242 waitForScriptEnd();
2243 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2244
2246 str::form("%s uninstall failed", step->ident().c_str()),
2247 true /*timestamp*/);
2248
2249 writeRpmMsgToHistory();
2250
2251 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2252 } else {
2253 ( *uninstallreport)->progress( 100, resObj );
2254 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2255
2256 PoolItem citem( *step );
2257 HistoryLog().remove(citem);
2258
2260 str::form("%s removed ok", step->ident().c_str()),
2261 true /*timestamp*/);
2262
2263 writeRpmMsgToHistory();
2264 }
2265 }
2266 if ( scriptreport ) {
2267 waitForScriptEnd();
2268 ( *scriptreport)->progress( 100, resObj );
2269 ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2270 }
2271 if ( transactionreport ) {
2272 waitForScriptEnd();
2273 ( *transactionreport)->progress( 100 );
2274 ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2275 }
2276 if ( cleanupreport ) {
2277 waitForScriptEnd();
2278 ( *cleanupreport)->progress( 100 );
2279 ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2280 }
2281 currentStepId = -1;
2282 lineno = 0;
2283 rpmmsg.clear();
2284 currentScriptType.clear();
2285 currentScriptPackage.clear();
2286 installreport.reset();
2287 uninstallreport.reset();
2288 scriptreport.reset();
2289 transactionreport.reset();
2290 cleanupreport.reset();
2291 };
2292
2293 // This sets up the process and pushes the required transactions steps to it
2294 // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2295 //
2296 // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2297 // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2298 // 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
2299
2300 constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2301
2302 const char *argv[] = {
2303 //"gdbserver",
2304 //"localhost:10001",
2305 zyppRpmBinary.data(),
2306 nullptr
2307 };
2308 auto prog = zyppng::Process::create();
2309
2310 // we set up a pipe to communicate with the process, it is too dangerous to use stdout since librpm
2311 // might print to it.
2312 auto messagePipe = zyppng::Pipe::create();
2313 if ( !messagePipe )
2314 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2315
2316 // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2317 // way than a FD to redirect that output
2318 auto scriptPipe = zyppng::Pipe::create();
2319 if ( !scriptPipe )
2320 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2321
2322 prog->addFd( messagePipe->writeFd );
2323 prog->addFd( scriptPipe->writeFd );
2324
2325 // set up the AsyncDataSource to read script output
2326 if ( !scriptSource->open( scriptPipe->readFd ) )
2327 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2328
2329 prog->sigStarted().connect( [&](){
2330
2331 // close the ends of the pipes we do not care about
2332 messagePipe->unrefWrite();
2333 scriptPipe->unrefWrite();
2334
2335 // read the stdout and forward it to our log
2336 prog->stdoutDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2337 while( prog->stdoutDevice()->canReadLine() ) {
2338 L_DBG("zypp-rpm") << "<stdout> " << prog->stdoutDevice()->readLine().asStringView(); // no endl! - readLine does not trim
2339 }
2340 });
2341
2342 // read the stderr and forward it to our log
2343 prog->stderrDevice()->connectFunc( &zyppng::IODevice::sigReadyRead, [&](){
2344 while( prog->stderrDevice()->canReadLine() ) {
2345 L_ERR("zypp-rpm") << "<stderr> " << prog->stderrDevice()->readLine().asStringView(); // no endl! - readLine does not trim
2346 }
2347 });
2348
2349 {
2350 // write the commit message in blocking mode
2351 const auto outFd = prog->stdinFd();
2352 OnScopeExit unblock([&](){
2353 io::setFDBlocking( outFd, false );
2354 });
2355 io::setFDBlocking( outFd );
2356
2357 // first we push the commit information to the process, starting with the byte size
2358 zyppng::rpc::HeaderSizeType msgSize = commit.ByteSizeLong();
2359 const auto written = zyppng::eintrSafeCall( ::write, outFd, &msgSize, sizeof(zyppng::rpc::HeaderSizeType) );
2360 if ( written != sizeof(zyppng::rpc::HeaderSizeType) ) {
2361 prog->stop( SIGKILL );
2362 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit size to subprocess" ) );
2363 }
2364
2365 zyppng::FileOutputStream fo ( outFd );
2366 if ( !commit.SerializeToZeroCopyStream( &fo ) ) {
2367 prog->stop( SIGKILL );
2368 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2369 }
2370 fo.Flush();
2371 }
2372
2373 });
2374
2375 // this is the source for control messages from zypp-rpm , we will get structured data information
2376 // in form of protobuf messages
2377 if ( !msgSource->open( messagePipe->readFd ) )
2378 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2379
2380 size_t pendingMessageSize = 0;
2381 const auto &processMessages = [&] ( ) {
2382
2383 // lambda function that parses the passed message type and checks if the stepId is a valid offset
2384 // in the steps list.
2385 const auto &parseMsgWithStepId = [&steps]( const auto &m, auto &p ){
2386 if ( !p.ParseFromString( m.value() ) ) {
2387 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2388 return false;
2389 }
2390
2391 auto id = p.stepid();
2392 if ( id < 0 || id >= steps.size() ) {
2393 ERR << "Received invalid stepId: " << id << " in " << m.messagetypename() << " message from zypp-rpm, ignoring." << std::endl;
2394 return false;
2395 }
2396 return true;
2397 };
2398
2399 while ( msgSource->bytesAvailable() ) {
2400
2401 if ( pendingMessageSize == 0 ) {
2402 if ( msgSource->bytesAvailable() >= sizeof( zyppng::rpc::HeaderSizeType ) ) {
2403 msgSource->read( reinterpret_cast<char *>( &pendingMessageSize ), sizeof( zyppng::rpc::HeaderSizeType ) );
2404 }
2405 }
2406
2407 if ( msgSource->bytesAvailable() < pendingMessageSize ) {
2408 return;
2409 }
2410
2411 auto bytes = msgSource->read( pendingMessageSize );
2412 pendingMessageSize = 0;
2413
2414 zypp::proto::Envelope m;
2415 if (! m.ParseFromArray( bytes.data(), bytes.size() ) ) {
2416 // 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
2417 // continue ( this should normally not happen , but code needs to handle it ).
2418 ERR << "Received misformed message from zypp-rpm, ignoring" << std::endl;
2419 return;
2420 }
2421
2422 // due to librpm behaviour we need to make sense of the order of messages we receive
2423 // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2424 // Script related messages. What we do is remember the current step we are in and only close
2425 // the step when we get the start of the next one
2426 const auto &mName = m.messagetypename();
2427 if ( mName == "zypp.proto.target.RpmLog" ) {
2428
2429 zpt::RpmLog p;
2430 if ( !p.ParseFromString( m.value() ) ) {
2431 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2432 continue;
2433 }
2434 ( p.level() >= RPMLOG_ERR ? L_ERR("zypp-rpm")
2435 : p.level() >= RPMLOG_WARNING ? L_WAR("zypp-rpm")
2436 : L_DBG("zypp-rpm") ) << "[rpm " << p.level() << "> " << p.line(); // no endl! - readLine does not trim
2437 report.sendLoglineRpm( p.line(), p.level() );
2438
2439 } else if ( mName == "zypp.proto.target.PackageBegin" ) {
2440 finalizeCurrentReport();
2441
2442 zpt::PackageBegin p;
2443 if ( !parseMsgWithStepId( m, p ) )
2444 continue;
2445
2446 aboutToStartNewReport();
2447
2448 auto & step = steps.at( p.stepid() );
2449 currentStepId = p.stepid();
2450 if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2451 uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2452 ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2453 } else {
2454 installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2455 ( *installreport )->start( makeResObject( step.satSolvable() ) );
2456 }
2457
2458 } else if ( mName == "zypp.proto.target.PackageFinished" ) {
2459 zpt::PackageFinished p;
2460 if ( !parseMsgWithStepId( m, p ) )
2461 continue;
2462
2463 if ( p.stepid() < 0 || p.stepid() > steps.size() )
2464 continue;
2465
2466 // here we only set the step stage to done, we however need to wait for the next start in order to send
2467 // the finished report since there might be a error pending to be reported
2468 steps[ p.stepid() ].stepStage( sat::Transaction::STEP_DONE );
2469
2470 } else if ( mName == "zypp.proto.target.PackageProgress" ) {
2471 zpt::PackageProgress p;
2472 if ( !parseMsgWithStepId( m, p ) )
2473 continue;
2474
2475 if ( uninstallreport )
2476 (*uninstallreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2477 else if ( installreport )
2478 (*installreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2479 else
2480 ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2481
2482 } else if ( mName == "zypp.proto.target.PackageError" ) {
2483 zpt::PackageError p;
2484 if ( !parseMsgWithStepId( m, p ) )
2485 continue;
2486
2487 if ( p.stepid() >= 0 && p.stepid() < steps.size() )
2488 steps[ p.stepid() ].stepStage( sat::Transaction::STEP_ERROR );
2489
2490 finalizeCurrentReport();
2491
2492 } else if ( mName == "zypp.proto.target.ScriptBegin" ) {
2493 finalizeCurrentReport();
2494
2495 zpt::ScriptBegin p;
2496 if ( !p.ParseFromString( m.value() ) ) {
2497 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2498 continue;
2499 }
2500
2501 aboutToStartNewReport();
2502
2503 Resolvable::constPtr resPtr;
2504 const auto stepId = p.stepid();
2505 if ( stepId >= 0 && stepId < steps.size() ) {
2506 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2507 }
2508
2509 currentStepId = p.stepid();
2510 scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2511 currentScriptType = p.scripttype();
2512 currentScriptPackage = p.scriptpackage();
2513 (*scriptreport)->start( p.scripttype(), p.scriptpackage(), resPtr );
2514
2515 } else if ( mName == "zypp.proto.target.ScriptFinished" ) {
2516
2517 // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2518
2519 } else if ( mName == "zypp.proto.target.ScriptError" ) {
2520
2521 zpt::ScriptError p;
2522 if ( !p.ParseFromString( m.value() ) ) {
2523 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2524 continue;
2525 }
2526
2527 Resolvable::constPtr resPtr;
2528 const auto stepId = p.stepid();
2529 if ( stepId >= 0 && stepId < steps.size() ) {
2530 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2531
2532 if ( p.fatal() ) {
2533 steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2534 }
2535
2536 }
2537
2539 str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2540 true /*timestamp*/);
2541
2542 writeRpmMsgToHistory();
2543
2544 if ( !scriptreport ) {
2545 ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2546 continue;
2547 }
2548
2549 // before killing the report we need to wait for the script end tag
2550 waitForScriptEnd();
2551 (*scriptreport)->finish( resPtr, p.fatal() ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2552
2553 // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2554 scriptreport.reset();
2555 currentStepId = -1;
2556
2557 } else if ( mName == "zypp.proto.target.CleanupBegin" ) {
2558 finalizeCurrentReport();
2559
2560 zpt::CleanupBegin beg;
2561 if ( !beg.ParseFromString( m.value() ) ) {
2562 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2563 continue;
2564 }
2565
2566 aboutToStartNewReport();
2567 cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2568 (*cleanupreport)->start( beg.nvra() );
2569 } else if ( mName == "zypp.proto.target.CleanupFinished" ) {
2570
2571 finalizeCurrentReport();
2572
2573 } else if ( mName == "zypp.proto.target.CleanupProgress" ) {
2574 zpt::CleanupProgress prog;
2575 if ( !prog.ParseFromString( m.value() ) ) {
2576 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2577 continue;
2578 }
2579
2580 if ( !cleanupreport ) {
2581 ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2582 continue;
2583 }
2584
2585 (*cleanupreport)->progress( prog.amount() );
2586
2587 } else if ( mName == "zypp.proto.target.TransBegin" ) {
2588 finalizeCurrentReport();
2589
2590 zpt::TransBegin beg;
2591 if ( !beg.ParseFromString( m.value() ) ) {
2592 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2593 continue;
2594 }
2595
2596 aboutToStartNewReport();
2597 transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2598 (*transactionreport)->start( beg.name() );
2599 } else if ( mName == "zypp.proto.target.TransFinished" ) {
2600
2601 finalizeCurrentReport();
2602
2603 } else if ( mName == "zypp.proto.target.TransProgress" ) {
2604 zpt::TransProgress prog;
2605 if ( !prog.ParseFromString( m.value() ) ) {
2606 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2607 continue;
2608 }
2609
2610 if ( !transactionreport ) {
2611 ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2612 continue;
2613 }
2614
2615 (*transactionreport)->progress( prog.amount() );
2616 } else if ( mName == "zypp.proto.target.TransactionError" ) {
2617
2618 zpt::TransactionError error;
2619 if ( !error.ParseFromString( m.value() ) ) {
2620 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2621 continue;
2622 }
2623
2624 // this value is checked later
2625 transactionError = std::move(error);
2626
2627 } else {
2628 ERR << "Received unexpected message from zypp-rpm: "<< m.messagetypename() << ", ignoring" << std::endl;
2629 return;
2630 }
2631
2632 }
2633 };
2634 msgSource->connectFunc( &zyppng::AsyncDataSource::sigReadyRead, processMessages );
2635
2636 // track the childs lifetime
2637 int zyppRpmExitCode = -1;
2638 prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2639 zyppRpmExitCode = code;
2640 loop->quit();
2641 });
2642
2643 if ( !prog->start( argv ) ) {
2644 HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2645 ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2646 }
2647
2648 loop->run();
2649
2650 // make sure to read ALL available messages
2651 processMessages();
2652
2653 // we will not receive a new start message , so we need to manually finalize the last report
2654 finalizeCurrentReport();
2655
2656 // make sure to read all data from the log source
2657 bool readMsgs = false;
2658 while( prog->stderrDevice()->canReadLine() ) {
2659 readMsgs = true;
2660 MIL << "zypp-rpm: " << prog->stderrDevice()->readLine().asStringView();
2661 }
2662 while( prog->stdoutDevice()->canReadLine() ) {
2663 readMsgs = true;
2664 MIL << "zypp-rpm: " << prog->stdoutDevice()->readLine().asStringView();
2665 }
2666
2667 while ( scriptSource->canReadLine() ) {
2668 readMsgs = true;
2669 MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2670 }
2671 if ( scriptSource->bytesAvailable() > 0 ) {
2672 readMsgs = true;
2673 MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2674 }
2675 if ( readMsgs )
2676 MIL << std::endl;
2677
2678 switch ( zyppRpmExitCode ) {
2679 // we need to look at the summary, handle finishedwitherrors like no error here
2680 case zypprpm::NoError:
2681 case zypprpm::RpmFinishedWithError:
2682 break;
2683 case zypprpm::RpmFinishedWithTransactionError: {
2684 // here zypp-rpm sent us a error description
2685 if ( transactionError ) {
2686
2687 std::ostringstream sstr;
2688 sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2689 for ( const auto & err : transactionError->problems() ) {
2690 sstr << " " << err.message() << "\n";
2691 }
2692 sstr << std::endl;
2694
2695 } else {
2696 ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more information.") );
2697 }
2698 break;
2699 }
2700 case zypprpm::FailedToOpenDb:
2701 ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2702 break;
2703 case zypprpm::WrongHeaderSize:
2704 case zypprpm::WrongMessageFormat:
2705 ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2706 break;
2707 case zypprpm::RpmInitFailed:
2708 ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2709 break;
2710 case zypprpm::FailedToReadPackage:
2711 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more information.") );
2712 break;
2713 case zypprpm::FailedToAddStepToTransaction:
2714 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more information.") );
2715 break;
2716 case zypprpm::RpmOrderFailed:
2717 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to order the transaction, check the logs for more information.") );
2718 break;
2719 }
2720
2721 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort; ++stepId ) {
2722 auto &step = steps[stepId];
2723 PoolItem citem( step );
2724
2725 if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2726 // other resolvables (non-Package) that are not handled by zypp-rpm
2727 if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2728 // Status is changed as the buddy package buddy
2729 // gets installed/deleted. Handle non-buddies only.
2730 if ( ! citem.buddy() && citem->isKind<Product>() ) {
2731 Product::constPtr p = citem->asKind<Product>();
2732
2733 if ( citem.status().isToBeInstalled() ) {
2734 ERR << "Can't install orphan product without release-package! " << citem << endl;
2735 } else {
2736 // Deleting the corresponding product entry is all we con do.
2737 // So the product will no longer be visible as installed.
2738 std::string referenceFilename( p->referenceFilename() );
2739
2740 if ( referenceFilename.empty() ) {
2741 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2742 } else {
2743 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2744
2745 if ( ! rpm().hasFile( referencePath.asString() ) ) {
2746 // If it's not owned by a package, we can delete it.
2747 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2748 if ( filesystem::unlink( referencePath ) != 0 )
2749 ERR << "Delete orphan product failed: " << referencePath << endl;
2750 } else {
2751 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2752 }
2753 }
2754 }
2756 step.stepStage( sat::Transaction::STEP_DONE );
2757 }
2758 }
2759 }
2760 }
2761 }
2762
2763 // Check presence of update scripts/messages. If aborting,
2764 // at least log omitted scripts.
2765 if ( ! successfullyInstalledPackages.empty() )
2766 {
2767 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2768 successfullyInstalledPackages, abort ) )
2769 {
2770 WAR << "Commit aborted by the user" << endl;
2771 abort = true;
2772 }
2773 // send messages after scripts in case some script generates output,
2774 // that should be kept in t %ghost message file.
2775 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2776 successfullyInstalledPackages,
2777 result_r );
2778 }
2779
2780 // jsc#SLE-5116: Log patch status changes to history
2781 // NOTE: Should be the last action as it may need to reload
2782 // the Target in case of an incomplete transaction.
2783 logPatchStatusChanges( result_r.transaction(), *this );
2784
2785 if ( abort ) {
2786 HistoryLog().comment( "Commit was aborted." );
2788 }
2789 }
2790
2792
2794 {
2795 return _rpm;
2796 }
2797
2798 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2799 {
2800 return _rpm.hasFile(path_str, name_str);
2801 }
2802
2804 namespace
2805 {
2806 parser::ProductFileData baseproductdata( const Pathname & root_r )
2807 {
2809 PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2810
2811 if ( baseproduct.isFile() )
2812 {
2813 try
2814 {
2815 ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2816 }
2817 catch ( const Exception & excpt )
2818 {
2819 ZYPP_CAUGHT( excpt );
2820 }
2821 }
2822 else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
2823 {
2824 ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
2825 }
2826 return ret;
2827 }
2828
2829 inline Pathname staticGuessRoot( const Pathname & root_r )
2830 {
2831 if ( root_r.empty() )
2832 {
2833 // empty root: use existing Target or assume "/"
2834 Pathname ret ( ZConfig::instance().systemRoot() );
2835 if ( ret.empty() )
2836 return Pathname("/");
2837 return ret;
2838 }
2839 return root_r;
2840 }
2841
2842 inline std::string firstNonEmptyLineIn( const Pathname & file_r )
2843 {
2844 std::ifstream idfile( file_r.c_str() );
2845 for( iostr::EachLine in( idfile ); in; in.next() )
2846 {
2847 std::string line( str::trim( *in ) );
2848 if ( ! line.empty() )
2849 return line;
2850 }
2851 return std::string();
2852 }
2853 } // namespace
2855
2857 {
2858 ResPool pool(ResPool::instance());
2859 for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
2860 {
2861 Product::constPtr p = (*it)->asKind<Product>();
2862 if ( p->isTargetDistribution() )
2863 return p;
2864 }
2865 return nullptr;
2866 }
2867
2869 {
2870 const Pathname needroot( staticGuessRoot(root_r) );
2871 const Target_constPtr target( getZYpp()->getTarget() );
2872 if ( target && target->root() == needroot )
2873 return target->requestedLocales();
2874 return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
2875 }
2876
2878 {
2879 MIL << "updateAutoInstalled if changed..." << endl;
2880 SolvIdentFile::Data newdata;
2881 for ( auto id : sat::Pool::instance().autoInstalled() )
2882 newdata.insert( IdString(id) ); // explicit ctor!
2883 _autoInstalledFile.setData( std::move(newdata) );
2884 }
2885
2887 { return baseproductdata( _root ).registerTarget(); }
2888 // static version:
2889 std::string TargetImpl::targetDistribution( const Pathname & root_r )
2890 { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
2891
2893 { return baseproductdata( _root ).registerRelease(); }
2894 // static version:
2896 { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
2897
2899 { return baseproductdata( _root ).registerFlavor(); }
2900 // static version:
2902 { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
2903
2905 {
2907 parser::ProductFileData pdata( baseproductdata( _root ) );
2908 ret.shortName = pdata.shortName();
2909 ret.summary = pdata.summary();
2910 return ret;
2911 }
2912 // static version:
2914 {
2916 parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
2917 ret.shortName = pdata.shortName();
2918 ret.summary = pdata.summary();
2919 return ret;
2920 }
2921
2923 {
2924 if ( _distributionVersion.empty() )
2925 {
2927 if ( !_distributionVersion.empty() )
2928 MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
2929 }
2930 return _distributionVersion;
2931 }
2932 // static version
2933 std::string TargetImpl::distributionVersion( const Pathname & root_r )
2934 {
2935 std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
2936 if ( distributionVersion.empty() )
2937 {
2938 // ...But the baseproduct method is not expected to work on RedHat derivatives.
2939 // On RHEL, Fedora and others the "product version" is determined by the first package
2940 // providing 'system-release'. This value is not hardcoded in YUM and can be configured
2941 // with the $distroverpkg variable.
2942 scoped_ptr<rpm::RpmDb> tmprpmdb;
2943 if ( ZConfig::instance().systemRoot() == Pathname() )
2944 {
2945 try
2946 {
2947 tmprpmdb.reset( new rpm::RpmDb );
2948 tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
2949 }
2950 catch( ... )
2951 {
2952 return "";
2953 }
2954 }
2957 distributionVersion = it->tag_version();
2958 }
2959 return distributionVersion;
2960 }
2961
2962
2964 {
2965 return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
2966 }
2967 // static version:
2968 std::string TargetImpl::distributionFlavor( const Pathname & root_r )
2969 {
2970 return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
2971 }
2972
2974 namespace
2975 {
2976 std::string guessAnonymousUniqueId( const Pathname & root_r )
2977 {
2978 // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
2979 std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
2980 if ( ret.empty() && root_r != "/" )
2981 {
2982 // if it has nonoe, use the outer systems one
2983 ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
2984 }
2985 return ret;
2986 }
2987 }
2988
2990 {
2991 return guessAnonymousUniqueId( root() );
2992 }
2993 // static version:
2994 std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
2995 {
2996 return guessAnonymousUniqueId( staticGuessRoot(root_r) );
2997 }
2998
3000
3002 {
3003 MIL << "New VendorAttr: " << vendorAttr_r << endl;
3004 _vendorAttr = std::move(vendorAttr_r);
3005 }
3007
3008 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3009 {
3010 // provide on local disk
3011 ManagedFile localfile = provideSrcPackage(srcPackage_r);
3012 // create a installation progress report proxy
3013 RpmInstallPackageReceiver progress( srcPackage_r );
3014 progress.connect(); // disconnected on destruction.
3015 // install it
3016 rpm().installPackage ( localfile );
3017 }
3018
3019 ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3020 {
3021 // provide on local disk
3022 repo::RepoMediaAccess access_r;
3023 repo::SrcPackageProvider prov( access_r );
3024 return prov.provideSrcPackage( srcPackage_r );
3025 }
3027 } // namespace target
3030} // namespace zypp
#define idstr(V)
const Pathname & _root
Definition: RepoManager.cc:144
#define MAXRPMMESSAGELINES
Definition: RpmDb.cc:63
Pathname _mountpoint
Definition: TargetImpl.cc:277
#define SUBST_IF(PAT, VAL)
ZYppCommitResult & _result
Definition: TargetImpl.cc:1561
TrueBool _guard
Definition: TargetImpl.cc:1560
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:174
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:74
bool upgradeMode() const
Definition: Resolver.cc:97
bool upgradingRepos() const
Whether there is at least one UpgradeRepo request pending.
Definition: Resolver.cc:136
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: Resolver.cc:126
std::string distroverpkg() const
Package telling the "product version" on systems not using /etc/product.d/baseproduct.
Definition: ZConfig.cc:1176
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:2892
const VendorAttr & vendorAttr() const
The targets current vendor equivalence settings.
Definition: TargetImpl.h:201
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: TargetImpl.cc:2886
LocaleSet requestedLocales() const
Languages to be supported by the system.
Definition: TargetImpl.h:157
void updateAutoInstalled()
Update the database of autoinstalled packages.
Definition: TargetImpl.cc:2877
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Provides a source package on the Target.
Definition: TargetImpl.cc:3019
Pathname _root
Path to the target.
Definition: TargetImpl.h:224
RequestedLocalesFile _requestedLocalesFile
Requested Locales database.
Definition: TargetImpl.h:228
void createLastDistributionFlavorCache() const
generates a cache of the last product flavor
Definition: TargetImpl.cc:909
std::string _distributionVersion
Cache distributionVersion.
Definition: TargetImpl.h:234
std::list< PoolItem > PoolItemList
list of pool items
Definition: TargetImpl.h:59
rpm::RpmDb _rpm
RPM database.
Definition: TargetImpl.h:226
rpm::RpmDb & rpm()
The RPM database.
Definition: TargetImpl.cc:2793
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:2922
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:230
Product::constPtr baseProduct() const
returns the target base installed product, also known as the distribution or platform.
Definition: TargetImpl.cc:2856
Target::DistributionLabel distributionLabel() const
This is shortName and summary attribute of the installed base product.
Definition: TargetImpl.cc:2904
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:2798
HardLocksFile _hardLocksFile
Hard-Locks database.
Definition: TargetImpl.h:232
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:2963
void commitInSingleTransaction(const ZYppCommitPolicy &policy_r, CommitPackageCache &packageCache_r, ZYppCommitResult &result_r)
Commit ordered changes (internal helper)
Definition: TargetImpl.cc:1909
void installSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Install a source package on the Target.
Definition: TargetImpl.cc:3008
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:236
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:2989
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:2898
Interface to the rpm program.
Definition: RpmDb.h:48
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition: RpmDb.cc:1613
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:1810
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 dirForEach(const Pathname &dir_r, function< bool(const Pathname &, const char *const)> fnc_r)
Invoke callback function fnc_r for each entry in directory dir_r.
Definition: PathInfo.cc:584
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 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::string asString(const DefaultIntegral< Tp, TInitial > &obj)
std::unordered_set< Locale > LocaleSet
Definition: Locale.h:27
ResObject::Ptr makeResObject(const sat::Solvable &solvable_r)
Create ResObject from sat::Solvable.
Definition: ResObject.cc:43
std::list< UpdateNotificationFile > UpdateNotifications
@ DownloadInHeaps
Similar to DownloadInAdvance, but try to split the transaction into heaps, where at the end of each h...
Definition: DownloadMode.h:29
@ DownloadOnly
Just download all packages to the local cache.
Definition: DownloadMode.h:25
@ DownloadAsNeeded
Alternating download and install.
Definition: DownloadMode.h:32
@ DownloadInAdvance
First download all packages to the local cache.
Definition: DownloadMode.h:27
@ DownloadDefault
libzypp will decide what to do.
Definition: DownloadMode.h:24
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:178
Convenience SendReport<rpm::SingleTransReport> wrapper.
Definition: TargetImpl.cc:1863
void report(const callback::UserData &userData_r)
Definition: TargetImpl.cc:1897
void sendLoglineRpm(const std::string &line_r, unsigned rpmlevel_r)
Convenience to send a contentLogline translating a rpm loglevel.
Definition: TargetImpl.cc:1873
void sendLogline(const std::string &line_r, ReportType::loglevel level_r=ReportType::loglevel::msg)
Convenience to send a contentLogline.
Definition: TargetImpl.cc:1865
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:426
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:418
#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