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