QueueItemUninstall.cc

Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
00002 /* QueueItemUninstall.cc
00003  *
00004  * Copyright (C) 2000-2002 Ximian, Inc.
00005  * Copyright (C) 2005 SUSE Linux Products GmbH
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License,
00009  * version 2, as published by the Free Software Foundation.
00010  *
00011  * This program is distributed in the hope that it will be useful, but
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019  * 02111-1307, USA.
00020  */
00021 
00022 #include "zypp/CapSet.h"
00023 #include "zypp/base/Logger.h"
00024 #include "zypp/base/String.h"
00025 #include "zypp/base/Gettext.h"
00026 
00027 #include "zypp/base/Algorithm.h"
00028 #include "zypp/ResPool.h"
00029 #include "zypp/ResFilters.h"
00030 #include "zypp/CapFilters.h"
00031 #include "zypp/CapFactory.h"
00032 #include "zypp/Patch.h"
00033 
00034 #include "zypp/solver/detail/QueueItemUninstall.h"
00035 #include "zypp/solver/detail/QueueItemEstablish.h"
00036 #include "zypp/solver/detail/QueueItemRequire.h"
00037 #include "zypp/solver/detail/QueueItem.h"
00038 #include "zypp/solver/detail/ResolverContext.h"
00039 #include "zypp/solver/detail/ResolverInfoMisc.h"
00040 #include "zypp/solver/detail/ResolverInfoMissingReq.h"
00041 
00043 namespace zypp
00044 { 
00045 
00046   namespace solver
00047   { 
00048 
00049     namespace detail
00050     { 
00051 
00052 using namespace std;
00053 
00054 IMPL_PTR_TYPE(QueueItemUninstall);
00055 
00056 //---------------------------------------------------------------------------
00057 
00058 std::ostream &
00059 QueueItemUninstall::dumpOn( std::ostream & os ) const
00060 {
00061     os << "[" << (_soft?"Soft":"") << "Uninstall: ";
00062 
00063     os << _item;
00064     os << " (";
00065     switch (_reason) {
00066         case QueueItemUninstall::CONFLICT:      os << "conflicts"; break;
00067         case QueueItemUninstall::OBSOLETE:      os << "obsoletes"; break;
00068         case QueueItemUninstall::UNSATISFIED:   os << "unsatisfied dependency"; break;
00069         case QueueItemUninstall::BACKOUT:       os << "uninstallable"; break;
00070         case QueueItemUninstall::UPGRADE:       os << "upgrade"; break;
00071         case QueueItemUninstall::DUPLICATE:     os << "duplicate"; break;
00072         case QueueItemUninstall::EXPLICIT:      os << "explicit"; break;
00073     }
00074     os << ")";
00075     if (_cap_leading_to_uninstall != Capability::noCap) {
00076         os << ", Triggered By ";
00077         os << _cap_leading_to_uninstall;
00078     }
00079     if (_upgraded_to) {
00080         os << ", Upgraded To ";
00081         os << _upgraded_to;
00082     }
00083     if (_explicitly_requested) os << ", Explicit";
00084     if (_remove_only) os << ", Remove Only";
00085     if (_due_to_conflict) os << ", Due To Conflict";
00086     if (_due_to_obsolete)
00087         os << ", Due To Obsolete:" << _obsoletes_item;
00088     if (_unlink) os << ", Unlink";
00089     os << "]";
00090     return os;
00091 }
00092 
00093 //---------------------------------------------------------------------------
00094 
00095 QueueItemUninstall::QueueItemUninstall (const ResPool & pool, PoolItem_Ref item, UninstallReason reason, bool soft)
00096     : QueueItem (QUEUE_ITEM_TYPE_UNINSTALL, pool)
00097     , _item (item)
00098     , _reason (reason)
00099     , _soft (soft)
00100     , _cap_leading_to_uninstall (Capability())
00101     , _upgraded_to (NULL)
00102     , _explicitly_requested (false)
00103     , _remove_only (false)
00104     , _due_to_conflict (false)
00105     , _due_to_obsolete (false)
00106     , _unlink (false)
00107     , _obsoletes_item (NULL)
00108 {
00109     _XDEBUG("QueueItemUninstall::QueueItemUninstall(" << item << ")");
00110 }
00111 
00112 
00113 QueueItemUninstall::~QueueItemUninstall()
00114 {
00115 }
00116 
00117 //---------------------------------------------------------------------------
00118 
00119 void
00120 QueueItemUninstall::setUnlink ()
00121 {
00122     _unlink = true;
00123     /* Reduce the priority so that unlink items will tend to get
00124        processed later.  We want to process unlinks as late as possible...
00125        this will make our "is this item in use" check more accurate. */
00126     setPriority (0);
00127 
00128     return;
00129 }
00130 
00131 //---------------------------------------------------------------------------
00132 
00133 struct UnlinkCheck
00134 {
00135     ResolverContext_Ptr context;
00136     bool cancel_unlink;
00137 
00138     // An item is to be uninstalled, it provides a capability
00139     //   which requirer needs (as match)
00140     //   if requirer is installed or to-be-installed:
00141     //     check if anyone else provides it to the requirer
00142     //     or if the uninstall breaks the requirer
00143     //       in this case, we have to cancel the uninstallation
00144 
00145     bool operator()( const CapAndItem & cai )
00146     {
00147         if (cancel_unlink)                              // already cancelled
00148             return true;
00149 
00150         if (! context->isPresent (cai.item))            // item is not (to-be-)installed
00151             return true;
00152 
00153         if (context->requirementIsMet (cai.cap))        // another resolvable provided match
00154             return true;
00155 
00156         cancel_unlink = true;                           // cancel, as this would break dependencies
00157 
00158         return true;
00159     }
00160 };
00161 
00162 //---------------------------------------------------------------------------
00163 
00164 
00165 struct UninstallProcess
00166 {
00167     ResPool pool;
00168     ResolverContext_Ptr context;
00169     PoolItem_Ref uninstalled_item;
00170     PoolItem_Ref upgraded_item;
00171     QueueItemList & qil;
00172     bool remove_only;
00173     bool soft;
00174 
00175     UninstallProcess (const ResPool & p, ResolverContext_Ptr ct, PoolItem_Ref u1, PoolItem_Ref u2, QueueItemList & l, bool ro, bool s)
00176         : pool (p)
00177         , context (ct)
00178         , uninstalled_item (u1)
00179         , upgraded_item (u2)
00180         , qil (l)
00181         , remove_only (ro)
00182         , soft (s)
00183     { }
00184 
00185     // the uninstall of uninstalled_item breaks the dependency 'match' of resolvable 'requirer'
00186 
00187     bool operator()( const CapAndItem & cai )
00188     {
00189         PoolItem requirer( cai.item );
00190         if (! context->isPresent (requirer))                            // its not installed -> dont care
00191             return true;
00192 
00193         if (context->requirementIsMet( cai.cap, false ))                // its provided by another installed resolvable -> dont care
00194             return true;
00195 
00196         if (context->getStatus(requirer).isSatisfied()) {               // it is just satisfied, check freshens and supplements
00197 #warning If an uninstall incompletes a satisfied, the uninstall should be cancelled
00198             QueueItemEstablish_Ptr establish_item = new QueueItemEstablish (pool, requirer, soft);      // re-check if its still needed
00199             qil.push_back (establish_item);
00200             return true;
00201         }
00202         QueueItemRequire_Ptr require_item = new QueueItemRequire( pool, cai.cap );      // issue a new require to fulfill this dependency
00203         require_item->addPoolItem (requirer);
00204         if (remove_only) {
00205             require_item->setRemoveOnly ();
00206         }
00207         require_item->setUpgradedPoolItem (upgraded_item);
00208         require_item->setLostPoolItem (uninstalled_item);                               // this is what we lost, dont re-install it
00209 
00210         qil.push_front (require_item);
00211 
00212         return true;
00213     }
00214 };
00215 
00216 
00217 // Handle items which freshen or supplement us -> re-establish them
00218 
00219 struct UninstallEstablishItem
00220 {
00221     const ResPool & pool;
00222     QueueItemList & qil;
00223     bool soft;
00224 
00225     UninstallEstablishItem (const ResPool & p, QueueItemList &l, bool s)
00226         : pool(p)
00227         , qil(l)
00228         , soft(s)
00229     { }
00230 
00231 
00232     // provider has a freshens on a just to-be-installed item
00233     //   re-establish provider, maybe its incomplete now
00234 
00235     bool operator()( const CapAndItem & cai )
00236     {
00237         _XDEBUG("QueueItemUninstall::UninstallEstablishItem (" << cai.item << ", " << cai.cap << ")");
00238 
00239         // re-establish only installed items which are not scheduled for removal yet.
00240 
00241         if (cai.item.status().staysInstalled()) {
00242             QueueItemEstablish_Ptr establish_item = new QueueItemEstablish (pool, cai.item, soft);
00243             qil.push_back (establish_item);
00244         }
00245         return true;
00246     }
00247 };
00248 
00249 // Handle installed items which provides a recommend -> remove it soft
00250 
00251 struct ProvidesItem
00252 {
00253     const ResPool & pool;
00254     QueueItemList & qil;
00255     bool soft;
00256 
00257     ProvidesItem (const ResPool & p, QueueItemList &l, bool s)
00258         : pool(p)
00259         , qil(l)
00260         , soft(s)
00261     { }
00262 
00263 
00264     bool operator()( const CapAndItem & cai )
00265     {
00266         _XDEBUG("remove soft item (" << cai.item << ", " << cai.cap << ")");
00267         PoolItem_Ref item( cai.item );
00268         if (!item.status().transacts()) {       // not scheduled for transaction yet
00269             QueueItemUninstall_Ptr uninstall_item = new QueueItemUninstall (pool, item, QueueItemUninstall::EXPLICIT, soft);
00270             uninstall_item->setUnlink ();
00271             qil.push_back (uninstall_item);
00272         }
00273         return true;
00274     }
00275 };
00276 
00277 
00278 // Uninstall atom of a to-be-uninstalled patch
00279 
00280 struct UninstallItem
00281 {
00282     ResPool pool;
00283     ResolverContext_Ptr context;
00284     QueueItemList & qil;
00285     bool soft;
00286 
00287     UninstallItem( const ResPool & p, ResolverContext_Ptr ct, QueueItemList & l, bool s )
00288         : pool( p )
00289         , context( ct )
00290         , qil( l )
00291         , soft( s )
00292     { }
00293 
00294     bool operator()( const CapAndItem & cai )
00295     {
00296         PoolItem item( cai.item );
00297 
00298         _XDEBUG( "UninstallItem (unlink) " << item );
00299         QueueItemUninstall_Ptr uninstall_item = new QueueItemUninstall( pool, item, QueueItemUninstall::EXPLICIT, soft );
00300         uninstall_item->setUnlink();
00301         qil.push_front( uninstall_item );
00302 
00303         return true;
00304     }
00305 };
00306 
00307 
00308 //-----------------------------------------------------------------------------
00309 
00310 
00311 bool
00312 QueueItemUninstall::process (ResolverContext_Ptr context, QueueItemList & qil)
00313 {
00314     ResStatus status = context->getStatus(_item);
00315 
00316     _XDEBUG("QueueItemUninstall::process(<" << status << ">" << _item << ( _unlink ? "[unlink]" : ""));
00317 
00318     /* In the case of an unlink, we only want to uninstall the item if it is
00319        being used by something else.  We can't really determine this with 100%
00320        accuracy, since some later queue item could cause something that requires
00321        the item to be uninstalled.  The alternative is to try to do something
00322        really clever... but I'm not clever enough to think of an algorithm that
00323          (1) Would do the right thing.
00324          (2) Is guaranteed to terminate. (!)
00325        so this will have to do.  In practice, I don't think that this is a serious
00326        problem. */
00327 
00328     if (_unlink) {
00329         /* If the item is to-be-installed, obviously it is being use! */
00330         if (status.isToBeInstalled()) {
00331             ResolverInfo_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_TO_BE_INSTALLED, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00332             context->addInfo (misc_info);
00333             goto finished;
00334 
00335         }
00336         else if (status.staysInstalled()) {
00337 
00338             UnlinkCheck info;
00339 
00340             /* Flag the item as to-be-uninstalled so that it won't
00341                satisfy any other item's deps during this check. */
00342 
00343             context->setStatus(_item, ResStatus::toBeUninstalled);
00344 
00345             info.context = context;
00346             info.cancel_unlink = false;
00347 
00348             // look at the provides of the to-be-uninstalled resolvable and
00349             //   check if anyone (installed) needs it
00350 
00351             CapSet provides = _item->dep(Dep::PROVIDES);
00352             for (CapSet::const_iterator iter = provides.begin(); iter != provides.end() && ! info.cancel_unlink; iter++) {
00353 
00354                 //world()->foreachRequiringPoolItem (*iter, unlink_check_cb, &info);
00355 
00356                 Dep dep( Dep::REQUIRES);
00357 
00358                 invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00359                               pool().byCapabilityIndexEnd( iter->index(), dep ),
00360                               resfilter::ByCapMatch( *iter ),
00361                               functor::functorRef<bool,CapAndItem>(info) );
00362 
00363             }
00364 
00365             /* Set the status back to normal. */
00366 
00367             context->setStatus(_item, status);
00368 
00369             if (info.cancel_unlink) {
00370                 ResolverInfo_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_INSTALLED, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00371                 context->addInfo (misc_info);
00372                 goto finished;
00373             }
00374         }
00375 
00376     }
00377     
00378     this->logInfo (context);
00379     
00380     context->uninstall (_item, _upgraded_to /*bool*/, _due_to_obsolete, _unlink);
00381     if (status.staysInstalled()) {
00382         if (! _explicitly_requested
00383             && _item.status().isLocked()) {
00384 
00385             ResolverInfoMisc_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_LOCKED,
00386                                                                    _item, RESOLVER_INFO_PRIORITY_VERBOSE,
00387                                                                    _cap_leading_to_uninstall);
00388             if (_due_to_obsolete)
00389             {
00390                 misc_info->setOtherPoolItem (_obsoletes_item);
00391                 misc_info->addTrigger (ResolverInfoMisc::OBSOLETE);
00392             } else if (_due_to_conflict)
00393             {
00394                 misc_info->addTrigger (ResolverInfoMisc::CONFLICT);             
00395             }
00396             
00397             context->addError (misc_info);
00398             goto finished;
00399         }
00400 
00401         if (_cap_leading_to_uninstall != Capability()           // non-empty _cap_leading_to_uninstall
00402             && !_due_to_conflict
00403             && !_due_to_obsolete)
00404         {
00405             ResolverInfo_Ptr info = new ResolverInfoMissingReq (_item, _cap_leading_to_uninstall);
00406             context->addInfo (info);
00407         }
00408 
00409         // we're uninstalling an installed item
00410         //   loop over all its provides and check if any installed item requires
00411         //   one of these provides
00412         CapSet provides = _item->dep(Dep::PROVIDES);
00413 
00414         for (CapSet::const_iterator iter = provides.begin(); iter != provides.end(); iter++) {
00415             UninstallProcess info ( pool(), context, _item, _upgraded_to, qil, _remove_only, _soft);
00416 
00417             //world()->foreachRequiringPoolItem (*iter, uninstall_process_cb, &info);
00418             Dep dep( Dep::REQUIRES );
00419 
00420             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00421                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00422                           resfilter::ByCapMatch( *iter ),
00423                           functor::functorRef<bool,CapAndItem>(info) );
00424 
00425             // re-establish all which supplement or freshen a provides of the just uninstalled item
00426 
00427             UninstallEstablishItem establish( pool(), qil, _soft );
00428 
00429             dep = Dep::SUPPLEMENTS;
00430             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00431                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00432                           resfilter::ByCapMatch( *iter ),
00433                           functor::functorRef<bool,CapAndItem>( establish ) );
00434 
00435             dep = Dep::FRESHENS;
00436             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00437                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00438                           resfilter::ByCapMatch( *iter ),
00439                           functor::functorRef<bool,CapAndItem>( establish ) );
00440         }
00441 
00442         // if its a patch, uninstall all its atoms
00443 
00444         if (_item->kind() == ResTraits<Patch>::kind) {
00445             Patch::constPtr patch = asKind<Patch>( _item );
00446             Patch::AtomList atoms = patch->atoms();
00447             UninstallItem callback( pool(), context, qil, _soft );
00448             CapFactory factory;
00449             Dep dep(Dep::PROVIDES);
00450 
00451             // loop over atom, find matching installed PoolItem and schedule this for removal
00452 
00453             for (Patch::AtomList::const_iterator it = atoms.begin(); it != atoms.end(); ++it) {
00454                 Resolvable::constPtr res = *it;
00455                 Capability capAtom =  factory.parse ( res->kind(), res->name(), Rel::EQ, res->edition());
00456                 invokeOnEach( pool().byCapabilityIndexBegin( capAtom.index(), dep ),
00457                               pool().byCapabilityIndexEnd( capAtom.index(), dep ),
00458                               functor::chain( resfilter::ByCaIInstalled(), resfilter::ByCapMatch( capAtom ) ),
00459                               functor::functorRef<bool,CapAndItem>( callback ) );
00460             }
00461         }
00462 
00463         // soft-remove the installed items which have been recommended by the to-be-uninstalled
00464         // but not when upgrade, and not for packages
00465 
00466         if (_upgraded_to                                // its an upgrade
00467             || _item->kind() == ResTraits<Package>::kind)
00468         {
00469             goto finished;
00470         }
00471 
00472         CapSet recomments = _item->dep (Dep::RECOMMENDS);
00473         for (CapSet::const_iterator iter = recomments.begin(); iter != recomments.end(); iter++) {
00474             const Capability cap = *iter;
00475             _XDEBUG("this recommends " << cap);
00476             ProvidesItem provides( pool(), qil, true ); // soft
00477 
00478             Dep dep(Dep::PROVIDES);
00479             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00480                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00481                           functor::chain( resfilter::ByCaIInstalled(), resfilter::ByCapMatch( *iter ) ),
00482                           functor::functorRef<bool,CapAndItem>( provides ) );
00483         }
00484 
00485     }
00486 
00487  finished:
00488     return true;
00489 }
00490 
00491 //---------------------------------------------------------------------------
00492 
00493 int
00494 QueueItemUninstall::cmp (QueueItem_constPtr item) const
00495 {
00496     int cmp = this->compare (item);             // assures equal type
00497     if (cmp != 0)
00498         return cmp;
00499 
00500     QueueItemUninstall_constPtr uninstall = dynamic_pointer_cast<const QueueItemUninstall>(item);
00501     return compareByNVRA (_item.resolvable(), uninstall->_item.resolvable());
00502 }
00503 
00504 
00505 QueueItem_Ptr
00506 QueueItemUninstall::copy (void) const
00507 {
00508     QueueItemUninstall_Ptr new_uninstall = new QueueItemUninstall (pool(), _item, _reason);
00509     new_uninstall->QueueItem::copy(this);
00510 
00511 
00512     new_uninstall->_item                      = _item;
00513     new_uninstall->_cap_leading_to_uninstall  = _cap_leading_to_uninstall;
00514     new_uninstall->_upgraded_to               = _upgraded_to;
00515 
00516     new_uninstall->_explicitly_requested      = _explicitly_requested;
00517     new_uninstall->_remove_only               = _remove_only;
00518     new_uninstall->_due_to_conflict           = _due_to_conflict;
00519     new_uninstall->_due_to_obsolete           = _due_to_obsolete;
00520     new_uninstall->_obsoletes_item            = _obsoletes_item;
00521     new_uninstall->_unlink                    = _unlink;
00522 
00523     return new_uninstall;
00524 }
00525 
00527     };// namespace detail
00530   };// namespace solver
00533 };// namespace zypp
00535 

Generated on Thu Sep 14 15:38:43 2006 for zypp by  doxygen 1.4.6