libzypp 17.31.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 std::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
1218 Pathname needrebootFile { Pathname::assertprefix( root(), ZConfig::instance().needrebootFile() ) };
1219 if ( PathInfo( needrebootFile ).isFile() )
1220 needrebootSpec.parseFrom( needrebootFile );
1221
1222 Pathname needrebootDir { Pathname::assertprefix( root(), ZConfig::instance().needrebootPath() ) };
1223 if ( PathInfo( needrebootDir ).isDir() )
1224 {
1225 static const StrMatcher isRpmConfigBackup( "\\.rpm(new|save|orig)$", Match::REGEX );
1226
1228 [&]( const Pathname & dir_r, const char *const str_r )->bool
1229 {
1230 if ( ! isRpmConfigBackup( str_r ) )
1231 {
1232 Pathname needrebootFile { needrebootDir / str_r };
1233 if ( PathInfo( needrebootFile ).isFile() )
1234 needrebootSpec.parseFrom( needrebootFile );
1235 }
1236 return true;
1237 });
1238 }
1239 satpool.setNeedrebootSpec( std::move(needrebootSpec) );
1240 }
1241
1242 if ( ZConfig::instance().apply_locks_file() )
1243 {
1244 const HardLocksFile::Data & hardLocks( _hardLocksFile.data() );
1245 if ( ! hardLocks.empty() )
1246 {
1248 }
1249 }
1250
1251 // now that the target is loaded, we can cache the flavor
1253
1254 MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl;
1255 }
1256
1258 //
1259 // COMMIT
1260 //
1263 {
1264 // ----------------------------------------------------------------- //
1265 ZYppCommitPolicy policy_r( policy_rX );
1266 bool explicitDryRun = policy_r.dryRun(); // explicit dry run will trigger a fileconflict check, implicit (download-only) not.
1267
1268 ShutdownLock lck("Zypp commit running.");
1269
1270 // Fake outstanding YCP fix: Honour restriction to media 1
1271 // at installation, but install all remaining packages if post-boot.
1272 if ( policy_r.restrictToMedia() > 1 )
1273 policy_r.allMedia();
1274
1275 if ( policy_r.downloadMode() == DownloadDefault ) {
1276 if ( root() == "/" )
1277 policy_r.downloadMode(DownloadInHeaps);
1278 else {
1279 if ( policy_r.singleTransModeEnabled() )
1281 else
1283 }
1284 }
1285 // DownloadOnly implies dry-run.
1286 else if ( policy_r.downloadMode() == DownloadOnly )
1287 policy_r.dryRun( true );
1288 // ----------------------------------------------------------------- //
1289
1290 MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
1291
1293 // Compute transaction:
1295 ZYppCommitResult result( root() );
1296 result.rTransaction() = pool_r.resolver().getTransaction();
1297 result.rTransaction().order();
1298 // steps: this is our todo-list
1300 if ( policy_r.restrictToMedia() )
1301 {
1302 // Collect until the 1st package from an unwanted media occurs.
1303 // Further collection could violate install order.
1304 MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
1305 for_( it, result.transaction().begin(), result.transaction().end() )
1306 {
1307 if ( makeResObject( *it )->mediaNr() > 1 )
1308 break;
1309 steps.push_back( *it );
1310 }
1311 }
1312 else
1313 {
1314 result.rTransactionStepList().insert( steps.end(), result.transaction().begin(), result.transaction().end() );
1315 }
1316 MIL << "Todo: " << result << endl;
1317
1319 // Prepare execution of commit plugins:
1321 PluginExecutor commitPlugins;
1322 if ( root() == "/" && ! policy_r.dryRun() )
1323 {
1324 commitPlugins.load( ZConfig::instance().pluginsPath()/"commit" );
1325 }
1326 if ( commitPlugins )
1327 commitPlugins.send( transactionPluginFrame( "COMMITBEGIN", steps ) );
1328
1330 // Write out a testcase if we're in dist upgrade mode.
1332 if ( pool_r.resolver().upgradeMode() || pool_r.resolver().upgradingRepos() )
1333 {
1334 if ( ! policy_r.dryRun() )
1335 {
1337 }
1338 else
1339 {
1340 DBG << "dryRun: Not writing upgrade testcase." << endl;
1341 }
1342 }
1343
1345 // Store non-package data:
1347 if ( ! policy_r.dryRun() )
1348 {
1350 // requested locales
1352 // autoinstalled
1353 {
1354 SolvIdentFile::Data newdata;
1355 for ( sat::Queue::value_type id : result.rTransaction().autoInstalled() )
1356 newdata.insert( IdString(id) );
1357 _autoInstalledFile.setData( newdata );
1358 }
1359 // hard locks
1360 if ( ZConfig::instance().apply_locks_file() )
1361 {
1362 HardLocksFile::Data newdata;
1363 pool_r.getHardLockQueries( newdata );
1364 _hardLocksFile.setData( newdata );
1365 }
1366 }
1367 else
1368 {
1369 DBG << "dryRun: Not storing non-package data." << endl;
1370 }
1371
1373 // First collect and display all messages
1374 // associated with patches to be installed.
1376 if ( ! policy_r.dryRun() )
1377 {
1378 for_( it, steps.begin(), steps.end() )
1379 {
1380 if ( ! it->satSolvable().isKind<Patch>() )
1381 continue;
1382
1383 PoolItem pi( *it );
1384 if ( ! pi.status().isToBeInstalled() )
1385 continue;
1386
1387 Patch::constPtr patch( asKind<Patch>(pi.resolvable()) );
1388 if ( ! patch ||patch->message().empty() )
1389 continue;
1390
1391 MIL << "Show message for " << patch << endl;
1393 if ( ! report->show( patch ) )
1394 {
1395 WAR << "commit aborted by the user" << endl;
1397 }
1398 }
1399 }
1400 else
1401 {
1402 DBG << "dryRun: Not checking patch messages." << endl;
1403 }
1404
1406 // Remove/install packages.
1408
1409 bool singleTransMode = policy_r.singleTransModeEnabled();
1410
1411 DBG << "commit log file is set to: " << HistoryLog::fname() << endl;
1412 if ( ! policy_r.dryRun() || policy_r.downloadMode() == DownloadOnly || singleTransMode )
1413 {
1414 // Prepare the package cache. Pass all items requiring download.
1415 CommitPackageCache packageCache;
1416 packageCache.setCommitList( steps.begin(), steps.end() );
1417
1418 bool miss = false;
1419 if ( policy_r.downloadMode() != DownloadAsNeeded || singleTransMode )
1420 {
1421 // Preload the cache. Until now this means pre-loading all packages.
1422 // Once DownloadInHeaps is fully implemented, this will change and
1423 // we may actually have more than one heap.
1424 for_( it, steps.begin(), steps.end() )
1425 {
1426 switch ( it->stepType() )
1427 {
1430 // proceed: only install actionas may require download.
1431 break;
1432
1433 default:
1434 // next: no download for or non-packages and delete actions.
1435 continue;
1436 break;
1437 }
1438
1439 PoolItem pi( *it );
1440 if ( pi->isKind<Package>() || pi->isKind<SrcPackage>() )
1441 {
1442 ManagedFile localfile;
1443 try
1444 {
1445 localfile = packageCache.get( pi );
1446 localfile.resetDispose(); // keep the package file in the cache
1447 }
1448 catch ( const AbortRequestException & exp )
1449 {
1450 it->stepStage( sat::Transaction::STEP_ERROR );
1451 miss = true;
1452 WAR << "commit cache preload aborted by the user" << endl;
1454 break;
1455 }
1456 catch ( const SkipRequestException & exp )
1457 {
1458 ZYPP_CAUGHT( exp );
1459 it->stepStage( sat::Transaction::STEP_ERROR );
1460 miss = true;
1461 WAR << "Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1462 continue;
1463 }
1464 catch ( const Exception & exp )
1465 {
1466 // bnc #395704: missing catch causes abort.
1467 // TODO see if packageCache fails to handle errors correctly.
1468 ZYPP_CAUGHT( exp );
1469 it->stepStage( sat::Transaction::STEP_ERROR );
1470 miss = true;
1471 INT << "Unexpected Error: Skipping cache preload package " << pi->asKind<Package>() << " in commit" << endl;
1472 continue;
1473 }
1474 }
1475 }
1476 packageCache.preloaded( true ); // try to avoid duplicate infoInCache CBs in commit
1477 }
1478
1479 if ( miss )
1480 {
1481 ERR << "Some packages could not be provided. Aborting commit."<< endl;
1482 }
1483 else
1484 {
1485 if ( ! policy_r.dryRun() )
1486 {
1487 if ( policy_r.singleTransModeEnabled() ) {
1488 commitInSingleTransaction( policy_r, packageCache, result );
1489 } else {
1490 // if cache is preloaded, check for file conflicts
1491 commitFindFileConflicts( policy_r, result );
1492 commit( policy_r, packageCache, result );
1493 }
1494 }
1495 else
1496 {
1497 DBG << "dryRun/downloadOnly: Not installing/deleting anything." << endl;
1498 if ( explicitDryRun ) {
1499 if ( policy_r.singleTransModeEnabled() ) {
1500 // single trans mode does a test install via rpm
1501 commitInSingleTransaction( policy_r, packageCache, result );
1502 } else {
1503 // if cache is preloaded, check for file conflicts
1504 commitFindFileConflicts( policy_r, result );
1505 }
1506 }
1507 }
1508 }
1509 }
1510 else
1511 {
1512 DBG << "dryRun: Not downloading/installing/deleting anything." << endl;
1513 if ( explicitDryRun ) {
1514 // if cache is preloaded, check for file conflicts
1515 commitFindFileConflicts( policy_r, result );
1516 }
1517 }
1518
1519 {
1520 // NOTE: Removing rpm in a transaction, rpm removes the /var/lib/rpm compat symlink.
1521 // We re-create it, in case it was lost to prevent legacy tools from accidentally
1522 // assuming no database is present.
1523 if ( ! PathInfo(_root/"/var/lib/rpm",PathInfo::LSTAT).isExist()
1524 && PathInfo(_root/"/usr/lib/sysimage/rpm").isDir() ) {
1525 WAR << "(rpm removed in commit?) Inject missing /var/lib/rpm compat symlink to /usr/lib/sysimage/rpm" << endl;
1526 filesystem::assert_dir( _root/"/var/lib" );
1527 filesystem::symlink( "../../usr/lib/sysimage/rpm", _root/"/var/lib/rpm" );
1528 }
1529 }
1530
1532 // Send result to commit plugins:
1534 if ( commitPlugins )
1535 commitPlugins.send( transactionPluginFrame( "COMMITEND", steps ) );
1536
1538 // Try to rebuild solv file while rpm database is still in cache
1540 if ( ! policy_r.dryRun() )
1541 {
1542 buildCache();
1543 }
1544
1545 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
1546 return result;
1547 }
1548
1550 //
1551 // COMMIT internal
1552 //
1554 namespace
1555 {
1556 struct NotifyAttemptToModify
1557 {
1558 NotifyAttemptToModify( ZYppCommitResult & result_r ) : _result( result_r ) {}
1559
1560 void operator()()
1561 { if ( _guard ) { _result.attemptToModify( true ); _guard = false; } }
1562
1563 TrueBool _guard;
1564 ZYppCommitResult & _result;
1565 };
1566 } // namespace
1567
1568 void TargetImpl::commit( const ZYppCommitPolicy & policy_r,
1569 CommitPackageCache & packageCache_r,
1570 ZYppCommitResult & result_r )
1571 {
1572 // steps: this is our todo-list
1574 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1575
1577
1578 // Send notification once upon 1st call to rpm
1579 NotifyAttemptToModify attemptToModify( result_r );
1580
1581 bool abort = false;
1582
1583 // bsc#1181328: Some systemd tools require /proc to be mounted
1584 AssertProcMounted assertProcMounted( _root );
1585
1586 RpmPostTransCollector postTransCollector( _root );
1587 std::vector<sat::Solvable> successfullyInstalledPackages;
1588 TargetImpl::PoolItemList remaining;
1589
1590 for_( step, steps.begin(), steps.end() )
1591 {
1592 PoolItem citem( *step );
1593 if ( step->stepType() == sat::Transaction::TRANSACTION_IGNORE )
1594 {
1595 if ( citem->isKind<Package>() )
1596 {
1597 // for packages this means being obsoleted (by rpm)
1598 // thius no additional action is needed.
1599 step->stepStage( sat::Transaction::STEP_DONE );
1600 continue;
1601 }
1602 }
1603
1604 if ( citem->isKind<Package>() )
1605 {
1606 Package::constPtr p = citem->asKind<Package>();
1607 if ( citem.status().isToBeInstalled() )
1608 {
1609 ManagedFile localfile;
1610 try
1611 {
1612 localfile = packageCache_r.get( citem );
1613 }
1614 catch ( const AbortRequestException &e )
1615 {
1616 WAR << "commit aborted by the user" << endl;
1617 abort = true;
1618 step->stepStage( sat::Transaction::STEP_ERROR );
1619 break;
1620 }
1621 catch ( const SkipRequestException &e )
1622 {
1623 ZYPP_CAUGHT( e );
1624 WAR << "Skipping package " << p << " in commit" << endl;
1625 step->stepStage( sat::Transaction::STEP_ERROR );
1626 continue;
1627 }
1628 catch ( const Exception &e )
1629 {
1630 // bnc #395704: missing catch causes abort.
1631 // TODO see if packageCache fails to handle errors correctly.
1632 ZYPP_CAUGHT( e );
1633 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
1634 step->stepStage( sat::Transaction::STEP_ERROR );
1635 continue;
1636 }
1637
1638 // create a installation progress report proxy
1639 RpmInstallPackageReceiver progress( citem.resolvable() );
1640 progress.connect(); // disconnected on destruction.
1641
1642 bool success = false;
1643 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1644 // Why force and nodeps?
1645 //
1646 // Because zypp builds the transaction and the resolver asserts that
1647 // everything is fine.
1648 // We use rpm just to unpack and register the package in the database.
1649 // We do this step by step, so rpm is not aware of the bigger context.
1650 // So we turn off rpms internal checks, because we do it inside zypp.
1651 flags |= rpm::RPMINST_NODEPS;
1652 flags |= rpm::RPMINST_FORCE;
1653 //
1654 if (p->multiversionInstall()) flags |= rpm::RPMINST_NOUPGRADE;
1655 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1656 if (policy_r.rpmExcludeDocs()) flags |= rpm::RPMINST_EXCLUDEDOCS;
1657 if (policy_r.rpmNoSignature()) flags |= rpm::RPMINST_NOSIGNATURE;
1658
1659 attemptToModify();
1660 try
1661 {
1663 if ( postTransCollector.collectScriptFromPackage( localfile ) )
1664 flags |= rpm::RPMINST_NOPOSTTRANS;
1665 rpm().installPackage( localfile, flags );
1666 HistoryLog().install(citem);
1667
1668 if ( progress.aborted() )
1669 {
1670 WAR << "commit aborted by the user" << endl;
1671 localfile.resetDispose(); // keep the package file in the cache
1672 abort = true;
1673 step->stepStage( sat::Transaction::STEP_ERROR );
1674 break;
1675 }
1676 else
1677 {
1678 if ( citem.isNeedreboot() ) {
1679 auto rebootNeededFile = root() / "/run/reboot-needed";
1680 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
1681 filesystem::touch( rebootNeededFile );
1682 }
1683
1684 success = true;
1685 step->stepStage( sat::Transaction::STEP_DONE );
1686 }
1687 }
1688 catch ( Exception & excpt_r )
1689 {
1690 ZYPP_CAUGHT(excpt_r);
1691 localfile.resetDispose(); // keep the package file in the cache
1692
1693 if ( policy_r.dryRun() )
1694 {
1695 WAR << "dry run failed" << endl;
1696 step->stepStage( sat::Transaction::STEP_ERROR );
1697 break;
1698 }
1699 // else
1700 if ( progress.aborted() )
1701 {
1702 WAR << "commit aborted by the user" << endl;
1703 abort = true;
1704 }
1705 else
1706 {
1707 WAR << "Install failed" << endl;
1708 }
1709 step->stepStage( sat::Transaction::STEP_ERROR );
1710 break; // stop
1711 }
1712
1713 if ( success && !policy_r.dryRun() )
1714 {
1716 successfullyInstalledPackages.push_back( citem.satSolvable() );
1717 step->stepStage( sat::Transaction::STEP_DONE );
1718 }
1719 }
1720 else
1721 {
1722 RpmRemovePackageReceiver progress( citem.resolvable() );
1723 progress.connect(); // disconnected on destruction.
1724
1725 bool success = false;
1726 rpm::RpmInstFlags flags( policy_r.rpmInstFlags() & rpm::RPMINST_JUSTDB );
1727 flags |= rpm::RPMINST_NODEPS;
1728 if (policy_r.dryRun()) flags |= rpm::RPMINST_TEST;
1729
1730 attemptToModify();
1731 try
1732 {
1733 rpm().removePackage( p, flags );
1734 HistoryLog().remove(citem);
1735
1736 if ( progress.aborted() )
1737 {
1738 WAR << "commit aborted by the user" << endl;
1739 abort = true;
1740 step->stepStage( sat::Transaction::STEP_ERROR );
1741 break;
1742 }
1743 else
1744 {
1745 success = true;
1746 step->stepStage( sat::Transaction::STEP_DONE );
1747 }
1748 }
1749 catch (Exception & excpt_r)
1750 {
1751 ZYPP_CAUGHT( excpt_r );
1752 if ( progress.aborted() )
1753 {
1754 WAR << "commit aborted by the user" << endl;
1755 abort = true;
1756 step->stepStage( sat::Transaction::STEP_ERROR );
1757 break;
1758 }
1759 // else
1760 WAR << "removal of " << p << " failed";
1761 step->stepStage( sat::Transaction::STEP_ERROR );
1762 }
1763 if ( success && !policy_r.dryRun() )
1764 {
1766 step->stepStage( sat::Transaction::STEP_DONE );
1767 }
1768 }
1769 }
1770 else if ( ! policy_r.dryRun() ) // other resolvables (non-Package)
1771 {
1772 // Status is changed as the buddy package buddy
1773 // gets installed/deleted. Handle non-buddies only.
1774 if ( ! citem.buddy() )
1775 {
1776 if ( citem->isKind<Product>() )
1777 {
1778 Product::constPtr p = citem->asKind<Product>();
1779 if ( citem.status().isToBeInstalled() )
1780 {
1781 ERR << "Can't install orphan product without release-package! " << citem << endl;
1782 }
1783 else
1784 {
1785 // Deleting the corresponding product entry is all we con do.
1786 // So the product will no longer be visible as installed.
1787 std::string referenceFilename( p->referenceFilename() );
1788 if ( referenceFilename.empty() )
1789 {
1790 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
1791 }
1792 else
1793 {
1794 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
1795 if ( ! rpm().hasFile( referencePath.asString() ) )
1796 {
1797 // If it's not owned by a package, we can delete it.
1798 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
1799 if ( filesystem::unlink( referencePath ) != 0 )
1800 ERR << "Delete orphan product failed: " << referencePath << endl;
1801 }
1802 else
1803 {
1804 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
1805 }
1806 }
1807 }
1808 }
1809 else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() )
1810 {
1811 // SrcPackage is install-only
1812 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
1813 installSrcPackage( p );
1814 }
1815
1817 step->stepStage( sat::Transaction::STEP_DONE );
1818 }
1819
1820 } // other resolvables
1821
1822 } // for
1823
1824 // process all remembered posttrans scripts. If aborting,
1825 // at least log omitted scripts.
1826 if ( abort || (abort = !postTransCollector.executeScripts()) )
1827 postTransCollector.discardScripts();
1828
1829 // Check presence of update scripts/messages. If aborting,
1830 // at least log omitted scripts.
1831 if ( ! successfullyInstalledPackages.empty() )
1832 {
1833 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
1834 successfullyInstalledPackages, abort ) )
1835 {
1836 WAR << "Commit aborted by the user" << endl;
1837 abort = true;
1838 }
1839 // send messages after scripts in case some script generates output,
1840 // that should be kept in t %ghost message file.
1841 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
1842 successfullyInstalledPackages,
1843 result_r );
1844 }
1845
1846 // jsc#SLE-5116: Log patch status changes to history
1847 // NOTE: Should be the last action as it may need to reload
1848 // the Target in case of an incomplete transaction.
1849 logPatchStatusChanges( result_r.transaction(), *this );
1850
1851 if ( abort )
1852 {
1853 HistoryLog().comment( "Commit was aborted." );
1855 }
1856 }
1857
1858
1865 struct SendSingleTransReport : public callback::SendReport<rpm::SingleTransReport>
1866 {
1868 void sendLogline( const std::string & line_r, ReportType::loglevel level_r = ReportType::loglevel::msg )
1869 {
1870 callback::UserData data { ReportType::contentLogline };
1871 data.set( "line", std::cref(line_r) );
1872 data.set( "level", level_r );
1873 report( data );
1874 }
1876 void sendLoglineRpm( const std::string & line_r, unsigned rpmlevel_r )
1877 {
1878 auto u2rpmlevel = []( unsigned rpmlevel_r ) -> ReportType::loglevel {
1879 switch ( rpmlevel_r ) {
1880 case RPMLOG_EMERG: [[fallthrough]]; // system is unusable
1881 case RPMLOG_ALERT: [[fallthrough]]; // action must be taken immediately
1882 case RPMLOG_CRIT: // critical conditions
1883 return ReportType::loglevel::crt;
1884 case RPMLOG_ERR: // error conditions
1885 return ReportType::loglevel::err;
1886 case RPMLOG_WARNING: // warning conditions
1887 return ReportType::loglevel::war;
1888 default: [[fallthrough]];
1889 case RPMLOG_NOTICE: [[fallthrough]]; // normal but significant condition
1890 case RPMLOG_INFO: // informational
1891 return ReportType::loglevel::msg;
1892 case RPMLOG_DEBUG:
1893 return ReportType::loglevel::dbg;
1894 }
1895 };
1896 sendLogline( line_r, u2rpmlevel( rpmlevel_r ) );
1897 }
1898
1899 private:
1900 void report( const callback::UserData & userData_r )
1901 { (*this)->report( userData_r ); }
1902 };
1903
1905
1911
1913 {
1914 namespace zpt = zypp::proto::target;
1915
1916 SendSingleTransReport report; // active throughout the whole rpm transaction
1917
1918 // steps: this is our todo-list
1920 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << steps.size() << endl;
1921
1923
1924 // Send notification once upon calling rpm
1925 NotifyAttemptToModify attemptToModify( result_r );
1926
1927 // let zypper know we executed in one big transaction so in case of failures it can show extended error information
1928 result_r.setSingleTransactionMode( true );
1929
1930 // bsc#1181328: Some systemd tools require /proc to be mounted
1931 AssertProcMounted assertProcMounted( _root );
1932
1933 // Why nodeps?
1934 //
1935 // Because zypp builds the transaction and the resolver asserts that
1936 // everything is fine, or the user decided to ignore problems.
1937 rpm::RpmInstFlags flags( policy_r.rpmInstFlags()
1939 // skip signature checks, we did that already
1942 // ignore untrusted keys since we already checked those earlier
1944
1945 zpt::Commit commit;
1946 commit.set_flags( flags );
1947 commit.set_ignorearch( !ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) );
1948 commit.set_arch( ZConfig::instance().systemArchitecture().asString() );
1949 commit.set_dbpath( rpm().dbPath().asString() );
1950 commit.set_root( rpm().root().asString() );
1951
1952 bool abort = false;
1953 zypp::AutoDispose<std::unordered_map<int, ManagedFile>> locCache([]( std::unordered_map<int, ManagedFile> &data ){
1954 for ( auto &[_, value] : data ) {
1955 (void)_; // unsused; for older g++ versions
1956 value.resetDispose();
1957 }
1958 data.clear();
1959 });
1960
1961 // fill the transaction
1962 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort ; ++stepId ) {
1963 auto &step = steps[stepId];
1964 PoolItem citem( step );
1965 if ( step.stepType() == sat::Transaction::TRANSACTION_IGNORE ) {
1966 if ( citem->isKind<Package>() )
1967 {
1968 // for packages this means being obsoleted (by rpm)
1969 // thius no additional action is needed.
1970 step.stepStage( sat::Transaction::STEP_DONE );
1971 continue;
1972 }
1973 }
1974
1975 if ( citem->isKind<Package>() ) {
1976 Package::constPtr p = citem->asKind<Package>();
1977 if ( citem.status().isToBeInstalled() )
1978 {
1979 try {
1980 locCache.value()[stepId] = packageCache_r.get( citem );
1981
1982 zpt::TransactionStep tStep;
1983 tStep.set_stepid( stepId );
1984 tStep.mutable_install()->set_pathname( locCache.value()[stepId]->asString() );
1985 tStep.mutable_install()->set_multiversion( p->multiversionInstall() );
1986
1987 *commit.mutable_steps()->Add( ) = std::move(tStep);
1988 }
1989 catch ( const AbortRequestException &e )
1990 {
1991 WAR << "commit aborted by the user" << endl;
1992 abort = true;
1993 step.stepStage( sat::Transaction::STEP_ERROR );
1994 break;
1995 }
1996 catch ( const SkipRequestException &e )
1997 {
1998 ZYPP_CAUGHT( e );
1999 WAR << "Skipping package " << p << " in commit" << endl;
2000 step.stepStage( sat::Transaction::STEP_ERROR );
2001 continue;
2002 }
2003 catch ( const Exception &e )
2004 {
2005 // bnc #395704: missing catch causes abort.
2006 // TODO see if packageCache fails to handle errors correctly.
2007 ZYPP_CAUGHT( e );
2008 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2009 step.stepStage( sat::Transaction::STEP_ERROR );
2010 continue;
2011 }
2012 } else {
2013
2014 zpt::TransactionStep tStep;
2015 tStep.set_stepid( stepId );
2016 tStep.mutable_remove()->set_name( p->name() );
2017 tStep.mutable_remove()->set_version( p->edition().version() );
2018 tStep.mutable_remove()->set_release( p->edition().release() );
2019 tStep.mutable_remove()->set_arch( p->arch().asString() );
2020
2021 *commit.mutable_steps()->Add() = std::move(tStep);
2022
2023 }
2024 } else if ( citem->isKind<SrcPackage>() && citem.status().isToBeInstalled() ) {
2025 // SrcPackage is install-only
2026 SrcPackage::constPtr p = citem->asKind<SrcPackage>();
2027
2028 try {
2029 // provide on local disk
2030 locCache.value()[stepId] = provideSrcPackage( p );
2031
2032 zpt::TransactionStep tStep;
2033 tStep.set_stepid( stepId );
2034 tStep.mutable_install()->set_pathname( locCache.value()[stepId]->asString() );
2035 tStep.mutable_install()->set_multiversion( false );
2036 *commit.mutable_steps()->Add() = std::move(tStep);
2037
2038 } catch ( const Exception &e ) {
2039 ZYPP_CAUGHT( e );
2040 INT << "Unexpected Error: Skipping package " << p << " in commit" << endl;
2041 step.stepStage( sat::Transaction::STEP_ERROR );
2042 continue;
2043 }
2044 }
2045 }
2046
2047 std::vector<sat::Solvable> successfullyInstalledPackages;
2048
2049 if ( commit.steps_size() ) {
2050
2051 // create the event loop early
2052 auto loop = zyppng::EventLoop::create();
2053
2054 attemptToModify();
2055
2056
2057 // transaction related variables:
2058 //
2059 // the index of the step in the transaction list that we currenty execute.
2060 // this can be -1
2061 int currentStepId = -1;
2062
2063 // sync flag, every time zypp-rpm finishes executing a step it writes a tag into
2064 // the script fd, once we receive it we set this flag to true and ignore all output
2065 // that is written to the pipe ( aside from buffering it ) until we finalize the current report
2066 // and start a new one
2067 bool gotEndOfScript = false;
2068
2069 // the possible reports we emit during the transaction
2070 std::unique_ptr<callback::SendReport <rpm::TransactionReportSA>> transactionreport;
2071 std::unique_ptr<callback::SendReport <rpm::InstallResolvableReportSA>> installreport;
2072 std::unique_ptr<callback::SendReport <rpm::RemoveResolvableReportSA>> uninstallreport;
2073 std::unique_ptr<callback::SendReport <rpm::CommitScriptReportSA>> scriptreport;
2074 std::unique_ptr<callback::SendReport <rpm::CleanupPackageReportSA>> cleanupreport;
2075
2076 // this will be set if we receive a transaction error description
2077 std::optional<zpt::TransactionError> transactionError;
2078
2079 // infos about the currently executed script, empty if no script is currently executed
2080 std::string currentScriptType;
2081 std::string currentScriptPackage;
2082
2083 // buffer to collect rpm output per report, this will be written to the log once the
2084 // report ends
2085 std::string rpmmsg;
2086
2087 // maximum number of lines that we are buffering in rpmmsg
2088 constexpr auto MAXRPMMESSAGELINES = 10000;
2089
2090 // current number of lines in the rpmmsg line buffer. This is capped to MAXRPMMESSAGELINES
2091 unsigned lineno = 0;
2092
2093 // the sources to communicate with zypp-rpm, we will associate pipes with them further down below
2094 auto msgSource = zyppng::AsyncDataSource::create();
2095 auto scriptSource = zyppng::AsyncDataSource::create();
2096
2097
2098 // helper function that sends RPM output to the currently active report, writing a warning to the log
2099 // if there is none
2100 const auto &sendRpmLineToReport = [&]( const std::string &line ){
2101
2102 const auto &sendLogRep = [&]( auto &report, const auto &cType ){
2103 callback::UserData cmdout(cType);
2104 if ( currentStepId >= 0 )
2105 cmdout.set( "solvable", steps.at(currentStepId).satSolvable() );
2106 cmdout.set( "line", line );
2107 report->report(cmdout);
2108 };
2109
2110 if ( installreport ) {
2111 sendLogRep( (*installreport), rpm::InstallResolvableReportSA::contentRpmout );
2112 } else if ( uninstallreport ) {
2113 sendLogRep( (*uninstallreport), rpm::RemoveResolvableReportSA::contentRpmout );
2114 } else if ( scriptreport ) {
2115 sendLogRep( (*scriptreport), rpm::CommitScriptReportSA::contentRpmout );
2116 } else if ( transactionreport ) {
2117 sendLogRep( (*transactionreport), rpm::TransactionReportSA::contentRpmout );
2118 } else if ( cleanupreport ) {
2119 sendLogRep( (*cleanupreport), rpm::CleanupPackageReportSA::contentRpmout );
2120 } else {
2121 WAR << "Got rpm output without active report " << line; // no endl! - readLine does not trim
2122 }
2123
2124 // remember rpm output
2125 if ( lineno >= MAXRPMMESSAGELINES ) {
2126 if ( line.find( " scriptlet failed, " ) == std::string::npos ) // always log %script errors
2127 return;
2128 }
2129 rpmmsg += line;
2130 if ( line.back() != '\n' )
2131 rpmmsg += '\n';
2132 };
2133
2134
2135 // callback and helper function to process data that is received on the script FD
2136 const auto &processDataFromScriptFd = [&](){
2137
2138 while ( scriptSource->canReadLine() ) {
2139
2140 if ( gotEndOfScript )
2141 return;
2142
2143 std::string l = scriptSource->readLine().asString();
2144 if( str::endsWith( l, endOfScriptTag ) ) {
2145 gotEndOfScript = true;
2146 std::string::size_type rawsize { l.size() - endOfScriptTag.size() };
2147 if ( not rawsize )
2148 return;
2149 l = l.substr( 0, rawsize );
2150 }
2151 L_DBG("zypp-rpm") << "[rpm> " << l; // no endl! - readLine does not trim
2152 sendRpmLineToReport( l );
2153 }
2154 };
2155 scriptSource->sigReadyRead().connect( processDataFromScriptFd );
2156
2157 // helper function that just waits until the end of script tag was received on the scriptSource
2158 const auto &waitForScriptEnd = [&]() {
2159
2160 // nothing to wait for
2161 if ( gotEndOfScript )
2162 return;
2163
2164 // we process all available data
2165 processDataFromScriptFd();
2166
2167 // end of script is always sent by zypp-rpm, we need to wait for it to keep order
2168 while ( scriptSource->readFdOpen() && scriptSource->canRead() && !gotEndOfScript ) {
2169 // readyRead will trigger processDataFromScriptFd so no need to call it again
2170 // we still got nothing, lets wait for more
2171 scriptSource->waitForReadyRead( 100 );
2172 }
2173 };
2174
2175 const auto &aboutToStartNewReport = [&](){
2176
2177 if ( transactionreport || installreport || uninstallreport || scriptreport || cleanupreport ) {
2178 ERR << "There is still a running report, this is a bug" << std::endl;
2179 assert(false);
2180 }
2181
2182 gotEndOfScript = false;
2183 };
2184
2185 const auto &writeRpmMsgToHistory = [&](){
2186 if ( rpmmsg.size() == 0 )
2187 return;
2188
2189 if ( lineno >= MAXRPMMESSAGELINES )
2190 rpmmsg += "[truncated]\n";
2191
2192 std::ostringstream sstr;
2193 sstr << "rpm output:" << endl << rpmmsg << endl;
2194 HistoryLog().comment(sstr.str());
2195 };
2196
2197 // helper function that closes the current report and cleans up the ressources
2198 const auto &finalizeCurrentReport = [&]() {
2199 sat::Transaction::Step *step = nullptr;
2200 Resolvable::constPtr resObj;
2201 if ( currentStepId >= 0 ) {
2202 step = &steps.at(currentStepId);
2203 resObj = makeResObject( step->satSolvable() );
2204 }
2205
2206 if ( installreport ) {
2207 waitForScriptEnd();
2208 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2209
2211 str::form("%s install failed", step->ident().c_str()),
2212 true /*timestamp*/);
2213
2214 writeRpmMsgToHistory();
2215
2216 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::INVALID );
2217 } else {
2218 ( *installreport)->progress( 100, resObj );
2219 ( *installreport)->finish( resObj, rpm::InstallResolvableReportSA::NO_ERROR );
2220
2221 if ( currentStepId >= 0 )
2222 locCache.value().erase( currentStepId );
2223 successfullyInstalledPackages.push_back( step->satSolvable() );
2224
2225 PoolItem citem( *step );
2226 if ( !( flags & rpm::RPMINST_TEST ) ) {
2227 // @TODO are we really doing this just for install?
2228 if ( citem.isNeedreboot() ) {
2229 auto rebootNeededFile = root() / "/run/reboot-needed";
2230 if ( filesystem::assert_file( rebootNeededFile ) == EEXIST)
2231 filesystem::touch( rebootNeededFile );
2232 }
2234 HistoryLog().install(citem);
2235 }
2236
2238 str::form("%s installed ok", step->ident().c_str()),
2239 true /*timestamp*/);
2240
2241 writeRpmMsgToHistory();
2242 }
2243 }
2244 if ( uninstallreport ) {
2245 waitForScriptEnd();
2246 if ( step->stepStage() == sat::Transaction::STEP_ERROR ) {
2247
2249 str::form("%s uninstall failed", step->ident().c_str()),
2250 true /*timestamp*/);
2251
2252 writeRpmMsgToHistory();
2253
2254 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::INVALID );
2255 } else {
2256 ( *uninstallreport)->progress( 100, resObj );
2257 ( *uninstallreport)->finish( resObj, rpm::RemoveResolvableReportSA::NO_ERROR );
2258
2259 PoolItem citem( *step );
2260 HistoryLog().remove(citem);
2261
2263 str::form("%s removed ok", step->ident().c_str()),
2264 true /*timestamp*/);
2265
2266 writeRpmMsgToHistory();
2267 }
2268 }
2269 if ( scriptreport ) {
2270 waitForScriptEnd();
2271 ( *scriptreport)->progress( 100, resObj );
2272 ( *scriptreport)->finish( resObj, rpm::CommitScriptReportSA::NO_ERROR );
2273 }
2274 if ( transactionreport ) {
2275 waitForScriptEnd();
2276 ( *transactionreport)->progress( 100 );
2277 ( *transactionreport)->finish( rpm::TransactionReportSA::NO_ERROR );
2278 }
2279 if ( cleanupreport ) {
2280 waitForScriptEnd();
2281 ( *cleanupreport)->progress( 100 );
2282 ( *cleanupreport)->finish( rpm::CleanupPackageReportSA::NO_ERROR );
2283 }
2284 currentStepId = -1;
2285 lineno = 0;
2286 rpmmsg.clear();
2287 currentScriptType.clear();
2288 currentScriptPackage.clear();
2289 installreport.reset();
2290 uninstallreport.reset();
2291 scriptreport.reset();
2292 transactionreport.reset();
2293 cleanupreport.reset();
2294 };
2295
2296 // This sets up the process and pushes the required transactions steps to it
2297 // careful when changing code here, zypp-rpm relies on the exact order data is transferred:
2298 //
2299 // 1) Size of the commit message , sizeof(zyppng::rpc::HeaderSizeType)
2300 // 2) The Commit Proto message, directly serialized to the FD, without Envelope
2301 // 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
2302
2303 constexpr std::string_view zyppRpmBinary(ZYPP_RPM_BINARY);
2304
2305 const char *argv[] = {
2306 //"gdbserver",
2307 //"localhost:10001",
2308 zyppRpmBinary.data(),
2309 nullptr
2310 };
2311 auto prog = zyppng::Process::create();
2312
2313 // we set up a pipe to communicate with the process, it is too dangerous to use stdout since librpm
2314 // might print to it.
2315 auto messagePipe = zyppng::Pipe::create();
2316 if ( !messagePipe )
2317 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create message pipe" ) );
2318
2319 // open a pipe that we are going to use to receive script output, this is a librpm feature, there is no other
2320 // way than a FD to redirect that output
2321 auto scriptPipe = zyppng::Pipe::create();
2322 if ( !scriptPipe )
2323 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to create scriptfd" ) );
2324
2325 prog->addFd( messagePipe->writeFd );
2326 prog->addFd( scriptPipe->writeFd );
2327
2328 // set up the AsyncDataSource to read script output
2329 if ( !scriptSource->openFds( std::vector<int>{ scriptPipe->readFd } ) )
2330 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open scriptFD to subprocess" ) );
2331
2332 prog->sigStarted().connect( [&](){
2333
2334 // close the ends of the pipes we do not care about
2335 messagePipe->unrefWrite();
2336 scriptPipe->unrefWrite();
2337
2338 // read the stdout and stderr and forward it to our log
2339 prog->connectFunc( &zyppng::IODevice::sigChannelReadyRead, [&]( int channel ){
2340 while( prog->canReadLine( channel ) ) {
2341 L_ERR("zypp-rpm") << ( channel == zyppng::Process::StdOut ? "<stdout> " : "<stderr> " ) << prog->channelReadLine( channel ).asStringView(); // no endl! - readLine does not trim
2342 }
2343 });
2344
2345 {
2346 // write the commit message in blocking mode
2347 const auto outFd = prog->stdinFd();
2348 OnScopeExit unblock([&](){
2349 io::setFDBlocking( outFd, false );
2350 });
2351 io::setFDBlocking( outFd );
2352
2353 // first we push the commit information to the process, starting with the byte size
2354 zyppng::rpc::HeaderSizeType msgSize = commit.ByteSizeLong();
2355 const auto written = zyppng::eintrSafeCall( ::write, outFd, &msgSize, sizeof(zyppng::rpc::HeaderSizeType) );
2356 if ( written != sizeof(zyppng::rpc::HeaderSizeType) ) {
2357 prog->stop( SIGKILL );
2358 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit size to subprocess" ) );
2359 }
2360
2361 zyppng::FileOutputStream fo ( outFd );
2362 if ( !commit.SerializeToZeroCopyStream( &fo ) ) {
2363 prog->stop( SIGKILL );
2364 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to write commit to subprocess" ) );
2365 }
2366 fo.Flush();
2367 }
2368
2369 });
2370
2371 // this is the source for control messages from zypp-rpm , we will get structured data information
2372 // in form of protobuf messages
2373 if ( !msgSource->openFds( std::vector<int>{ messagePipe->readFd } ) )
2374 ZYPP_THROW( target::rpm::RpmSubprocessException( "Failed to open read stream to subprocess" ) );
2375
2376 zyppng::rpc::HeaderSizeType pendingMessageSize = 0;
2377 const auto &processMessages = [&] ( ) {
2378
2379 // lambda function that parses the passed message type and checks if the stepId is a valid offset
2380 // in the steps list.
2381 const auto &parseMsgWithStepId = [&steps]( const auto &m, auto &p ){
2382 if ( !p.ParseFromString( m.value() ) ) {
2383 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2384 return false;
2385 }
2386
2387 auto id = p.stepid();
2388 if ( id < 0 || id >= steps.size() ) {
2389 ERR << "Received invalid stepId: " << id << " in " << m.messagetypename() << " message from zypp-rpm, ignoring." << std::endl;
2390 return false;
2391 }
2392 return true;
2393 };
2394
2395 while ( msgSource->bytesAvailable() ) {
2396
2397 if ( pendingMessageSize == 0 ) {
2398 if ( std::size_t(msgSource->bytesAvailable()) >= sizeof( zyppng::rpc::HeaderSizeType ) ) {
2399 msgSource->read( reinterpret_cast<char *>( &pendingMessageSize ), sizeof( zyppng::rpc::HeaderSizeType ) );
2400 }
2401 }
2402
2403 if ( msgSource->bytesAvailable() < pendingMessageSize ) {
2404 return;
2405 }
2406
2407 auto bytes = msgSource->read( pendingMessageSize );
2408 pendingMessageSize = 0;
2409
2410 zypp::proto::Envelope m;
2411 if (! m.ParseFromArray( bytes.data(), bytes.size() ) ) {
2412 // 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
2413 // continue ( this should normally not happen , but code needs to handle it ).
2414 ERR << "Received misformed message from zypp-rpm, ignoring" << std::endl;
2415 return;
2416 }
2417
2418 // due to librpm behaviour we need to make sense of the order of messages we receive
2419 // because we first get a PackageFinished BEFORE getting a PackageError, same applies to
2420 // Script related messages. What we do is remember the current step we are in and only close
2421 // the step when we get the start of the next one
2422 const auto &mName = m.messagetypename();
2423 if ( mName == "zypp.proto.target.RpmLog" ) {
2424
2425 zpt::RpmLog p;
2426 if ( !p.ParseFromString( m.value() ) ) {
2427 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2428 continue;
2429 }
2430 ( p.level() >= RPMLOG_ERR ? L_ERR("zypp-rpm")
2431 : p.level() >= RPMLOG_WARNING ? L_WAR("zypp-rpm")
2432 : L_DBG("zypp-rpm") ) << "[rpm " << p.level() << "> " << p.line(); // no endl! - readLine does not trim
2433 report.sendLoglineRpm( p.line(), p.level() );
2434
2435 } else if ( mName == "zypp.proto.target.PackageBegin" ) {
2436 finalizeCurrentReport();
2437
2438 zpt::PackageBegin p;
2439 if ( !parseMsgWithStepId( m, p ) )
2440 continue;
2441
2442 aboutToStartNewReport();
2443
2444 auto & step = steps.at( p.stepid() );
2445 currentStepId = p.stepid();
2446 if ( step.stepType() == sat::Transaction::TRANSACTION_ERASE ) {
2447 uninstallreport = std::make_unique< callback::SendReport <rpm::RemoveResolvableReportSA> > ();
2448 ( *uninstallreport )->start( makeResObject( step.satSolvable() ) );
2449 } else {
2450 installreport = std::make_unique< callback::SendReport <rpm::InstallResolvableReportSA> > ();
2451 ( *installreport )->start( makeResObject( step.satSolvable() ) );
2452 }
2453
2454 } else if ( mName == "zypp.proto.target.PackageFinished" ) {
2455 zpt::PackageFinished p;
2456 if ( !parseMsgWithStepId( m, p ) )
2457 continue;
2458
2459 if ( p.stepid() < 0 || p.stepid() > steps.size() )
2460 continue;
2461
2462 // here we only set the step stage to done, we however need to wait for the next start in order to send
2463 // the finished report since there might be a error pending to be reported
2464 steps[ p.stepid() ].stepStage( sat::Transaction::STEP_DONE );
2465
2466 } else if ( mName == "zypp.proto.target.PackageProgress" ) {
2467 zpt::PackageProgress p;
2468 if ( !parseMsgWithStepId( m, p ) )
2469 continue;
2470
2471 if ( uninstallreport )
2472 (*uninstallreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2473 else if ( installreport )
2474 (*installreport)->progress( p.amount(), makeResObject( steps.at( p.stepid() ) ));
2475 else
2476 ERR << "Received a " << mName << " message but there is no corresponding report running." << std::endl;
2477
2478 } else if ( mName == "zypp.proto.target.PackageError" ) {
2479 zpt::PackageError p;
2480 if ( !parseMsgWithStepId( m, p ) )
2481 continue;
2482
2483 if ( p.stepid() >= 0 && p.stepid() < steps.size() )
2484 steps[ p.stepid() ].stepStage( sat::Transaction::STEP_ERROR );
2485
2486 finalizeCurrentReport();
2487
2488 } else if ( mName == "zypp.proto.target.ScriptBegin" ) {
2489 finalizeCurrentReport();
2490
2491 zpt::ScriptBegin p;
2492 if ( !p.ParseFromString( m.value() ) ) {
2493 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2494 continue;
2495 }
2496
2497 aboutToStartNewReport();
2498
2499 Resolvable::constPtr resPtr;
2500 const auto stepId = p.stepid();
2501 if ( stepId >= 0 && stepId < steps.size() ) {
2502 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2503 }
2504
2505 currentStepId = p.stepid();
2506 scriptreport = std::make_unique< callback::SendReport <rpm::CommitScriptReportSA> > ();
2507 currentScriptType = p.scripttype();
2508 currentScriptPackage = p.scriptpackage();
2509 (*scriptreport)->start( p.scripttype(), p.scriptpackage(), resPtr );
2510
2511 } else if ( mName == "zypp.proto.target.ScriptFinished" ) {
2512
2513 // we just read the message, we do not act on it because a ScriptError is reported after ScriptFinished
2514
2515 } else if ( mName == "zypp.proto.target.ScriptError" ) {
2516
2517 zpt::ScriptError p;
2518 if ( !p.ParseFromString( m.value() ) ) {
2519 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2520 continue;
2521 }
2522
2523 Resolvable::constPtr resPtr;
2524 const auto stepId = p.stepid();
2525 if ( stepId >= 0 && stepId < steps.size() ) {
2526 resPtr = makeResObject( steps.at(stepId).satSolvable() );
2527
2528 if ( p.fatal() ) {
2529 steps.at( stepId ).stepStage( sat::Transaction::STEP_ERROR );
2530 }
2531
2532 }
2533
2535 str::form("Failed to execute %s script for %s ", currentScriptType.c_str(), currentScriptPackage.size() ? currentScriptPackage.c_str() : "unknown" ),
2536 true /*timestamp*/);
2537
2538 writeRpmMsgToHistory();
2539
2540 if ( !scriptreport ) {
2541 ERR << "Received a ScriptError message, but there is no running report. " << std::endl;
2542 continue;
2543 }
2544
2545 // before killing the report we need to wait for the script end tag
2546 waitForScriptEnd();
2547 (*scriptreport)->finish( resPtr, p.fatal() ? rpm::CommitScriptReportSA::CRITICAL : rpm::CommitScriptReportSA::WARN );
2548
2549 // manually reset the current report since we already sent the finish(), rest will be reset by the new start
2550 scriptreport.reset();
2551 currentStepId = -1;
2552
2553 } else if ( mName == "zypp.proto.target.CleanupBegin" ) {
2554 finalizeCurrentReport();
2555
2556 zpt::CleanupBegin beg;
2557 if ( !beg.ParseFromString( m.value() ) ) {
2558 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2559 continue;
2560 }
2561
2562 aboutToStartNewReport();
2563 cleanupreport = std::make_unique< callback::SendReport <rpm::CleanupPackageReportSA> > ();
2564 (*cleanupreport)->start( beg.nvra() );
2565 } else if ( mName == "zypp.proto.target.CleanupFinished" ) {
2566
2567 finalizeCurrentReport();
2568
2569 } else if ( mName == "zypp.proto.target.CleanupProgress" ) {
2570 zpt::CleanupProgress prog;
2571 if ( !prog.ParseFromString( m.value() ) ) {
2572 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2573 continue;
2574 }
2575
2576 if ( !cleanupreport ) {
2577 ERR << "Received a CleanupProgress message, but there is no running report. " << std::endl;
2578 continue;
2579 }
2580
2581 (*cleanupreport)->progress( prog.amount() );
2582
2583 } else if ( mName == "zypp.proto.target.TransBegin" ) {
2584 finalizeCurrentReport();
2585
2586 zpt::TransBegin beg;
2587 if ( !beg.ParseFromString( m.value() ) ) {
2588 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2589 continue;
2590 }
2591
2592 aboutToStartNewReport();
2593 transactionreport = std::make_unique< callback::SendReport <rpm::TransactionReportSA> > ();
2594 (*transactionreport)->start( beg.name() );
2595 } else if ( mName == "zypp.proto.target.TransFinished" ) {
2596
2597 finalizeCurrentReport();
2598
2599 } else if ( mName == "zypp.proto.target.TransProgress" ) {
2600 zpt::TransProgress prog;
2601 if ( !prog.ParseFromString( m.value() ) ) {
2602 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2603 continue;
2604 }
2605
2606 if ( !transactionreport ) {
2607 ERR << "Received a TransactionProgress message, but there is no running report. " << std::endl;
2608 continue;
2609 }
2610
2611 (*transactionreport)->progress( prog.amount() );
2612 } else if ( mName == "zypp.proto.target.TransactionError" ) {
2613
2614 zpt::TransactionError error;
2615 if ( !error.ParseFromString( m.value() ) ) {
2616 ERR << "Failed to parse " << m.messagetypename() << " message from zypp-rpm." << std::endl;
2617 continue;
2618 }
2619
2620 // this value is checked later
2621 transactionError = std::move(error);
2622
2623 } else {
2624 ERR << "Received unexpected message from zypp-rpm: "<< m.messagetypename() << ", ignoring" << std::endl;
2625 return;
2626 }
2627
2628 }
2629 };
2630 msgSource->connectFunc( &zyppng::AsyncDataSource::sigReadyRead, processMessages );
2631
2632 // track the childs lifetime
2633 int zyppRpmExitCode = -1;
2634 prog->connectFunc( &zyppng::Process::sigFinished, [&]( int code ){
2635 zyppRpmExitCode = code;
2636 loop->quit();
2637 });
2638
2639 if ( !prog->start( argv ) ) {
2640 HistoryLog().comment( "Commit was aborted, failed to run zypp-rpm" );
2641 ZYPP_THROW( target::rpm::RpmSubprocessException( prog->execError() ) );
2642 }
2643
2644 loop->run();
2645
2646 // make sure to read ALL available messages
2647 processMessages();
2648
2649 // we will not receive a new start message , so we need to manually finalize the last report
2650 finalizeCurrentReport();
2651
2652 // make sure to read all data from the log source
2653 bool readMsgs = false;
2654 while( prog->canReadLine( zyppng::Process::StdErr ) ) {
2655 readMsgs = true;
2656 MIL << "zypp-rpm: " << prog->channelReadLine( zyppng::Process::StdErr ).asStringView();
2657 }
2658 while( prog->canReadLine( zyppng::Process::StdOut ) ) {
2659 readMsgs = true;
2660 MIL << "zypp-rpm: " << prog->channelReadLine( zyppng::Process::StdOut ).asStringView();
2661 }
2662
2663 while ( scriptSource->canReadLine() ) {
2664 readMsgs = true;
2665 MIL << "rpm-script-fd: " << scriptSource->readLine().asStringView();
2666 }
2667 if ( scriptSource->bytesAvailable() > 0 ) {
2668 readMsgs = true;
2669 MIL << "rpm-script-fd: " << scriptSource->readAll().asStringView();
2670 }
2671 if ( readMsgs )
2672 MIL << std::endl;
2673
2674 switch ( zyppRpmExitCode ) {
2675 // we need to look at the summary, handle finishedwitherrors like no error here
2676 case zypprpm::NoError:
2677 case zypprpm::RpmFinishedWithError:
2678 break;
2679 case zypprpm::RpmFinishedWithTransactionError: {
2680 // here zypp-rpm sent us a error description
2681 if ( transactionError ) {
2682
2683 std::ostringstream sstr;
2684 sstr << _("Executing the transaction failed because of the following problems:") << "\n";
2685 for ( const auto & err : transactionError->problems() ) {
2686 sstr << " " << err.message() << "\n";
2687 }
2688 sstr << std::endl;
2690
2691 } else {
2692 ZYPP_THROW( rpm::RpmTransactionFailedException("RPM failed with a unexpected error, check the logs for more information.") );
2693 }
2694 break;
2695 }
2696 case zypprpm::FailedToOpenDb:
2697 ZYPP_THROW( rpm::RpmDbOpenException( rpm().root(), rpm().dbPath() ) );
2698 break;
2699 case zypprpm::WrongHeaderSize:
2700 case zypprpm::WrongMessageFormat:
2701 ZYPP_THROW( rpm::RpmSubprocessException("Failed to communicate with zypp-rpm, this is most likely a bug. Consider to fall back to legacy transaction strategy.") );
2702 break;
2703 case zypprpm::RpmInitFailed:
2704 ZYPP_THROW( rpm::RpmInitException( rpm().root(), rpm().dbPath() ) );
2705 break;
2706 case zypprpm::FailedToReadPackage:
2707 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm was unable to read a package, check the logs for more information.") );
2708 break;
2709 case zypprpm::FailedToAddStepToTransaction:
2710 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to build the transaction, check the logs for more information.") );
2711 break;
2712 case zypprpm::RpmOrderFailed:
2713 ZYPP_THROW( rpm::RpmSubprocessException("zypp-rpm failed to order the transaction, check the logs for more information.") );
2714 break;
2715 }
2716
2717 for ( int stepId = 0; (ZYppCommitResult::TransactionStepList::size_type)stepId < steps.size() && !abort; ++stepId ) {
2718 auto &step = steps[stepId];
2719 PoolItem citem( step );
2720
2721 if ( step.stepStage() == sat::Transaction::STEP_TODO ) {
2722 // other resolvables (non-Package) that are not handled by zypp-rpm
2723 if ( !citem->isKind<Package>() && !policy_r.dryRun() ) {
2724 // Status is changed as the buddy package buddy
2725 // gets installed/deleted. Handle non-buddies only.
2726 if ( ! citem.buddy() && citem->isKind<Product>() ) {
2727 Product::constPtr p = citem->asKind<Product>();
2728
2729 if ( citem.status().isToBeInstalled() ) {
2730 ERR << "Can't install orphan product without release-package! " << citem << endl;
2731 } else {
2732 // Deleting the corresponding product entry is all we con do.
2733 // So the product will no longer be visible as installed.
2734 std::string referenceFilename( p->referenceFilename() );
2735
2736 if ( referenceFilename.empty() ) {
2737 ERR << "Can't remove orphan product without 'referenceFilename'! " << citem << endl;
2738 } else {
2739 Pathname referencePath { Pathname("/etc/products.d") / referenceFilename }; // no root prefix for rpmdb lookup!
2740
2741 if ( ! rpm().hasFile( referencePath.asString() ) ) {
2742 // If it's not owned by a package, we can delete it.
2743 referencePath = Pathname::assertprefix( _root, referencePath ); // now add a root prefix
2744 if ( filesystem::unlink( referencePath ) != 0 )
2745 ERR << "Delete orphan product failed: " << referencePath << endl;
2746 } else {
2747 WAR << "Won't remove orphan product: '/etc/products.d/" << referenceFilename << "' is owned by a package." << endl;
2748 }
2749 }
2750 }
2752 step.stepStage( sat::Transaction::STEP_DONE );
2753 }
2754 }
2755 }
2756 }
2757 }
2758
2759 // Check presence of update scripts/messages. If aborting,
2760 // at least log omitted scripts.
2761 if ( ! successfullyInstalledPackages.empty() )
2762 {
2763 if ( ! RunUpdateScripts( _root, ZConfig::instance().update_scriptsPath(),
2764 successfullyInstalledPackages, abort ) )
2765 {
2766 WAR << "Commit aborted by the user" << endl;
2767 abort = true;
2768 }
2769 // send messages after scripts in case some script generates output,
2770 // that should be kept in t %ghost message file.
2771 RunUpdateMessages( _root, ZConfig::instance().update_messagesPath(),
2772 successfullyInstalledPackages,
2773 result_r );
2774 }
2775
2776 // jsc#SLE-5116: Log patch status changes to history
2777 // NOTE: Should be the last action as it may need to reload
2778 // the Target in case of an incomplete transaction.
2779 logPatchStatusChanges( result_r.transaction(), *this );
2780
2781 if ( abort ) {
2782 HistoryLog().comment( "Commit was aborted." );
2784 }
2785 }
2786
2788
2790 {
2791 return _rpm;
2792 }
2793
2794 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
2795 {
2796 return _rpm.hasFile(path_str, name_str);
2797 }
2798
2800 namespace
2801 {
2802 parser::ProductFileData baseproductdata( const Pathname & root_r )
2803 {
2805 PathInfo baseproduct( Pathname::assertprefix( root_r, "/etc/products.d/baseproduct" ) );
2806
2807 if ( baseproduct.isFile() )
2808 {
2809 try
2810 {
2811 ret = parser::ProductFileReader::scanFile( baseproduct.path() );
2812 }
2813 catch ( const Exception & excpt )
2814 {
2815 ZYPP_CAUGHT( excpt );
2816 }
2817 }
2818 else if ( PathInfo( Pathname::assertprefix( root_r, "/etc/products.d" ) ).isDir() )
2819 {
2820 ERR << "baseproduct symlink is dangling or missing: " << baseproduct << endl;
2821 }
2822 return ret;
2823 }
2824
2825 inline Pathname staticGuessRoot( const Pathname & root_r )
2826 {
2827 if ( root_r.empty() )
2828 {
2829 // empty root: use existing Target or assume "/"
2830 Pathname ret ( ZConfig::instance().systemRoot() );
2831 if ( ret.empty() )
2832 return Pathname("/");
2833 return ret;
2834 }
2835 return root_r;
2836 }
2837
2838 inline std::string firstNonEmptyLineIn( const Pathname & file_r )
2839 {
2840 std::ifstream idfile( file_r.c_str() );
2841 for( iostr::EachLine in( idfile ); in; in.next() )
2842 {
2843 std::string line( str::trim( *in ) );
2844 if ( ! line.empty() )
2845 return line;
2846 }
2847 return std::string();
2848 }
2849 } // namespace
2851
2853 {
2854 ResPool pool(ResPool::instance());
2855 for_( it, pool.byKindBegin<Product>(), pool.byKindEnd<Product>() )
2856 {
2857 Product::constPtr p = (*it)->asKind<Product>();
2858 if ( p->isTargetDistribution() )
2859 return p;
2860 }
2861 return nullptr;
2862 }
2863
2865 {
2866 const Pathname needroot( staticGuessRoot(root_r) );
2867 const Target_constPtr target( getZYpp()->getTarget() );
2868 if ( target && target->root() == needroot )
2869 return target->requestedLocales();
2870 return RequestedLocalesFile( home(needroot) / "RequestedLocales" ).locales();
2871 }
2872
2874 {
2875 MIL << "updateAutoInstalled if changed..." << endl;
2876 SolvIdentFile::Data newdata;
2877 for ( auto id : sat::Pool::instance().autoInstalled() )
2878 newdata.insert( IdString(id) ); // explicit ctor!
2879 _autoInstalledFile.setData( std::move(newdata) );
2880 }
2881
2883 { return baseproductdata( _root ).registerTarget(); }
2884 // static version:
2885 std::string TargetImpl::targetDistribution( const Pathname & root_r )
2886 { return baseproductdata( staticGuessRoot(root_r) ).registerTarget(); }
2887
2889 { return baseproductdata( _root ).registerRelease(); }
2890 // static version:
2892 { return baseproductdata( staticGuessRoot(root_r) ).registerRelease();}
2893
2895 { return baseproductdata( _root ).registerFlavor(); }
2896 // static version:
2898 { return baseproductdata( staticGuessRoot(root_r) ).registerFlavor();}
2899
2901 {
2903 parser::ProductFileData pdata( baseproductdata( _root ) );
2904 ret.shortName = pdata.shortName();
2905 ret.summary = pdata.summary();
2906 return ret;
2907 }
2908 // static version:
2910 {
2912 parser::ProductFileData pdata( baseproductdata( staticGuessRoot(root_r) ) );
2913 ret.shortName = pdata.shortName();
2914 ret.summary = pdata.summary();
2915 return ret;
2916 }
2917
2919 {
2920 if ( _distributionVersion.empty() )
2921 {
2923 if ( !_distributionVersion.empty() )
2924 MIL << "Remember distributionVersion = '" << _distributionVersion << "'" << endl;
2925 }
2926 return _distributionVersion;
2927 }
2928 // static version
2929 std::string TargetImpl::distributionVersion( const Pathname & root_r )
2930 {
2931 std::string distributionVersion = baseproductdata( staticGuessRoot(root_r) ).edition().version();
2932 if ( distributionVersion.empty() )
2933 {
2934 // ...But the baseproduct method is not expected to work on RedHat derivatives.
2935 // On RHEL, Fedora and others the "product version" is determined by the first package
2936 // providing 'system-release'. This value is not hardcoded in YUM and can be configured
2937 // with the $distroverpkg variable.
2938 scoped_ptr<rpm::RpmDb> tmprpmdb;
2939 if ( ZConfig::instance().systemRoot() == Pathname() )
2940 {
2941 try
2942 {
2943 tmprpmdb.reset( new rpm::RpmDb );
2944 tmprpmdb->initDatabase( /*default ctor uses / but no additional keyring exports */ );
2945 }
2946 catch( ... )
2947 {
2948 return "";
2949 }
2950 }
2953 distributionVersion = it->tag_version();
2954 }
2955 return distributionVersion;
2956 }
2957
2958
2960 {
2961 return firstNonEmptyLineIn( home() / "LastDistributionFlavor" );
2962 }
2963 // static version:
2964 std::string TargetImpl::distributionFlavor( const Pathname & root_r )
2965 {
2966 return firstNonEmptyLineIn( staticGuessRoot(root_r) / "/var/lib/zypp/LastDistributionFlavor" );
2967 }
2968
2970 namespace
2971 {
2972 std::string guessAnonymousUniqueId( const Pathname & root_r )
2973 {
2974 // bsc#1024741: Omit creating a new uid for chrooted systems (if it already has one, fine)
2975 std::string ret( firstNonEmptyLineIn( root_r / "/var/lib/zypp/AnonymousUniqueId" ) );
2976 if ( ret.empty() && root_r != "/" )
2977 {
2978 // if it has nonoe, use the outer systems one
2979 ret = firstNonEmptyLineIn( "/var/lib/zypp/AnonymousUniqueId" );
2980 }
2981 return ret;
2982 }
2983 }
2984
2986 {
2987 return guessAnonymousUniqueId( root() );
2988 }
2989 // static version:
2990 std::string TargetImpl::anonymousUniqueId( const Pathname & root_r )
2991 {
2992 return guessAnonymousUniqueId( staticGuessRoot(root_r) );
2993 }
2994
2996
2998 {
2999 MIL << "New VendorAttr: " << vendorAttr_r << endl;
3000 _vendorAttr = std::move(vendorAttr_r);
3001 }
3003
3004 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3005 {
3006 // provide on local disk
3007 ManagedFile localfile = provideSrcPackage(srcPackage_r);
3008 // create a installation progress report proxy
3009 RpmInstallPackageReceiver progress( srcPackage_r );
3010 progress.connect(); // disconnected on destruction.
3011 // install it
3012 rpm().installPackage ( localfile );
3013 }
3014
3015 ManagedFile TargetImpl::provideSrcPackage( const SrcPackage_constPtr & srcPackage_r )
3016 {
3017 // provide on local disk
3018 repo::RepoMediaAccess access_r;
3019 repo::SrcPackageProvider prov( access_r );
3020 return prov.provideSrcPackage( srcPackage_r );
3021 }
3023 } // namespace target
3026} // namespace zypp
#define idstr(V)
const Pathname & _root
Definition: RepoManager.cc:145
#define MAXRPMMESSAGELINES
Definition: RpmDb.cc:63
Pathname _mountpoint
Definition: TargetImpl.cc:277
#define SUBST_IF(PAT, VAL)
ZYppCommitResult & _result
Definition: TargetImpl.cc:1564
TrueBool _guard
Definition: TargetImpl.cc:1563
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:180
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:226
ResStatus & status() const
Returns the current status.
Definition: PoolItem.cc:211
sat::Solvable buddy() const
Return the buddy we share our status object with.
Definition: PoolItem.cc:214
Product interface.
Definition: Product.h:33
TraitsType::constPtrType constPtr
Definition: Product.h:38
Track changing files or directories.
Definition: RepoStatus.h:41
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
Definition: RepoStatus.cc:194
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
Definition: RepoStatus.cc:212
bool solvablesEmpty() const
Whether Repository contains solvables.
Definition: Repository.cc:219
SolvableIterator solvablesEnd() const
Iterator behind the last Solvable.
Definition: Repository.cc:241
SolvableIterator solvablesBegin() const
Iterator to the first Solvable.
Definition: Repository.cc:231
size_type solvablesSize() const
Number of solvables in Repository.
Definition: Repository.cc:225
void addSolv(const Pathname &file_r)
Load Solvables from a solv-file.
Definition: Repository.cc:320
void eraseFromPool()
Remove this Repository from its Pool.
Definition: Repository.cc:297
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from the established one.
Definition: PoolImpl.cc:26
Global ResObject pool.
Definition: ResPool.h:61
static ResPool instance()
Singleton ctor.
Definition: ResPool.cc:37
EstablishedStates::ChangedPseudoInstalled ChangedPseudoInstalled
Map holding pseudo installed items where current and established status differ.
Definition: ResPool.h:341
void setHardLockQueries(const HardLockQueries &newLocks_r)
Set a new set of queries.
Definition: ResPool.cc:103
Resolver & resolver() const
The Resolver.
Definition: ResPool.cc:61
const LocaleSet & getRequestedLocales() const
Return the requested locales.
Definition: ResPool.cc:130
ChangedPseudoInstalled changedPseudoInstalled() const
Return all pseudo installed items whose current state differs from their initial one.
Definition: ResPool.h:349
byKind_iterator byKindEnd(const ResKind &kind_r) const
Definition: ResPool.h:268
EstablishedStates establishedStates() const
Factory for EstablishedStates.
Definition: ResPool.cc:76
byKind_iterator byKindBegin(const ResKind &kind_r) const
Definition: ResPool.h:261
void getHardLockQueries(HardLockQueries &activeLocks_r)
Suggest a new set of queries based on the current selection.
Definition: ResPool.cc:106
bool isToBeInstalled() const
Definition: ResStatus.h:253
bool resetTransact(TransactByValue causer_r)
Not the same as setTransact( false ).
Definition: ResStatus.h:484
TraitsType::constPtrType constPtr
Definition: Resolvable.h:59
sat::Transaction getTransaction()
Return the Transaction computed by the last solver run.
Definition: Resolver.cc:77
bool upgradeMode() const
Definition: Resolver.cc:100
bool upgradingRepos() const
Whether there is at least one UpgradeRepo request pending.
Definition: Resolver.cc:139
Attempts to create a lock to prevent the system from going into hibernate/shutdown.
SrcPackage interface.
Definition: SrcPackage.h:30
TraitsType::constPtrType constPtr
Definition: SrcPackage.h:36
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition: StrMatcher.h:298
Definition of vendor equivalence.
Definition: VendorAttr.h:61
Interim helper class to collect global options and settings.
Definition: ZConfig.h:64
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:823
std::string distroverpkg() const
Package telling the "product version" on systems not using /etc/product.d/baseproduct.
Definition: ZConfig.cc:1205
Options and policies for ZYpp::commit.
ZYppCommitPolicy & rpmInstFlags(target::rpm::RpmInstFlags newFlags_r)
The default target::rpm::RpmInstFlags.
bool singleTransModeEnabled() const
ZYppCommitPolicy & rpmExcludeDocs(bool yesNo_r)
Use rpm option –excludedocs (default: false)
ZYppCommitPolicy & dryRun(bool yesNo_r)
Set dry run (default: false).
ZYppCommitPolicy & restrictToMedia(unsigned mediaNr_r)
Restrict commit to media 1.
ZYppCommitPolicy & downloadMode(DownloadMode val_r)
Commit download policy to use.
ZYppCommitPolicy & allMedia()
Process all media (default)
ZYppCommitPolicy & rpmNoSignature(bool yesNo_r)
Use rpm option –nosignature (default: false)
Result returned from ZYpp::commit.
TransactionStepList & rTransactionStepList()
Manipulate transactionStepList.
void setSingleTransactionMode(bool yesno_r)
std::vector< sat::Transaction::Step > TransactionStepList
const sat::Transaction & transaction() const
The full transaction list.
sat::Transaction & rTransaction()
Manipulate transaction.
Typesafe passing of user data via callbacks.
Definition: UserData.h:39
bool set(const std::string &key_r, AnyType val_r)
Set the value for key (nonconst version always returns true).
Definition: UserData.h:118
zypp::ContentType ContentType
Definition: UserData.h:50
std::string receiveLine()
Read one line from the input stream.
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:221
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
const char * c_str() const
String representation.
Definition: Pathname.h:110
const std::string & asString() const
String representation.
Definition: Pathname.h:91
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r prefixed with root_r, unless it is already prefixed.
Definition: Pathname.cc:271
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:128
static TmpFile makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition: TmpPath.cc:218
Pathname path() const
Definition: TmpPath.cc:146
Data returned by ProductFileReader.
bool empty() const
Whether this is an empty object without valid data.
static ProductFileData scanFile(const Pathname &file_r)
Parse one file (or symlink) and return the ProductFileData parsed.
Provides files from different repos.
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r) const
Provide SrcPackage in a local file.
Global sat-pool.
Definition: Pool.h:47
void setAutoInstalled(const Queue &autoInstalled_r)
Set ident list of all autoinstalled solvables.
Definition: Pool.cc:265
Pathname rootDir() const
Get rootdir (for file conflicts check)
Definition: Pool.cc:64
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
static const std::string & systemRepoAlias()
Reserved system repository alias @System .
Definition: Pool.cc:46
void setNeedrebootSpec(sat::SolvableSpec needrebootSpec_r)
Solvables which should trigger the reboot-needed hint if installed/updated.
Definition: Pool.cc:267
Repository systemRepo()
Return the system repository, create it if missing.
Definition: Pool.cc:178
void initRequestedLocales(const LocaleSet &locales_r)
Start tracking changes based on this locales_r.
Definition: Pool.cc:251
Libsolv Id queue wrapper.
Definition: Queue.h:35
detail::IdType value_type
Definition: Queue.h:38
void push(value_type val_r)
Push a value to the end off the Queue.
Definition: Queue.cc:103
Define a set of Solvables by ident and provides.
Definition: SolvableSpec.h:45
void addProvides(Capability provides_r)
A all sat::Solvable matching this provides_r.
void parseFrom(const InputStream &istr_r)
Parse file istr_r and add its specs (one per line, #-comments).
A Solvable object within the sat Pool.
Definition: Solvable.h:54
A single step within a Transaction.
Definition: Transaction.h:219
StepType stepType() const
Type of action to perform in this step.
Definition: Transaction.cc:388
StepStage stepStage() const
Step action result.
Definition: Transaction.cc:391
Solvable satSolvable() const
Return the corresponding Solvable.
Definition: Transaction.h:243
Libsolv transaction wrapper.
Definition: Transaction.h:52
const_iterator end() const
Iterator behind the last TransactionStep.
Definition: Transaction.cc:343
StringQueue autoInstalled() const
Return the ident strings of all packages that would be auto-installed after the transaction is run.
Definition: Transaction.cc:358
const_iterator begin() const
Iterator to the first TransactionStep.
Definition: Transaction.cc:337
bool order()
Order transaction steps for commit.
Definition: Transaction.cc:328
@ TRANSACTION_MULTIINSTALL
[M] Install(multiversion) item (
Definition: Transaction.h:67
@ TRANSACTION_INSTALL
[+] Install(update) item
Definition: Transaction.h:66
@ TRANSACTION_IGNORE
[ ] Nothing (includes implicit deletes due to obsoletes and non-package actions)
Definition: Transaction.h:64
@ TRANSACTION_ERASE
[-] Delete item
Definition: Transaction.h:65
@ STEP_DONE
[OK] success
Definition: Transaction.h:74
@ STEP_TODO
[__] unprocessed
Definition: Transaction.h:73
@ STEP_ERROR
[**] error
Definition: Transaction.h:75
Target::commit helper optimizing package provision.
void setCommitList(std::vector< sat::Solvable > commitList_r)
Download(commit) sequence of solvables to compute read ahead.
bool preloaded() const
Whether preloaded hint is set.
ManagedFile get(const PoolItem &citem_r)
Provide a package.
void setData(const Data &data_r)
Store new Data.
Definition: HardLocksFile.h:73
const Data & data() const
Return the data.
Definition: HardLocksFile.h:57
pool::PoolTraits::HardLockQueries Data
Definition: HardLocksFile.h:41
Save and restore locale set from file.
const LocaleSet & locales() const
Return the loacale set.
void setLocales(const LocaleSet &locales_r)
Store a new locale set.
void tryLevel(target::rpm::InstallResolvableReport::RpmLevel level_r)
Extract and remember posttrans scripts for later execution.
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:2888
const VendorAttr & vendorAttr() const
The targets current vendor equivalence settings.
Definition: TargetImpl.h:199
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: TargetImpl.cc:2882
LocaleSet requestedLocales() const
Languages to be supported by the system.
Definition: TargetImpl.h:155
void updateAutoInstalled()
Update the database of autoinstalled packages.
Definition: TargetImpl.cc:2873
ManagedFile provideSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Provides a source package on the Target.
Definition: TargetImpl.cc:3015
Pathname _root
Path to the target.
Definition: TargetImpl.h:222
RequestedLocalesFile _requestedLocalesFile
Requested Locales database.
Definition: TargetImpl.h:226
void createLastDistributionFlavorCache() const
generates a cache of the last product flavor
Definition: TargetImpl.cc:909
std::string _distributionVersion
Cache distributionVersion.
Definition: TargetImpl.h:232
std::list< PoolItem > PoolItemList
list of pool items
Definition: TargetImpl.h:59
rpm::RpmDb _rpm
RPM database.
Definition: TargetImpl.h:224
rpm::RpmDb & rpm()
The RPM database.
Definition: TargetImpl.cc:2789
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:2918
void createAnonymousId() const
generates the unique anonymous id which is called when creating the target
Definition: TargetImpl.cc:887
SolvIdentFile _autoInstalledFile
user/auto installed database
Definition: TargetImpl.h:228
Product::constPtr baseProduct() const
returns the target base installed product, also known as the distribution or platform.
Definition: TargetImpl.cc:2852
Target::DistributionLabel distributionLabel() const
This is shortName and summary attribute of the installed base product.
Definition: TargetImpl.cc:2900
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:2794
HardLocksFile _hardLocksFile
Hard-Locks database.
Definition: TargetImpl.h:230
Pathname root() const
The root set for this target.
Definition: TargetImpl.h:116
void load(bool force=true)
Definition: TargetImpl.cc:1126
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
Definition: TargetImpl.cc:2959
void commitInSingleTransaction(const ZYppCommitPolicy &policy_r, CommitPackageCache &packageCache_r, ZYppCommitResult &result_r)
Commit ordered changes (internal helper)
Definition: TargetImpl.cc:1912
void installSrcPackage(const SrcPackage_constPtr &srcPackage_r)
Install a source package on the Target.
Definition: TargetImpl.cc:3004
ZYppCommitResult commit(ResPool pool_r, const ZYppCommitPolicy &policy_r)
Commit changes in the pool.
Definition: TargetImpl.cc:1262
VendorAttr _vendorAttr
vendor equivalence settings.
Definition: TargetImpl.h:234
Pathname home() const
The directory to store things.
Definition: TargetImpl.h:120
void commitFindFileConflicts(const ZYppCommitPolicy &policy_r, ZYppCommitResult &result_r)
Commit helper checking for file conflicts after download.
Pathname defaultSolvfilesPath() const
The systems default solv file location.
Definition: TargetImpl.cc:958
std::string anonymousUniqueId() const
anonymous unique id
Definition: TargetImpl.cc:2985
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:2894
Interface to the rpm program.
Definition: RpmDb.h:48
void installPackage(const Pathname &filename, RpmInstFlags flags=RPMINST_NONE)
install rpm package
Definition: RpmDb.cc:1655
void initDatabase(Pathname root_r=Pathname(), bool doRebuild_r=false)
Prepare access to the rpm database below root_r.
Definition: RpmDb.cc:266
void removePackage(const std::string &name_r, RpmInstFlags flags=RPMINST_NONE)
remove rpm package
Definition: RpmDb.cc:1852
void closeDatabase()
Block further access to the rpm database and go back to uninitialized state.
Definition: RpmDb.cc:356
bool hasFile(const std::string &file_r, const std::string &name_r="") const
Return true if at least one package owns a certain file (name_r empty) Return true if package name_r ...
Definition: RpmDb.cc:967
Subclass to retrieve database content.
Definition: librpmDb.h:336
bool findByProvides(const std::string &tag_r)
Reset to iterate all packages that provide a certain tag.
Definition: librpmDb.cc:743
bool write(const Pathname &path_r, const std::string &key_r, const std::string &val_r, const std::string &newcomment_r)
Add or change a value in sysconfig file path_r.
Definition: sysconfig.cc:80
int chmod(const Pathname &path, mode_t mode)
Like 'chmod'.
Definition: PathInfo.cc:1092
const StrMatcher & matchNoDots()
Convenience returning StrMatcher( "[^.]*", Match::GLOB )
Definition: PathInfo.cc:26
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:605
int unlink(const Pathname &path)
Like 'unlink'.
Definition: PathInfo.cc:700
int rename(const Pathname &oldpath, const Pathname &newpath)
Like 'rename'.
Definition: PathInfo.cc:742
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
Definition: PathInfo.cc:1183
int recursive_rmdir(const Pathname &path)
Like 'rm -r DIR'.
Definition: PathInfo.cc:412
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
int addmod(const Pathname &path, mode_t mode)
Add the mode bits to the file given by path.
Definition: PathInfo.cc:1101
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like 'readlink'.
Definition: PathInfo.cc:924
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition: PathInfo.cc:319
int touch(const Pathname &path)
Change file's modification and access times.
Definition: PathInfo.cc:1234
std::string md5sum(const Pathname &file)
Compute a files md5sum.
Definition: PathInfo.cc:1024
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like 'symlink'.
Definition: PathInfo.cc:855
BlockingMode setFDBlocking(int fd, bool mode)
Definition: IOTools.cc:31
std::string getline(std::istream &str)
Read one line from stream.
Definition: IOStream.cc:33
std::string toJSON(void)
Definition: Json.h:136
SolvableIdType size_type
Definition: PoolMember.h:126
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition: Pool.cc:286
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1027
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1085
bool endsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasSuffix
Definition: String.h:1092
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition: String.cc:177
unsigned splitEscaped(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", bool withEmpty=false)
Split line_r into words with respect to escape delimeters.
Definition: String.h:595
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
@ RPMINST_ALLOWUNTRUSTED
Definition: RpmFlags.h:53
IMPL_PTR_TYPE(TargetImpl)
void XRunUpdateMessages(const Pathname &root_r, const Pathname &messagesPath_r, const std::vector< sat::Solvable > &checkPackages_r, ZYppCommitResult &result_r)
Definition: TargetImpl.cc:802
std::string rpmDbStateHash(const Pathname &root_r)
Definition: TargetImpl.cc:95
void writeUpgradeTestcase()
Definition: TargetImpl.cc:356
static bool fileMissing(const Pathname &pathname)
helper functor
Definition: TargetImpl.cc:882
void updateFileContent(const Pathname &filename, boost::function< bool()> condition, boost::function< std::string()> value)
updates the content of filename if condition is true, setting the content the the value returned by v...
Definition: TargetImpl.cc:847
RepoStatus rpmDbRepoStatus(const Pathname &root_r)
Definition: TargetImpl.cc:113
static std::string generateRandomId()
generates a random id using uuidgen
Definition: TargetImpl.cc:836
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
std::unordered_set< Locale > LocaleSet
Definition: Locale.h:27
ResObject::Ptr makeResObject(const sat::Solvable &solvable_r)
Create ResObject from sat::Solvable.
Definition: ResObject.cc:43
std::list< UpdateNotificationFile > UpdateNotifications
std::string asString(const Patch::Category &obj)
Definition: Patch.cc:122
@ DownloadInHeaps
Similar to DownloadInAdvance, but try to split the transaction into heaps, where at the end of each h...
Definition: DownloadMode.h:29
@ DownloadOnly
Just download all packages to the local cache.
Definition: DownloadMode.h:25
@ DownloadAsNeeded
Alternating download and install.
Definition: DownloadMode.h:32
@ DownloadInAdvance
First download all packages to the local cache.
Definition: DownloadMode.h:27
@ DownloadDefault
libzypp will decide what to do.
Definition: DownloadMode.h:24
JSON array.
Definition: Json.h:257
std::string asJSON() const
JSON representation.
Definition: Json.h:279
void add(const Value &val_r)
Push JSON Value to Array.
Definition: Json.h:271
JSON object.
Definition: Json.h:322
void add(const String &key_r, const Value &val_r)
Add key/value pair.
Definition: Json.h:336
std::string asJSON() const
JSON representation.
Definition: Json.h:344
bool isKind(const ResKind &kind_r) const
Definition: SolvableType.h:64
Solvable satSolvable() const
Return the corresponding sat::Solvable.
Definition: SolvableType.h:57
bool isNeedreboot() const
Definition: SolvableType.h:83
static PoolImpl & myPool()
Definition: PoolImpl.cc:184
Convenience SendReport<rpm::SingleTransReport> wrapper.
Definition: TargetImpl.cc:1866
void report(const callback::UserData &userData_r)
Definition: TargetImpl.cc:1900
void sendLoglineRpm(const std::string &line_r, unsigned rpmlevel_r)
Convenience to send a contentLogline translating a rpm loglevel.
Definition: TargetImpl.cc:1876
void sendLogline(const std::string &line_r, ReportType::loglevel level_r=ReportType::loglevel::msg)
Convenience to send a contentLogline.
Definition: TargetImpl.cc:1868
static const UserData::ContentType contentRpmout
"zypp-rpm/cleanupkgsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/scriptsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/installpkgsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentRpmout
"zypp-rpm/removepkgsa": Additional rpm output (sent immediately).
static const UserData::ContentType contentLogline
"zypp-rpm/logline" report a line suitable to be written to the screen.
static const UserData::ContentType contentRpmout
"zypp-rpm/transactionsa": Additional rpm output (sent immediately).
#define NON_COPYABLE(CLASS)
Delete copy ctor and copy assign.
Definition: Easy.h:59
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
#define NON_MOVABLE(CLASS)
Delete move ctor and move assign.
Definition: Easy.h:69
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:436
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
#define _(MSG)
Definition: Gettext.h:37
#define L_ERR(GROUP)
Definition: Logger.h:107
#define DBG
Definition: Logger.h:95
#define MIL
Definition: Logger.h:96
#define ERR
Definition: Logger.h:98
#define L_WAR(GROUP)
Definition: Logger.h:106
#define WAR
Definition: Logger.h:97
#define L_DBG(GROUP)
Definition: Logger.h:104
#define INT
Definition: Logger.h:100