00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include <sstream>
00014 #include <string>
00015 #include <list>
00016 #include <set>
00017
00018 #include "zypp/base/Logger.h"
00019 #include "zypp/base/Exception.h"
00020 #include "zypp/base/Iterator.h"
00021 #include "zypp/base/Gettext.h"
00022 #include "zypp/base/UserRequestException.h"
00023
00024 #include "zypp/PoolItem.h"
00025 #include "zypp/Resolvable.h"
00026 #include "zypp/ResObject.h"
00027 #include "zypp/Package.h"
00028 #include "zypp/SrcPackage.h"
00029 #include "zypp/Pattern.h"
00030 #include "zypp/Selection.h"
00031 #include "zypp/Script.h"
00032 #include "zypp/Message.h"
00033 #include "zypp/Url.h"
00034
00035 #include "zypp/CapMatchHelper.h"
00036 #include "zypp/ResFilters.h"
00037 #include "zypp/target/CommitLog.h"
00038 #include "zypp/target/TargetImpl.h"
00039 #include "zypp/target/TargetCallbackReceiver.h"
00040 #include "zypp/target/rpm/librpmDb.h"
00041 #include "zypp/target/CommitPackageCache.h"
00042
00043 #include "zypp/pool/GetResolvablesToInsDel.h"
00044 #include "zypp/solver/detail/Helper.h"
00045
00046 #include "zypp/repo/DeltaCandidates.h"
00047 #include "zypp/repo/PackageProvider.h"
00048 #include "zypp/repo/ScriptProvider.h"
00049 #include "zypp/repo/SrcPackageProvider.h"
00050
00051 using namespace std;
00052 using namespace zypp;
00053 using namespace zypp::resfilter;
00054 using zypp::solver::detail::Helper;
00055
00057 namespace zypp
00058 {
00059
00060 namespace target
00061 {
00062
00064 namespace
00065 {
00066 void ExecuteScriptHelper( repo::RepoMediaAccess & access_r,
00067 Script::constPtr script_r,
00068 bool do_r )
00069 {
00070 MIL << "Execute script " << script_r << endl;
00071 if ( ! script_r )
00072 {
00073 INT << "NULL Script passed." << endl;
00074 return;
00075 }
00076
00077 repo::ScriptProvider prov( access_r );
00078 ManagedFile localfile = prov.provideScript( script_r, do_r );
00079
00080 if ( localfile->empty() )
00081 {
00082 DBG << "No " << (do_r?"do":"undo") << " script for " << script_r << endl;
00083 return;
00084 }
00085
00086
00087 callback::SendReport<ScriptResolvableReport> report;
00088 report->start( script_r, localfile,
00089 (do_r ? ScriptResolvableReport::DO
00090 : ScriptResolvableReport::UNDO ) );
00091
00092 PathInfo pi( localfile );
00093 if ( ! pi.isFile() )
00094 {
00095 std::ostringstream err;
00096 err << "Script is not a file: " << pi.fileType() << " " << localfile;
00097 report->problem( err.str() );
00098 ZYPP_THROW(Exception(err.str()));
00099 }
00100
00101 filesystem::chmod( localfile, S_IRUSR|S_IXUSR );
00102 ExternalProgram prog( localfile->asString(), ExternalProgram::Stderr_To_Stdout, false, -1, true );
00103 for ( std::string output = prog.receiveLine(); output.length(); output = prog.receiveLine() )
00104 {
00105 if ( ! report->progress( ScriptResolvableReport::OUTPUT, output ) )
00106 {
00107 WAR << "User request to abort script." << endl;
00108 prog.kill();
00109 }
00110 }
00111
00112 int exitCode = prog.close();
00113 if ( exitCode != 0 )
00114 {
00115 std::ostringstream err;
00116 err << "Script failed with exit code " << exitCode;
00117 report->problem( err.str() );
00118 ZYPP_THROW(Exception(err.str()));
00119 }
00120
00121 report->finish();
00122 return;
00123 }
00124
00125 inline void ExecuteDoScript( repo::RepoMediaAccess & access_r, const Script::constPtr & script_r )
00126 {
00127 ExecuteScriptHelper( access_r, script_r, true );
00128 }
00129
00130 inline void ExecuteUndoScript( repo::RepoMediaAccess & access_r, const Script::constPtr & script_r )
00131 {
00132 ExecuteScriptHelper( access_r, script_r, false );
00133 }
00135 }
00137
00139 namespace
00140 {
00141
00143 struct StorageRemoveObsoleted
00144 {
00145 StorageRemoveObsoleted( storage::PersistentStorage & storage_r,
00146 const PoolItem & byPoolitem_r )
00147 : _storage( storage_r )
00148 , _byPoolitem( byPoolitem_r )
00149 {}
00150
00151 bool operator()( const PoolItem & poolitem_r ) const
00152 {
00153 if ( ! poolitem_r.status().isInstalled() )
00154 return true;
00155
00156 if ( isKind<Package>(poolitem_r.resolvable()) )
00157 {
00158 ERR << "Ignore unsupported Package/non-Package obsolete: "
00159 << _byPoolitem << " obsoletes " << poolitem_r << endl;
00160 return true;
00161 }
00162
00163 try
00164 {
00165 _storage.deleteObject( poolitem_r.resolvable() );
00166 MIL<< "Obsoleted: " << poolitem_r << " (by " << _byPoolitem << ")" << endl;
00167 }
00168 catch ( Exception & excpt_r )
00169 {
00170 ZYPP_CAUGHT( excpt_r );
00171 WAR << "Failed obsolete: " << poolitem_r << " (by " << _byPoolitem << ")" << endl;
00172 }
00173
00174 return true;
00175 }
00176
00177 private:
00178 storage::PersistentStorage & _storage;
00179 const PoolItem _byPoolitem;
00180 };
00181
00187 void obsoleteMatchesFromStorage( storage::PersistentStorage & storage_r,
00188 const ResPool & pool_r,
00189 const PoolItem & byPoolitem_r )
00190 {
00191 forEachPoolItemMatchedBy( pool_r, byPoolitem_r, Dep::OBSOLETES,
00192 OncePerPoolItem( StorageRemoveObsoleted( storage_r,
00193 byPoolitem_r ) ) );
00194 }
00195
00197 }
00199
00201 struct QueryInstalledEditionHelper
00202 {
00203 bool operator()( const std::string & name_r,
00204 const Edition & ed_r,
00205 const Arch & arch_r ) const
00206 {
00207 rpm::librpmDb::db_const_iterator it;
00208 for ( it.findByName( name_r ); *it; ++it )
00209 {
00210 if ( arch_r == it->tag_arch()
00211 && ( ed_r == Edition::noedition || ed_r == it->tag_edition() ) )
00212 {
00213 return true;
00214 }
00215 }
00216 return false;
00217 }
00218 };
00219
00225 struct RepoProvidePackage
00226 {
00227 ResPool _pool;
00228 repo::RepoMediaAccess &_access;
00229
00230 RepoProvidePackage( repo::RepoMediaAccess &access, ResPool pool_r )
00231 : _pool(pool_r), _access(access)
00232 {
00233
00234 }
00235
00236 ManagedFile operator()( const PoolItem & pi )
00237 {
00238
00239
00240 repo::PackageProviderPolicy packageProviderPolicy;
00241 packageProviderPolicy.queryInstalledCB( QueryInstalledEditionHelper() );
00242
00243 Package::constPtr p = asKind<Package>(pi.resolvable());
00244
00245
00246
00247
00248 std::list<Repository> repos( _pool.knownRepositoriesBegin(), _pool.knownRepositoriesEnd() );
00249 repo::DeltaCandidates deltas(repos);
00250 repo::PackageProvider pkgProvider( _access, p, deltas, packageProviderPolicy );
00251 return pkgProvider.providePackage();
00252 }
00253 };
00255
00256 IMPL_PTR_TYPE(TargetImpl);
00257
00258 TargetImpl_Ptr TargetImpl::_nullimpl;
00259
00261 TargetImpl_Ptr TargetImpl::nullimpl()
00262 {
00263 if (_nullimpl == 0)
00264 _nullimpl = new TargetImpl;
00265 return _nullimpl;
00266 }
00267
00269
00270
00271
00272
00273 TargetImpl::TargetImpl(const Pathname & root_r)
00274 : _root(root_r), _storage_enabled(false)
00275 {
00276 _rpm.initDatabase(root_r);
00277 MIL << "Initialized target on " << _root << endl;
00278 }
00279
00281
00282
00283
00284
00285 TargetImpl::~TargetImpl()
00286 {
00287 _rpm.closeDatabase();
00288 MIL << "Targets closed" << endl;
00289 }
00290
00291 bool TargetImpl::isStorageEnabled() const
00292 {
00293 return _storage_enabled;
00294 }
00295
00296
00297 void TargetImpl::enableStorage(const Pathname &root_r)
00298 {
00299 _storage.init(root_r);
00300 _storage_enabled = true;
00301 }
00302
00303 Pathname TargetImpl::root() const
00304 {
00305 return _root;
00306 }
00307
00308 void TargetImpl::loadKindResolvables( const Resolvable::Kind kind )
00309 {
00310
00311 if ( _resstore_loaded[kind] )
00312 return;
00313
00314 if ( kind == ResTraits<zypp::Package>::kind )
00315 {
00316 std::list<Package::Ptr> packages = _rpm.getPackages();
00317 for (std::list<Package::Ptr>::const_iterator it = packages.begin();
00318 it != packages.end();
00319 it++)
00320 {
00321 _store.insert(*it);
00322 }
00323 _resstore_loaded[kind] = true;
00324 }
00325 else
00326 {
00327 if ( isStorageEnabled() )
00328 {
00329
00330 std::list<ResObject::Ptr> resolvables = _storage.storedObjects(kind);
00331 for (std::list<ResObject::Ptr>::iterator it = resolvables.begin();
00332 it != resolvables.end();
00333 it++)
00334 {
00335 _store.insert(*it);
00336 }
00337 }
00338 else
00339 {
00340 WAR << "storage target not enabled" << std::endl;
00341 }
00342 _resstore_loaded[kind] = true;
00343 }
00344 }
00345
00346 ResStore::resfilter_const_iterator TargetImpl::byKindBegin( const ResObject::Kind & kind_r ) const
00347 {
00348 TargetImpl *ptr = const_cast<TargetImpl *>(this);
00349 ptr->loadKindResolvables(kind_r);
00350 resfilter::ResFilter filter = ByKind(kind_r);
00351 return make_filter_iterator( filter, _store.begin(), _store.end() );
00352 }
00353
00354 ResStore::resfilter_const_iterator TargetImpl::byKindEnd( const ResObject::Kind & kind_r ) const
00355 {
00356 TargetImpl *ptr = const_cast<TargetImpl *>(this);
00357 ptr->loadKindResolvables(kind_r);
00358 resfilter::ResFilter filter = ByKind(kind_r);
00359 return make_filter_iterator( filter, _store.end(), _store.end() );
00360 }
00361
00362 const ResStore & TargetImpl::resolvables()
00363 {
00364 loadKindResolvables( ResTraits<zypp::Patch>::kind );
00365 loadKindResolvables( ResTraits<zypp::Selection>::kind );
00366 loadKindResolvables( ResTraits<zypp::Pattern>::kind );
00367 loadKindResolvables( ResTraits<zypp::Product>::kind );
00368 loadKindResolvables( ResTraits<zypp::Language>::kind );
00369 loadKindResolvables( ResTraits<zypp::Package>::kind );
00370 return _store;
00371 }
00372
00373 void TargetImpl::reset()
00374 {
00375
00376 _store.clear();
00377 _resstore_loaded[ResTraits<zypp::Patch>::kind] = false;
00378 _resstore_loaded[ResTraits<zypp::Selection>::kind] = false;
00379 _resstore_loaded[ResTraits<zypp::Pattern>::kind] = false;
00380 _resstore_loaded[ResTraits<zypp::Product>::kind] = false;
00381 _resstore_loaded[ResTraits<zypp::Language>::kind] = false;
00382 _resstore_loaded[ResTraits<zypp::Package>::kind] = false;
00383 }
00384
00385 ZYppCommitResult TargetImpl::commit( ResPool pool_r, const ZYppCommitPolicy & policy_rX )
00386 {
00387
00388
00389
00390 ZYppCommitPolicy policy_r( policy_rX );
00391 if ( policy_r.restrictToMedia() > 1 )
00392 policy_r.allMedia();
00393
00394
00395 MIL << "TargetImpl::commit(<pool>, " << policy_r << ")" << endl;
00396 ZYppCommitResult result;
00397
00398 TargetImpl::PoolItemList to_uninstall;
00399 TargetImpl::PoolItemList to_install;
00400 TargetImpl::PoolItemList to_srcinstall;
00401 {
00402
00403 pool::GetResolvablesToInsDel
00404 collect( pool_r, policy_r.restrictToMedia() ? pool::GetResolvablesToInsDel::ORDER_BY_MEDIANR
00405 : pool::GetResolvablesToInsDel::ORDER_BY_SOURCE );
00406 MIL << "GetResolvablesToInsDel: " << endl << collect << endl;
00407 to_uninstall.swap( collect._toDelete );
00408 to_install.swap( collect._toInstall );
00409 to_srcinstall.swap( collect._toSrcinstall );
00410 }
00411
00412 if ( policy_r.restrictToMedia() )
00413 {
00414 MIL << "Restrict to media number " << policy_r.restrictToMedia() << endl;
00415 }
00416
00417 commit (to_uninstall, policy_r, pool_r );
00418
00419 if (policy_r.restrictToMedia() == 0)
00420 {
00421 result._remaining = commit( to_install, policy_r, pool_r );
00422 result._srcremaining = commit( to_srcinstall, policy_r, pool_r );
00423 }
00424 else
00425 {
00426 TargetImpl::PoolItemList current_install;
00427 TargetImpl::PoolItemList current_srcinstall;
00428
00429
00430
00431 bool hitUnwantedMedia = false;
00432 for (TargetImpl::PoolItemList::iterator it = to_install.begin(); it != to_install.end(); ++it)
00433 {
00434 ResObject::constPtr res( it->resolvable() );
00435
00436 if ( hitUnwantedMedia
00437 || ( res->mediaNr() && res->mediaNr() != policy_r.restrictToMedia() ) )
00438 {
00439 hitUnwantedMedia = true;
00440 result._remaining.push_back( *it );
00441 }
00442 else
00443 {
00444 current_install.push_back( *it );
00445 }
00446 }
00447
00448 TargetImpl::PoolItemList bad = commit( current_install, policy_r, pool_r );
00449 result._remaining.insert(result._remaining.end(), bad.begin(), bad.end());
00450
00451 for (TargetImpl::PoolItemList::iterator it = to_srcinstall.begin(); it != to_srcinstall.end(); ++it)
00452 {
00453 Resolvable::constPtr res( it->resolvable() );
00454 Package::constPtr pkg( asKind<Package>(res) );
00455 if (pkg && policy_r.restrictToMedia() != pkg->mediaNr())
00456 {
00457 XXX << "Package " << *pkg << ", wrong media " << pkg->mediaNr() << endl;
00458 result._srcremaining.push_back( *it );
00459 }
00460 else
00461 {
00462 current_srcinstall.push_back( *it );
00463 }
00464 }
00465 bad = commit( current_srcinstall, policy_r, pool_r );
00466 result._srcremaining.insert(result._srcremaining.end(), bad.begin(), bad.end());
00467 }
00468
00469
00470 result._result = (to_install.size() - result._remaining.size());
00471 MIL << "TargetImpl::commit(<pool>, " << policy_r << ") returns: " << result << endl;
00472 return result;
00473 }
00474
00475
00476 TargetImpl::PoolItemList
00477 TargetImpl::commit( const TargetImpl::PoolItemList & items_r,
00478 const ZYppCommitPolicy & policy_r,
00479 const ResPool & pool_r )
00480 {
00481 TargetImpl::PoolItemList remaining;
00482 repo::RepoMediaAccess access;
00483 MIL << "TargetImpl::commit(<list>" << policy_r << ")" << endl;
00484
00485 bool abort = false;
00486
00487
00488 Repository lastUsedRepo;
00489
00490 RepoProvidePackage repoProvidePackage( access, pool_r);
00491
00492 CommitPackageCache packageCache( items_r.begin(), items_r.end(),
00493 root() / "tmp", repoProvidePackage );
00494
00495 for (TargetImpl::PoolItemList::const_iterator it = items_r.begin(); it != items_r.end(); it++)
00496 {
00497 if (isKind<Package>(it->resolvable()))
00498 {
00499 Package::constPtr p = asKind<Package>(it->resolvable());
00500 if (it->status().isToBeInstalled())
00501 {
00502 ManagedFile localfile;
00503 try
00504 {
00505 localfile = packageCache.get( it );
00506 }
00507 catch ( const SkipRequestException &e )
00508 {
00509 ZYPP_CAUGHT( e );
00510 WAR << "Skipping package " << p << " in commit" << endl;
00511 continue;
00512 }
00513
00514 lastUsedRepo = p->repository();
00515
00516 #warning Exception handling
00517
00518 RpmInstallPackageReceiver progress( it->resolvable() );
00519 progress.connect();
00520 bool success = true;
00521 unsigned flags = 0;
00522
00523
00524
00525
00526
00527
00528
00529 flags |= rpm::RpmDb::RPMINST_NODEPS;
00530 flags |= rpm::RpmDb::RPMINST_FORCE;
00531
00532 if (p->installOnly()) flags |= rpm::RpmDb::RPMINST_NOUPGRADE;
00533 if (policy_r.dryRun()) flags |= rpm::RpmDb::RPMINST_TEST;
00534 if (policy_r.rpmNoSignature()) flags |= rpm::RpmDb::RPMINST_NOSIGNATURE;
00535
00536 try
00537 {
00538 progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
00539 rpm().installPackage( localfile, flags );
00540
00541 if ( progress.aborted() )
00542 {
00543 WAR << "commit aborted by the user" << endl;
00544 progress.disconnect();
00545 success = false;
00546 abort = true;
00547 break;
00548 }
00549 }
00550 catch (Exception & excpt_r)
00551 {
00552 ZYPP_CAUGHT(excpt_r);
00553 if ( policy_r.dryRun() )
00554 {
00555 WAR << "dry run failed" << endl;
00556 progress.disconnect();
00557 break;
00558 }
00559
00560 WAR << "Install failed" << endl;
00561 remaining.push_back( *it );
00562 success = false;
00563 }
00564
00565 if ( success && !policy_r.dryRun() )
00566 {
00567 it->status().resetTransact( ResStatus::USER );
00568 }
00569 progress.disconnect();
00570 }
00571 else
00572 {
00573 bool success = true;
00574
00575 RpmRemovePackageReceiver progress( it->resolvable() );
00576 progress.connect();
00577 unsigned flags = rpm::RpmDb::RPMINST_NODEPS;
00578 if (policy_r.dryRun()) flags |= rpm::RpmDb::RPMINST_TEST;
00579 try
00580 {
00581 rpm().removePackage( p, flags );
00582 }
00583 catch (Exception & excpt_r)
00584 {
00585 WAR << "removal of " << p << " failed";
00586 success = false;
00587 ZYPP_CAUGHT( excpt_r );
00588 }
00589 if (success
00590 && !policy_r.dryRun())
00591 {
00592 it->status().resetTransact( ResStatus::USER );
00593 }
00594 progress.disconnect();
00595 }
00596 }
00597 else if (!policy_r.dryRun())
00598 {
00599 if ( isStorageEnabled() )
00600 {
00601 if (it->status().isToBeInstalled())
00602 {
00603
00604 obsoleteMatchesFromStorage( _storage, pool_r, *it );
00605
00606 bool success = false;
00607 try
00608 {
00609 if (isKind<Message>(it->resolvable()))
00610 {
00611 Message::constPtr m = dynamic_pointer_cast<const Message>(it->resolvable());
00612 std::string text = m->text().asString();
00613
00614 callback::SendReport<target::MessageResolvableReport> report;
00615
00616 report->show( m );
00617
00618 MIL << "Displaying the text '" << text << "'" << endl;
00619 }
00620 else if (isKind<Script>(it->resolvable()))
00621 {
00622 ExecuteDoScript( access, asKind<Script>(it->resolvable()) );
00623 }
00624 else if (!isKind<Atom>(it->resolvable()))
00625 {
00626
00627 if (true)
00628 {
00629
00630
00631 #warning REMOVE ALL OLD VERSIONS AND NOT JUST ONE
00632 PoolItem_Ref old = Helper::findInstalledItem (pool_r, *it);
00633 if (old)
00634 {
00635 _storage.deleteObject(old.resolvable());
00636 }
00637 }
00638 _storage.storeObject(it->resolvable());
00639 }
00640 success = true;
00641 }
00642 catch (Exception & excpt_r)
00643 {
00644 ZYPP_CAUGHT(excpt_r);
00645 WAR << "Install of Resolvable from storage failed" << endl;
00646 }
00647 if (success)
00648 it->status().resetTransact( ResStatus::USER );
00649 }
00650 else
00651 {
00652 bool success = false;
00653 try
00654 {
00655 if (isKind<Atom>(it->resolvable()))
00656 {
00657 DBG << "Uninstalling atom - no-op" << endl;
00658 }
00659 else if (isKind<Message>(it->resolvable()))
00660 {
00661 DBG << "Uninstalling message - no-op" << endl;
00662 }
00663 else if (isKind<Script>(it->resolvable()))
00664 {
00665 ExecuteUndoScript( access, asKind<Script>(it->resolvable()) );
00666 }
00667 else
00668 {
00669 _storage.deleteObject(it->resolvable());
00670 }
00671 success = true;
00672 }
00673 catch (Exception & excpt_r)
00674 {
00675 ZYPP_CAUGHT(excpt_r);
00676 WAR << "Uninstall of Resolvable from storage failed" << endl;
00677 }
00678 if (success)
00679 it->status().resetTransact( ResStatus::USER );
00680 }
00681 }
00682 else
00683 {
00684 WAR << "storage target disabled" << std::endl;
00685 }
00686
00687 }
00688
00689 }
00690
00691
00692
00693
00694
00695
00696
00697 if (lastUsedRepo)
00698 {
00699
00700 }
00701
00702 if ( abort )
00703 ZYPP_THROW( TargetAbortedException( N_("Installation has been aborted as directed.") ) );
00704
00705 return remaining;
00706 }
00707
00708 rpm::RpmDb & TargetImpl::rpm()
00709 {
00710 return _rpm;
00711 }
00712
00713 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
00714 {
00715 return _rpm.hasFile(path_str, name_str);
00716 }
00717
00720 ResObject::constPtr TargetImpl::whoOwnsFile (const std::string & path_str) const
00721 {
00722 string name = _rpm.whoOwnsFile (path_str);
00723 if (name.empty())
00724 return NULL;
00725
00726 for (ResStore::const_iterator it = _store.begin(); it != _store.end(); ++it)
00727 {
00728 if ((*it)->name() == name)
00729 {
00730 return *it;
00731 }
00732 }
00733 return NULL;
00734 }
00735
00737 bool TargetImpl::setInstallationLogfile(const Pathname & path_r)
00738 {
00739 CommitLog::setFname(path_r);
00740 return true;
00741 }
00742
00743 Date TargetImpl::timestamp() const
00744 {
00745 Date ts_rpm;
00746 Date ts_store;
00747
00748 ts_rpm = _rpm.timestamp();
00749
00750 if ( isStorageEnabled() )
00751 ts_store = _storage.timestamp();
00752
00753 if ( ts_rpm > ts_store )
00754 {
00755 return ts_rpm;
00756 }
00757 else if (ts_rpm < ts_store)
00758 {
00759 return ts_store;
00760 }
00761 else
00762 {
00763
00764 if ( ts_rpm != 0 )
00765 return ts_rpm;
00766 else
00767 return Date::now();
00768 }
00769 }
00770
00771 void TargetImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
00772 {
00773
00774 repo::RepoMediaAccess access_r;
00775 repo::SrcPackageProvider prov( access_r );
00776 ManagedFile localfile = prov.provideSrcPackage( srcPackage_r );
00777
00778 rpm().installPackage ( localfile );
00779 }
00780
00782 }
00785 }