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