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