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