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