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