1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/TargetImpl.cc
17 #include "zypp/base/Logger.h"
18 #include "zypp/base/Exception.h"
19 #include "zypp/base/Gettext.h"
20 #include "zypp/PoolItem.h"
21 #include "zypp/Resolvable.h"
22 #include "zypp/ResObject.h"
23 #include "zypp/Package.h"
24 #include "zypp/Script.h"
25 #include "zypp/Message.h"
26 #include "zypp/Source.h"
29 #include "zypp/target/TargetImpl.h"
30 #include "zypp/target/TargetCallbackReceiver.h"
32 #include "zypp/solver/detail/InstallOrder.h"
35 using zypp::solver::detail::InstallOrder;
37 ///////////////////////////////////////////////////////////////////
39 { /////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////
42 { /////////////////////////////////////////////////////////////////
44 ///////////////////////////////////////////////////////////////////
46 { /////////////////////////////////////////////////////////////////
52 /////////////////////////////////////////////////////////////////
54 ///////////////////////////////////////////////////////////////////
56 IMPL_PTR_TYPE(TargetImpl);
58 TargetImpl_Ptr TargetImpl::_nullimpl;
60 /** Null implementation */
61 TargetImpl_Ptr TargetImpl::nullimpl()
64 _nullimpl = new TargetImpl;
69 ///////////////////////////////////////////////////////////////////
71 // METHOD NAME : TargetImpl::TargetImpl
74 TargetImpl::TargetImpl(const Pathname & root_r)
77 _rpm.initDatabase(_root);
78 _storage_enabled = false;
79 MIL << "Initialized target on " << _root << endl;
82 ///////////////////////////////////////////////////////////////////
84 // METHOD NAME : TargetImpl::~TargetImpl
87 TargetImpl::~TargetImpl()
90 MIL << "Targets closed" << endl;
93 bool TargetImpl::isStorageEnabled() const
95 return _storage_enabled;
99 void TargetImpl::enableStorage(const Pathname &root_r)
101 _storage.init(root_r);
102 _storage_enabled = true;
105 Pathname TargetImpl::root() const
110 const ResStore & TargetImpl::resolvables()
114 std::list<Package::Ptr> packages = _rpm.getPackages();
115 for (std::list<Package::Ptr>::const_iterator it = packages.begin();
116 it != packages.end();
122 if ( isStorageEnabled() )
124 // resolvables stored in the zypp storage database
125 std::list<ResObject::Ptr> resolvables = _storage.storedObjects();
126 for (std::list<ResObject::Ptr>::iterator it = resolvables.begin();
127 it != resolvables.end();
135 WAR << "storage target not enabled" << std::endl;
142 Pathname TargetImpl::getRpmFile(Package::constPtr package)
144 callback::SendReport<source::DownloadResolvableReport> report;
146 #warning FIXME: error handling
148 report->start( package, package->source().url() );
150 Pathname file = package->getPlainRpm();
152 report->finish( package, source::DownloadResolvableReport::NO_ERROR, "" );
158 int TargetImpl::commit(ResPool pool_r, unsigned int medianr, TargetImpl::PoolItemList & errors_r, TargetImpl::PoolItemList & remaining_r, TargetImpl::PoolItemList & srcremaining_r)
160 MIL << "TargetImpl::commit(<pool>, " << medianr << ")" << endl;
164 srcremaining_r.clear();
166 TargetImpl::PoolItemList to_uninstall;
167 TargetImpl::PoolItemList to_install;
168 TargetImpl::PoolItemList to_srcinstall;
169 getResolvablesToInsDel( pool_r, to_uninstall, to_install, to_srcinstall );
172 MIL << "Restrict to media number " << medianr << endl;
175 commit (to_uninstall);
177 if (medianr == 0) { // commit all
178 remaining_r = commit( to_install );
179 srcremaining_r = commit( to_srcinstall );
183 TargetImpl::PoolItemList current_install;
184 TargetImpl::PoolItemList current_srcinstall;
186 for (TargetImpl::PoolItemList::iterator it = to_install.begin(); it != to_install.end(); ++it)
188 Resolvable::constPtr res( it->resolvable() );
189 Package::constPtr pkg( asKind<Package>(res) );
190 if (pkg && medianr != pkg->mediaId()) // check medianr for packages only
192 XXX << "Package " << *pkg << ", wrong media " << pkg->mediaId() << endl;
193 remaining_r.push_back( *it );
197 current_install.push_back( *it );
200 TargetImpl::PoolItemList bad = commit (current_install);
201 remaining_r.insert(remaining_r.end(), bad.begin(), bad.end());
203 for (TargetImpl::PoolItemList::iterator it = to_srcinstall.begin(); it != to_srcinstall.end(); ++it)
205 Resolvable::constPtr res( it->resolvable() );
206 Package::constPtr pkg( asKind<Package>(res) );
207 if (pkg && medianr != pkg->mediaId()) // check medianr for packages only
209 XXX << "Package " << *pkg << ", wrong media " << pkg->mediaId() << endl;
210 srcremaining_r.push_back( *it );
213 current_srcinstall.push_back( *it );
216 bad = commit (current_srcinstall);
217 srcremaining_r.insert(srcremaining_r.end(), bad.begin(), bad.end());
219 return to_install.size() - remaining_r.size();
223 TargetImpl::PoolItemList
224 TargetImpl::commit( const TargetImpl::PoolItemList & items_r)
226 TargetImpl::PoolItemList remaining;
228 MIL << "TargetImpl::commit(<list>)" << endl;
232 // remember the last used source (if any)
233 Source_Ref lastUsedSource;
235 for (TargetImpl::PoolItemList::const_iterator it = items_r.begin(); it != items_r.end(); it++)
237 if (isKind<Package>(it->resolvable()))
239 Package::constPtr p = dynamic_pointer_cast<const Package>(it->resolvable());
240 if (it->status().isToBeInstalled())
242 Pathname localfile = getRpmFile( p );
243 lastUsedSource = p->source(); // remember the package source
245 #warning Exception handling
246 // create a installation progress report proxy
247 RpmInstallPackageReceiver progress( it->resolvable() );
252 progress.tryLevel( target::rpm::InstallResolvableReport::RPM );
254 rpm().installPackage(localfile,
255 p->installOnly() ? rpm::RpmDb::RPMINST_NOUPGRADE : 0);
257 if( progress.aborted() )
259 WAR << "commit aborted by the user" << endl;
260 progress.disconnect();
266 catch (Exception & excpt_r) {
267 ZYPP_CAUGHT(excpt_r);
268 WAR << "Install failed, retrying with --nodeps" << endl;
270 progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS );
271 rpm().installPackage(localfile,
272 p->installOnly() ? rpm::RpmDb::RPMINST_NOUPGRADE : rpm::RpmDb::RPMINST_NODEPS);
274 if( progress.aborted() )
276 WAR << "commit aborted by the user" << endl;
278 progress.disconnect();
282 catch (Exception & excpt_r)
284 ZYPP_CAUGHT(excpt_r);
285 WAR << "Install failed again, retrying with --force --nodeps" << endl;
288 progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
289 rpm().installPackage(localfile,
290 p->installOnly() ? rpm::RpmDb::RPMINST_NOUPGRADE : (rpm::RpmDb::RPMINST_NODEPS|rpm::RpmDb::RPMINST_FORCE));
292 catch (Exception & excpt_r) {
293 remaining.push_back( *it );
295 ZYPP_CAUGHT(excpt_r);
298 if( progress.aborted() )
300 WAR << "commit aborted by the user" << endl;
302 progress.disconnect();
308 it->status().setTransact( false, ResStatus::USER );
310 progress.disconnect();
311 p->source().releaseFile(p->plainRpm(), p->mediaId());
317 RpmRemovePackageReceiver progress(it->resolvable());
320 rpm().removePackage( p, rpm::RpmDb::RPMINST_NODEPS );
322 catch (Exception & excpt_r) {
323 WAR << "removal of " << p << " failed";
325 ZYPP_CAUGHT( excpt_r );
328 it->status().setTransact( false, ResStatus::USER );
330 progress.disconnect();
333 else // other resolvables
335 if ( isStorageEnabled() )
337 if (it->status().isToBeInstalled())
339 bool success = false;
342 if (isKind<Message>(it->resolvable()))
344 Message::constPtr m = dynamic_pointer_cast<const Message>(it->resolvable());
345 std::string text = m->text().asString();
347 callback::SendReport<target::MessageResolvableReport> report;
351 MIL << "Displaying the text " << text << endl;
353 else if (isKind<Script>(it->resolvable()))
355 Script::constPtr s = dynamic_pointer_cast<const Script>(it->resolvable());
356 Pathname p = s->do_script();
357 if (p != "" && p != "/")
359 ExternalProgram* prog = new ExternalProgram(p.asString(), ExternalProgram::Discard_Stderr, false, -1, true);
361 ZYPP_THROW(Exception("Cannot run the script"));
362 int retval = prog->close();
365 ZYPP_THROW(Exception("Exit code of script is non-zero"));
369 ERR << "Do script not defined" << endl;
374 _storage.storeObject(it->resolvable());
378 catch (Exception & excpt_r)
380 ZYPP_CAUGHT(excpt_r);
381 WAR << "Install of Resolvable from storage failed" << endl;
384 it->status().setTransact( false, ResStatus::USER );
388 bool success = false;
391 if (isKind<Message>(it->resolvable()))
393 DBG << "Uninstalling message - no-op" << endl;
395 else if (isKind<Script>(it->resolvable()))
397 Script::constPtr s = dynamic_pointer_cast<const Script>(it->resolvable());
398 Pathname p = s->undo_script();
399 if (! s->undo_available())
401 DBG << "Undo script not available" << endl;
403 if (p != "" && p != "/")
405 ExternalProgram* prog = new ExternalProgram(p.asString(), ExternalProgram::Discard_Stderr, false, -1, true);
407 ZYPP_THROW(Exception("Cannot run the script"));
408 int retval = prog->close();
411 ZYPP_THROW(Exception("Exit code of script is non-zero"));
415 ERR << "Undo script not defined" << endl;
420 _storage.deleteObject(it->resolvable());
424 catch (Exception & excpt_r)
426 ZYPP_CAUGHT(excpt_r);
427 WAR << "Uninstall of Resolvable from storage failed" << endl;
430 it->status().setTransact( false, ResStatus::USER );
435 WAR << "storage target disabled" << std::endl;
438 } // other resolvables
442 // we're done with the commit, release the source media
443 // In the case of a single media, end of commit means we don't need _this_
445 // In the case of 'commit any media', end of commit means we're completely
446 // done and don't need the source's media anyways.
448 if (lastUsedSource) { // if a source was used
449 lastUsedSource.release(); // release their medias
453 ZYPP_THROW( TargetAbortedException( N_("Target commit aborted by user.") ) );
458 rpm::RpmDb & TargetImpl::rpm()
461 bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
462 { return _rpm.hasFile(path_str, name_str); }
464 /** Return the resolvable which provides path_str (rpm -qf)
465 return NULL if no resolvable provides this file */
466 ResObject::constPtr TargetImpl::whoOwnsFile (const std::string & path_str) const
468 string name = _rpm.whoOwnsFile (path_str);
472 for (ResStore::const_iterator it = _store.begin(); it != _store.end(); ++it) {
473 if ((*it)->name() == name) {
480 /** Set the log file for target */
481 bool TargetImpl::setInstallationLogfile(const Pathname & path_r)
483 return rpm::RpmDb::setInstallationLogfile(path_r);
486 //-----------------------------------------------------------------------------
487 /******************************************************************
490 ** FUNCTION NAME : strip_obsoleted_to_delete
491 ** FUNCTION TYPE : void
493 ** strip packages to_delete which get obsoleted by
494 ** to_install (i.e. delay deletion in case the
495 ** obsoleting package likes to save whatever...
499 strip_obsoleted_to_delete( TargetImpl::PoolItemList & deleteList_r,
500 const TargetImpl::PoolItemList & instlist_r )
502 if ( deleteList_r.size() == 0 || instlist_r.size() == 0 )
503 return; // ---> nothing to do
505 // build obsoletes from instlist_r
507 for ( TargetImpl::PoolItemList::const_iterator it = instlist_r.begin();
508 it != instlist_r.end(); ++it )
510 PoolItem_Ref item( *it );
511 obsoletes.insert( item->dep(Dep::OBSOLETES).begin(), item->dep(Dep::OBSOLETES).end() );
513 if ( obsoletes.size() == 0 )
514 return; // ---> nothing to do
517 TargetImpl::PoolItemList undelayed;
518 // forall applDelete Packages...
519 for ( TargetImpl::PoolItemList::iterator it = deleteList_r.begin();
520 it != deleteList_r.end(); ++it )
522 PoolItem_Ref ipkg( *it );
523 bool delayPkg = false;
524 // ...check whether an obsoletes....
525 for ( CapSet::iterator obs = obsoletes.begin();
526 ! delayPkg && obs != obsoletes.end(); ++obs )
528 // ...matches anything provided by the package?
529 for ( CapSet::const_iterator prov = ipkg->dep(Dep::PROVIDES).begin();
530 prov != ipkg->dep(Dep::PROVIDES).end(); ++prov )
532 if ( obs->matches( *prov ) == CapMatch::yes )
534 // if so, delay package deletion
535 DBG << "Ignore appl_delete (should be obsoleted): " << ipkg << endl;
537 ipkg.status().setTransact( false, ResStatus::USER );
543 DBG << "undelayed " << ipkg << endl;
544 undelayed.push_back( ipkg );
548 deleteList_r.swap( undelayed );
555 TargetImpl::getResolvablesToInsDel ( const ResPool pool_r,
556 TargetImpl::PoolItemList & dellist_r,
557 TargetImpl::PoolItemList & instlist_r,
558 TargetImpl::PoolItemList & srclist_r ) const
563 TargetImpl::PoolItemList nonpkglist;
565 for ( ResPool::const_iterator it = pool_r.begin(); it != pool_r.end(); ++it )
567 if (it->status().isToBeInstalled())
569 if ((*it)->kind() == ResTraits<SrcPackage>::kind) {
570 srclist_r.push_back( *it );
572 else if ((*it)->kind() != ResTraits<Package>::kind) {
573 nonpkglist.push_back( *it );
576 instlist_r.push_back( *it );
578 else if (it->status().isToBeUninstalled())
580 if ( it->status().isToBeUninstalledDueToObsolete() )
582 DBG << "Ignore auto_delete (should be obsoleted): " << *it << endl;
584 else if ( it->status().isToBeUninstalledDueToUpgrade() )
586 DBG << "Ignore auto_delete (should be upgraded): " << *it << endl;
589 dellist_r.push_back( *it );
594 MIL << "ResolvablesToInsDel: delete " << dellist_r.size()
595 << ", install " << instlist_r.size()
596 << ", srcinstall " << srclist_r.size()
597 << ", nonpkg " << nonpkglist.size() << endl;
599 ///////////////////////////////////////////////////////////////////
601 // strip packages to_delete which get obsoleted by
602 // to_install (i.e. delay deletion in case the
603 // obsoleting package likes to save whatever...
605 ///////////////////////////////////////////////////////////////////
606 strip_obsoleted_to_delete( dellist_r, instlist_r );
608 if ( dellist_r.size() ) {
609 ///////////////////////////////////////////////////////////////////
611 // sort delete list...
613 ///////////////////////////////////////////////////////////////////
614 TargetImpl::PoolItemSet delset; // for delete order
615 TargetImpl::PoolItemSet dummy; // dummy, empty, should contain already installed
616 for ( TargetImpl::PoolItemList::iterator pkgIt = dellist_r.begin();
617 pkgIt != dellist_r.end(); ++pkgIt )
619 delset.insert( *pkgIt );
622 InstallOrder order( pool_r, delset, dummy ); // sort according top prereq
624 const TargetImpl::PoolItemList dsorted( order.getTopSorted() );
627 for ( TargetImpl::PoolItemList::const_reverse_iterator cit = dsorted.rbegin();
628 cit != dsorted.rend(); ++cit )
630 dellist_r.push_back( *cit );
634 ///////////////////////////////////////////////////////////////////
636 // sort installed list...
638 ///////////////////////////////////////////////////////////////////
639 if ( instlist_r.empty() ) {
640 instlist_r.splice( instlist_r.end(), nonpkglist );
644 #warning Source Rank Priority ?
646 ///////////////////////////////////////////////////////////////////
647 // Get desired order of InstSrc'es to install from.
648 ///////////////////////////////////////////////////////////////////
649 typedef map<unsigned,unsigned> RankPriority;
651 RankPriority rankPriority;
653 InstSrcManager::ISrcIdList sourcerank( Y2PM::instSrcManager().instOrderSources() );
654 // map InstSrc rank to install priority
656 for ( InstSrcManager::ISrcIdList::const_iterator it = sourcerank.begin();
657 it != sourcerank.end(); ++it, ++prio ) {
658 rankPriority[(*it)->descr()->default_rank()] = prio;
663 ///////////////////////////////////////////////////////////////////
664 // Compute install order according to packages prereq.
665 // Try to group packages with respect to the desired install order
666 ///////////////////////////////////////////////////////////////////
667 // backup list for debug purpose.
668 // You can as well build the set, clear the list and rebuild it in install order.
669 TargetImpl::PoolItemList instbackup_r;
670 instbackup_r.swap( instlist_r );
672 TargetImpl::PoolItemSet insset; // for install order
673 TargetImpl::PoolItemSet installed; // dummy, empty, should contain already installed
674 for ( TargetImpl::PoolItemList::iterator resIt = instbackup_r.begin(); resIt != instbackup_r.end(); ++resIt ) {
675 insset.insert( *resIt );
677 InstallOrder order( pool_r, insset, installed );
678 // start recursive depth-first-search
680 MIL << "order.init() done" << endl;
681 order.printAdj( XXX, false );
682 ///////////////////////////////////////////////////////////////////
683 // build install list in install order
684 ///////////////////////////////////////////////////////////////////
685 TargetImpl::PoolItemList best_list;
686 // unsigned best_prio = 0;
687 unsigned best_medianum = 0;
689 TargetImpl::PoolItemList last_list;
690 // unsigned last_prio = 0;
691 unsigned last_medianum = 0;
693 TargetImpl::PoolItemList other_list;
695 for ( TargetImpl::PoolItemList items = order.computeNextSet(); ! items.empty(); items = order.computeNextSet() )
697 MIL << "order.computeNextSet: " << items.size() << " resolvables" << endl;
698 ///////////////////////////////////////////////////////////////////
699 // items contains all packages we could install now. Pick all packages
700 // from current media, or best media if none for current.
701 ///////////////////////////////////////////////////////////////////
707 for ( TargetImpl::PoolItemList::iterator cit = items.begin(); cit != items.end(); ++cit )
709 Resolvable::constPtr res( cit->resolvable() );
711 Package::constPtr cpkg( asKind<Package>(res) );
713 XXX << "Not a package " << *cit << endl;
714 order.setInstalled( *cit );
715 other_list.push_back( *cit );
718 XXX << "Package " << *cpkg << ", media " << cpkg->mediaId() << " last_medianum " << last_medianum << " best_medianum " << best_medianum << endl;
719 if ( // rankPriority[cpkg->instSrcRank()] == last_prio &&
720 cpkg->mediaId() == last_medianum ) {
721 // prefer packages on current media.
722 last_list.push_back( *cit );
726 if ( last_list.empty() ) {
727 // check for best media as long as there are no packages for current media.
729 if ( ! best_list.empty() ) {
732 if ( rankPriority[cpkg->instSrcRank()] < best_prio ) {
733 best_list.clear(); // new best
734 } else if ( rankPriority[cpkg->instSrcRank()] == best_prio ) {
737 if ( cpkg->mediaId() < best_medianum ) {
738 best_list.clear(); // new best
739 } else if ( cpkg->mediaId() == best_medianum ) {
740 best_list.push_back( *cit ); // same as best -> add
752 if ( best_list.empty() )
754 // first package or new best
755 best_list.push_back( *cit );
756 // best_prio = rankPriority[cpkg->instSrcRank()];
757 best_medianum = cpkg->mediaId();
762 } // for all packages in current set
764 ///////////////////////////////////////////////////////////////////
765 // remove packages picked from install order and append them to
767 ///////////////////////////////////////////////////////////////////
768 TargetImpl::PoolItemList & take_list( last_list.empty() ? best_list : last_list );
769 if ( last_list.empty() )
771 MIL << "SET NEW media " << best_medianum << endl;
772 // last_prio = best_prio;
773 last_medianum = best_medianum;
777 MIL << "SET CONTINUE" << endl;
780 for ( TargetImpl::PoolItemList::iterator it = take_list.begin(); it != take_list.end(); ++it )
782 order.setInstalled( *it );
783 XXX << "SET isrc " << (*it)->name() << endl;
785 // move everthing from take_list to the end of instlist_r, clean take_list
786 instlist_r.splice( instlist_r.end(), take_list );
787 // same for other_list
788 instlist_r.splice( instlist_r.end(), other_list );
790 } // for all sets computed
793 if ( instbackup_r.size() != instlist_r.size() )
795 ERR << "***************** Lost packages in InstallOrder sort." << endl;
797 instlist_r.splice( instlist_r.end(), nonpkglist );
801 /////////////////////////////////////////////////////////////////
802 } // namespace target
803 ///////////////////////////////////////////////////////////////////
804 /////////////////////////////////////////////////////////////////
806 ///////////////////////////////////////////////////////////////////