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