1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/TargetImpl.cc
19 #include <sys/types.h>
22 #include "zypp/base/Logger.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"
27 #include "zypp/base/UserRequestException.h"
29 #include "zypp/ZConfig.h"
31 #include "zypp/PoolItem.h"
32 #include "zypp/ResObjects.h"
34 #include "zypp/TmpPath.h"
35 #include "zypp/RepoStatus.h"
36 #include "zypp/ExternalProgram.h"
37 #include "zypp/Repository.h"
39 #include "zypp/ResFilters.h"
40 #include "zypp/target/CommitLog.h"
41 #include "zypp/target/TargetImpl.h"
42 #include "zypp/target/TargetCallbackReceiver.h"
43 #include "zypp/target/rpm/librpmDb.h"
44 #include "zypp/target/CommitPackageCache.h"
46 #include "zypp/pool/GetResolvablesToInsDel.h"
47 #include "zypp/solver/detail/Helper.h"
49 #include "zypp/repo/DeltaCandidates.h"
50 #include "zypp/repo/PackageProvider.h"
51 #include "zypp/repo/ScriptProvider.h"
52 #include "zypp/repo/SrcPackageProvider.h"
54 #include "zypp/sat/Pool.h"
58 using namespace zypp::resfilter;
59 using zypp::solver::detail::Helper;
61 ///////////////////////////////////////////////////////////////////
63 { /////////////////////////////////////////////////////////////////
64 ///////////////////////////////////////////////////////////////////
66 { /////////////////////////////////////////////////////////////////
68 ///////////////////////////////////////////////////////////////////
70 { /////////////////////////////////////////////////////////////////
72 // Execute file (passed as pi_t) as script
73 // report against report_r
75 void ExecuteScript( const Pathname & pn_r,
76 callback::SendReport<ScriptResolvableReport> * report)
81 std::ostringstream err;
82 err << "Script is not a file: " << pi.fileType() << " " << pn_r;
84 (*report)->problem( err.str() );
85 ZYPP_THROW(Exception(err.str()));
88 filesystem::chmod( pn_r, S_IRUSR|S_IWUSR|S_IXUSR ); // "rwx------"
89 ExternalProgram prog( pn_r.asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true );
91 for ( std::string output = prog.receiveLine(); output.length(); output = prog.receiveLine() )
93 // hmm, this depends on a ScriptResolvableReport :-(
95 && ! (*report)->progress( ScriptResolvableReport::OUTPUT, output ) )
97 WAR << "User request to abort script." << endl;
98 prog.kill(); // the rest is handled by exit code evaluation.
102 int exitCode = prog.close();
105 std::ostringstream err;
106 err << "Script failed with exit code " << exitCode;
108 (*report)->problem( err.str() );
109 ZYPP_THROW(Exception(err.str()));
114 // Check for (and run) update script
115 // path: directory where to look
116 // name,version,release: Script name must match 'name-version.release-' prefix
118 #warning needs to be reimplemented exception safe
119 #warning needs to take root prefix into account and execute chroot
120 void RunUpdateScript(Pathname path, std::string name, std::string version, std::string release)
122 // open the scripts directory
124 DIR *dir = opendir(path.asString().c_str());
127 WAR << "Cannot access directory " << path << endl;
131 // compute the name-version.release- prefix
132 std::string prefix = name + "-" + version + "-" + release + "-";
133 size_t pfx_size = prefix.length();
136 ERR << "Prefix size (" << pfx_size << ") larger than supported (255)" << endl;
140 // scan directory for match
141 const char *found = NULL;
142 struct dirent *dentry;
143 while ((dentry = readdir(dir)))
145 if (strncmp( dentry->d_name, prefix.c_str(), pfx_size) == 0) {
146 found = dentry->d_name;
152 ExecuteScript( Pathname(path / found), NULL );
158 // Fetch and execute remote script
159 // access_r: remote access handle
160 // script_r: script (resolvable) handle
161 // do_r: true for 'do', false for 'undo'
163 void ExecuteScriptHelper( repo::RepoMediaAccess & access_r,
164 Script::constPtr script_r,
167 MIL << "Execute script " << script_r << endl;
170 INT << "NULL Script passed." << endl;
174 repo::ScriptProvider prov( access_r );
175 ManagedFile localfile = prov.provideScript( script_r, do_r );
177 if ( localfile->empty() )
179 DBG << "No " << (do_r?"do":"undo") << " script for " << script_r << endl;
184 callback::SendReport<ScriptResolvableReport> report;
185 report->start( script_r, localfile,
186 (do_r ? ScriptResolvableReport::DO
187 : ScriptResolvableReport::UNDO ) );
189 ExecuteScript( localfile, &report );
195 inline void ExecuteDoScript( repo::RepoMediaAccess & access_r, const Script::constPtr & script_r )
197 ExecuteScriptHelper( access_r, script_r, true );
200 inline void ExecuteUndoScript( repo::RepoMediaAccess & access_r, const Script::constPtr & script_r )
202 ExecuteScriptHelper( access_r, script_r, false );
204 /////////////////////////////////////////////////////////////////
206 ///////////////////////////////////////////////////////////////////
208 /** Helper for PackageProvider queries during commit. */
209 struct QueryInstalledEditionHelper
211 bool operator()( const std::string & name_r,
212 const Edition & ed_r,
213 const Arch & arch_r ) const
215 rpm::librpmDb::db_const_iterator it;
216 for ( it.findByName( name_r ); *it; ++it )
218 if ( arch_r == it->tag_arch()
219 && ( ed_r == Edition::noedition || ed_r == it->tag_edition() ) )
229 * \short Let the Source provide the package.
230 * \p pool_r \ref ResPool used to get candidates
231 * \p pi item to be commited
233 struct RepoProvidePackage
236 repo::RepoMediaAccess &_access;
238 RepoProvidePackage( repo::RepoMediaAccess &access, ResPool pool_r )
239 : _pool(pool_r), _access(access)
244 ManagedFile operator()( const PoolItem & pi )
246 // Redirect PackageProvider queries for installed editions
247 // (in case of patch/delta rpm processing) to rpmDb.
248 repo::PackageProviderPolicy packageProviderPolicy;
249 packageProviderPolicy.queryInstalledCB( QueryInstalledEditionHelper() );
251 Package::constPtr p = asKind<Package>(pi.resolvable());
254 // Build a repository list for repos
255 // contributing to the pool
256 std::list<Repository> repos( _pool.knownRepositoriesBegin(), _pool.knownRepositoriesEnd() );
257 repo::DeltaCandidates deltas(repos, p->name());
258 repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
259 return pkgProvider.providePackage();
262 ///////////////////////////////////////////////////////////////////
264 IMPL_PTR_TYPE(TargetImpl);
266 TargetImpl_Ptr TargetImpl::_nullimpl;
268 /** Null implementation */
269 TargetImpl_Ptr TargetImpl::nullimpl()
272 _nullimpl = new TargetImpl;
276 ///////////////////////////////////////////////////////////////////
278 // METHOD NAME : TargetImpl::TargetImpl
279 // METHOD TYPE : Ctor
281 TargetImpl::TargetImpl( const Pathname & root_r, bool doRebuild_r )
283 , _requestedLocalesFile( home() / "RequestedLocales" )
284 , _softLocksFile( home() / "SoftLocks" )
285 , _hardLocksFile( Pathname::assertprefix( _root, ZConfig::instance().locksFile() ) )
287 _rpm.initDatabase( root_r, Pathname(), doRebuild_r );
288 MIL << "Initialized target on " << _root << endl;
291 ///////////////////////////////////////////////////////////////////
293 // METHOD NAME : TargetImpl::~TargetImpl
294 // METHOD TYPE : Dtor
296 TargetImpl::~TargetImpl()
298 _rpm.closeDatabase();
299 MIL << "Targets closed" << endl;
302 void TargetImpl::clearCache()
304 Pathname base = Pathname::assertprefix( _root,
305 ZConfig::instance().repoSolvfilesPath() / sat::Pool::instance().systemRepoName() );
306 filesystem::recursive_rmdir( base );
309 void TargetImpl::buildCache()
311 Pathname base = Pathname::assertprefix( _root,
312 ZConfig::instance().repoSolvfilesPath() / sat::Pool::instance().systemRepoName() );
313 Pathname rpmsolv = base/"solv";
314 Pathname rpmsolvcookie = base/"cookie";
316 bool build_rpm_solv = true;
317 // lets see if the rpm solv cache exists
319 RepoStatus rpmstatus(_root + "/var/lib/rpm/Name");
320 bool solvexisted = PathInfo(rpmsolv).isExist();
323 // see the status of the cache
324 PathInfo cookie( rpmsolvcookie );
325 MIL << "Read cookie: " << cookie << endl;
326 if ( cookie.isExist() )
328 RepoStatus status = RepoStatus::fromCookieFile(rpmsolvcookie);
329 // now compare it with the rpm database
330 if ( status.checksum() == rpmstatus.checksum() )
331 build_rpm_solv = false;
332 MIL << "Read cookie: " << rpmsolvcookie << " says: "
333 << (build_rpm_solv ? "outdated" : "uptodate") << endl;
337 if ( build_rpm_solv )
339 // Take care we unlink the solvfile on exception
340 ManagedFile guard( base, filesystem::recursive_rmdir );
342 // if it does not exist yet, we better create it
343 filesystem::assert_dir( base );
345 filesystem::TmpFile tmpsolv( filesystem::TmpFile::makeSibling( rpmsolv ) );
348 Exception ex("Failed to cache rpm database.");
349 ex.remember(str::form(
350 "Cannot create temporary file under %s.", base.c_str()));
356 if ( ! _root.empty() )
357 cmd << " -r '" << _root << "'";
360 cmd << " '" << rpmsolv << "'";
362 cmd << " > '" << tmpsolv.path() << "'";
364 MIL << "Executing: " << cmd << endl;
365 ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
368 for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
369 WAR << " " << output;
370 cmd << " " << output;
373 int ret = prog.close();
376 Exception ex(str::form("Failed to cache rpm database (%d).", ret));
377 ex.remember( cmd.str() );
381 ret = filesystem::rename( tmpsolv, rpmsolv );
383 ZYPP_THROW(Exception("Failed to move cache to final destination"));
384 // if this fails, don't bother throwing exceptions
385 filesystem::chmod( rpmsolv, 0644 );
387 rpmstatus.saveToCookieFile(rpmsolvcookie);
390 guard.resetDispose();
394 void TargetImpl::unload()
396 Repository system( sat::Pool::instance().findSystemRepo() );
398 system.eraseFromPool();
402 void TargetImpl::load()
406 // now add the repos to the pool
407 sat::Pool satpool( sat::Pool::instance() );
408 Pathname rpmsolv( Pathname::assertprefix( _root,
409 ZConfig::instance().repoSolvfilesPath() / satpool.systemRepoName() / "solv" ) );
410 MIL << "adding " << rpmsolv << " to pool(" << satpool.systemRepoName() << ")" << endl;
412 // Providing an empty system repo, unload any old content
413 Repository system( sat::Pool::instance().findSystemRepo() );
414 if ( system && ! system.solvablesEmpty() )
416 system.eraseFromPool(); // invalidates system
420 system = satpool.systemRepo();
425 system.addSolv( rpmsolv );
427 catch ( const Exception & exp )
430 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
434 system.addSolv( rpmsolv );
437 // (Re)Load the requested locales et al.
438 // If the requested locales are empty, we leave the pool untouched
439 // to avoid undoing changes the application applied. We expect this
440 // to happen on a bare metal installation only. An already existing
441 // target should be loaded before its settings are changed.
443 const LocaleSet & requestedLocales( _requestedLocalesFile.locales() );
444 if ( ! requestedLocales.empty() )
446 satpool.setRequestedLocales( requestedLocales );
450 const SoftLocksFile::Data & softLocks( _softLocksFile.data() );
451 if ( ! softLocks.empty() )
453 ResPool::instance().setAutoSoftLocks( softLocks );
456 if ( ZConfig::instance().apply_locks_file() )
458 const HardLocksFile::Data & hardLocks( _hardLocksFile.data() );
459 if ( ! hardLocks.empty() )
461 ResPool::instance().setHardLockQueries( hardLocks );
466 MIL << "Target loaded: " << system.solvablesSize() << " resolvables" << endl;
469 ZYppCommitResult TargetImpl::commit( ResPool pool_r, const ZYppCommitPolicy & policy_rX )
471 // ----------------------------------------------------------------- //
472 // Fake outstanding YCP fix: Honour restriction to media 1
473 // at installation, but install all remaining packages if post-boot.
474 ZYppCommitPolicy policy_r( policy_rX );
475 if ( policy_r.restrictToMedia() > 1 )
477 // ----------------------------------------------------------------- //
479 MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
481 ///////////////////////////////////////////////////////////////////
482 // Store non-package data:
483 ///////////////////////////////////////////////////////////////////
484 filesystem::assert_dir( home() );
486 _requestedLocalesFile.setLocales( pool_r.getRequestedLocales() );
489 SoftLocksFile::Data newdata;
490 pool_r.getActiveSoftLocks( newdata );
491 _softLocksFile.setData( newdata );
494 if ( ZConfig::instance().apply_locks_file() )
496 HardLocksFile::Data newdata;
497 pool_r.getHardLockQueries( newdata );
498 _hardLocksFile.setData( newdata );
501 ///////////////////////////////////////////////////////////////////
503 ///////////////////////////////////////////////////////////////////
504 if ( root() == "/" && CommitLog::fname().empty() )
506 // Yes, we simply hijack /var/log/YaST2/y2logRPM
507 // until we maintain some zypp history database.
508 CommitLog::setFname( "/var/log/YaST2/y2logRPM" );
511 ZYppCommitResult result;
513 TargetImpl::PoolItemList to_uninstall;
514 TargetImpl::PoolItemList to_install;
515 TargetImpl::PoolItemList to_srcinstall;
518 pool::GetResolvablesToInsDel
519 collect( pool_r, policy_r.restrictToMedia() ? pool::GetResolvablesToInsDel::ORDER_BY_MEDIANR
520 : pool::GetResolvablesToInsDel::ORDER_BY_SOURCE );
521 MIL << "GetResolvablesToInsDel: " << endl << collect << endl;
522 to_uninstall.swap( collect._toDelete );
523 to_install.swap( collect._toInstall );
524 to_srcinstall.swap( collect._toSrcinstall );
527 if ( policy_r.restrictToMedia() )
529 MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
532 commit (to_uninstall, policy_r, pool_r );
534 if (policy_r.restrictToMedia() == 0)
536 result._remaining = commit( to_install, policy_r, pool_r );
537 result._srcremaining = commit( to_srcinstall, policy_r, pool_r );
541 TargetImpl::PoolItemList current_install;
542 TargetImpl::PoolItemList current_srcinstall;
544 // Collect until the 1st package from an unwanted media occurs.
545 // Further collection could violate install order.
546 bool hitUnwantedMedia = false;
547 for (TargetImpl::PoolItemList::iterator it = to_install.begin(); it != to_install.end(); ++it)
549 ResObject::constPtr res( it->resolvable() );
551 if ( hitUnwantedMedia
552 || ( res->mediaNr() && res->mediaNr() != policy_r.restrictToMedia() ) )
554 hitUnwantedMedia = true;
555 result._remaining.push_back( *it );
559 current_install.push_back( *it );
563 TargetImpl::PoolItemList bad = commit( current_install, policy_r, pool_r );
564 result._remaining.insert(result._remaining.end(), bad.begin(), bad.end());
566 for (TargetImpl::PoolItemList::iterator it = to_srcinstall.begin(); it != to_srcinstall.end(); ++it)
568 Resolvable::constPtr res( it->resolvable() );
569 Package::constPtr pkg( asKind<Package>(res) );
570 if (pkg && policy_r.restrictToMedia() != pkg->mediaNr()) // check medianr for packages only
572 XXX << "Package " << *pkg << ", wrong media " << pkg->mediaNr() << endl;
573 result._srcremaining.push_back( *it );
577 current_srcinstall.push_back( *it );
580 bad = commit( current_srcinstall, policy_r, pool_r );
581 result._srcremaining.insert(result._srcremaining.end(), bad.begin(), bad.end());
584 // Try to rebuild solv file while rpm database is still in cache.
587 result._result = (to_install.size() - result._remaining.size());
588 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
593 TargetImpl::PoolItemList
594 TargetImpl::commit( const TargetImpl::PoolItemList & items_r,
595 const ZYppCommitPolicy & policy_r,
596 const ResPool & pool_r )
598 TargetImpl::PoolItemList remaining;
599 repo::RepoMediaAccess access;
600 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << endl;
604 // remember the last used source (if any)
605 Repository lastUsedRepo;
607 RepoProvidePackage repoProvidePackage( access, pool_r);
608 // prepare the package cache.
609 CommitPackageCache packageCache( items_r.begin(), items_r.end(),
610 root() / "tmp", repoProvidePackage );
612 for (TargetImpl::PoolItemList::const_iterator it = items_r.begin(); it != items_r.end(); it++)
614 if (isKind<Package>(it->resolvable()))
616 Package::constPtr p = asKind<Package>(it->resolvable());
617 if (it->status().isToBeInstalled())
619 ManagedFile localfile;
622 localfile = packageCache.get( it );
624 catch ( const SkipRequestException &e )
627 WAR << "Skipping package " << p << " in commit" << endl;
631 lastUsedRepo = p->repository(); // remember the package source
633 #warning Exception handling
634 // create a installation progress report proxy
635 RpmInstallPackageReceiver progress( it->resolvable() );
639 // Why force and nodeps?
641 // Because zypp builds the transaction and the resolver asserts that
642 // everything is fine.
643 // We use rpm just to unpack and register the package in the database.
644 // We do this step by step, so rpm is not aware of the bigger context.
645 // So we turn off rpms internal checks, because we do it inside zypp.
646 flags |= rpm::RpmDb::RPMINST_NODEPS;
647 flags |= rpm::RpmDb::RPMINST_FORCE;
649 if (p->installOnly()) flags |= rpm::RpmDb::RPMINST_NOUPGRADE;
650 if (policy_r.dryRun()) flags |= rpm::RpmDb::RPMINST_TEST;
651 if (policy_r.rpmNoSignature()) flags |= rpm::RpmDb::RPMINST_NOSIGNATURE;
655 progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
656 rpm().installPackage( localfile, flags );
658 if ( progress.aborted() )
660 WAR << "commit aborted by the user" << endl;
661 progress.disconnect();
667 catch (Exception & excpt_r)
669 ZYPP_CAUGHT(excpt_r);
670 if ( policy_r.dryRun() )
672 WAR << "dry run failed" << endl;
673 progress.disconnect();
677 WAR << "Install failed" << endl;
678 remaining.push_back( *it );
679 progress.disconnect();
684 if ( success && !policy_r.dryRun() )
686 it->status().resetTransact( ResStatus::USER );
687 // check for and run an update script
688 RunUpdateScript(ZConfig::instance().update_scriptsPath(), p->name(), p->edition().version(), p->edition().release());
690 progress.disconnect();
696 RpmRemovePackageReceiver progress( it->resolvable() );
698 unsigned flags = rpm::RpmDb::RPMINST_NODEPS;
699 if (policy_r.dryRun()) flags |= rpm::RpmDb::RPMINST_TEST;
702 rpm().removePackage( p, flags );
704 catch (Exception & excpt_r)
706 WAR << "removal of " << p << " failed";
708 ZYPP_CAUGHT( excpt_r );
711 && !policy_r.dryRun())
713 it->status().resetTransact( ResStatus::USER );
715 progress.disconnect();
718 else if (!policy_r.dryRun()) // other resolvables (non-Package)
720 if (it->status().isToBeInstalled())
722 bool success = false;
725 if (isKind<Message>(it->resolvable()))
727 Message::constPtr m = dynamic_pointer_cast<const Message>(it->resolvable());
728 std::string text = m->text().asString();
730 callback::SendReport<target::MessageResolvableReport> report;
734 MIL << "Displaying the text '" << text << "'" << endl;
736 else if (isKind<Script>(it->resolvable()))
738 ExecuteDoScript( access, asKind<Script>(it->resolvable()));
740 else if (!isKind<Atom>(it->resolvable())) // atoms are re-created from the patch data, no need to save them
742 // #160792 do not just add, also remove older versions
743 if (true) // !installOnly - only on Package?!
745 // this would delete the same item over and over
746 //for (PoolItem old = Helper::findInstalledItem (pool_r, *it); old; )
747 #warning REMOVE ALL OLD VERSIONS AND NOT JUST ONE
748 PoolItem old = Helper::findInstalledItem (pool_r, *it);
751 // FIXME _storage.deleteObject(old.resolvable());
754 // FIXME _storage.storeObject(it->resolvable());
758 catch (Exception & excpt_r)
760 ZYPP_CAUGHT(excpt_r);
761 WAR << "Install of Resolvable from storage failed" << endl;
764 it->status().resetTransact( ResStatus::USER );
767 { // isToBeUninstalled
768 bool success = false;
771 if (isKind<Atom>(it->resolvable()))
773 DBG << "Uninstalling atom - no-op" << endl;
775 else if (isKind<Message>(it->resolvable()))
777 DBG << "Uninstalling message - no-op" << endl;
779 else if (isKind<Script>(it->resolvable()))
781 ExecuteUndoScript( access, asKind<Script>(it->resolvable()));
785 //FIXME _storage.deleteObject(it->resolvable());
789 catch (Exception & excpt_r)
791 ZYPP_CAUGHT(excpt_r);
792 WAR << "Uninstall of Resolvable from storage failed" << endl;
795 it->status().resetTransact( ResStatus::USER );
798 } // other resolvables
802 // we're done with the commit, release the source media
803 // In the case of a single media, end of commit means we don't need _this_
805 // In the case of 'commit any media', end of commit means we're completely
806 // done and don't need the source's media anyways.
809 { // if a source was used
810 //lastUsedRepo.release(); // release their medias
814 ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) );
819 rpm::RpmDb & TargetImpl::rpm()
824 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
826 return _rpm.hasFile(path_str, name_str);
829 /** Set the log file for target */
830 bool TargetImpl::setInstallationLogfile(const Pathname & path_r)
832 CommitLog::setFname(path_r);
836 Date TargetImpl::timestamp() const
838 return _rpm.timestamp();
841 std::string TargetImpl::release() const
843 std::ifstream suseRelease( (_root / "/etc/SuSE-release").c_str() );
844 for( iostr::EachLine in( suseRelease ); in; in.next() )
846 std::string line( str::trim( *in ) );
847 if ( ! line.empty() )
851 return _("Unknown Distribution");
854 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
856 // provide on local disk
857 repo::RepoMediaAccess access_r;
858 repo::SrcPackageProvider prov( access_r );
859 ManagedFile localfile = prov.provideSrcPackage( srcPackage_r );
861 rpm().installPackage ( localfile );
864 /////////////////////////////////////////////////////////////////
865 } // namespace target
866 ///////////////////////////////////////////////////////////////////
867 /////////////////////////////////////////////////////////////////
869 ///////////////////////////////////////////////////////////////////