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 ))               // 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             && item.status().maySetToBeUninstalledSoft()) // checking the permission to delete it (Bug 217574)      
00270         {
00271             QueueItemUninstall_Ptr uninstall_item = new QueueItemUninstall (pool, item, QueueItemUninstall::EXPLICIT, soft);
00272             uninstall_item->setUnlink ();
00273             qil.push_back (uninstall_item);
00274         } else {
00275             _XDEBUG(" ---> do not remove cause it has been set for transaction or can not set for uninstallation due right problems.");     
00276         }
00277         return true;
00278     }
00279 };
00280 
00281 
00282 // Uninstall atom of a to-be-uninstalled patch
00283 
00284 struct UninstallItem
00285 {
00286     ResPool pool;
00287     ResolverContext_Ptr context;
00288     QueueItemList & qil;
00289     bool soft;
00290 
00291     UninstallItem( const ResPool & p, ResolverContext_Ptr ct, QueueItemList & l, bool s )
00292         : pool( p )
00293         , context( ct )
00294         , qil( l )
00295         , soft( s )
00296     { }
00297 
00298     bool operator()( const CapAndItem & cai )
00299     {
00300         PoolItem item( cai.item );
00301 
00302         _XDEBUG( "UninstallItem (unlink) " << item );
00303         QueueItemUninstall_Ptr uninstall_item = new QueueItemUninstall( pool, item, QueueItemUninstall::EXPLICIT, soft );
00304         uninstall_item->setUnlink();
00305         qil.push_front( uninstall_item );
00306 
00307         return true;
00308     }
00309 };
00310 
00311 
00312 //-----------------------------------------------------------------------------
00313 
00314 
00315 bool
00316 QueueItemUninstall::process (ResolverContext_Ptr context, QueueItemList & qil)
00317 {
00318     ResStatus status = context->getStatus(_item);
00319 
00320     _XDEBUG("QueueItemUninstall::process(<" << status << ">" << _item << ( _unlink ? "[unlink]" : ""));
00321 
00322     /* In the case of an unlink, we only want to uninstall the item if it is
00323        being used by something else.  We can't really determine this with 100%
00324        accuracy, since some later queue item could cause something that requires
00325        the item to be uninstalled.  The alternative is to try to do something
00326        really clever... but I'm not clever enough to think of an algorithm that
00327          (1) Would do the right thing.
00328          (2) Is guaranteed to terminate. (!)
00329        so this will have to do.  In practice, I don't think that this is a serious
00330        problem. */
00331 
00332     if (_unlink) {
00333         /* If the item is to-be-installed, obviously it is being use! */
00334         if (status.isToBeInstalled()) {
00335             ResolverInfo_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_TO_BE_INSTALLED, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00336             context->addInfo (misc_info);
00337             goto finished;
00338 
00339         }
00340         else if (status.staysInstalled()) {
00341 
00342             UnlinkCheck info;
00343 
00344             /* Flag the item as to-be-uninstalled so that it won't
00345                satisfy any other item's deps during this check. */
00346 
00347             context->setStatus(_item, ResStatus::toBeUninstalled);
00348 
00349             info.context = context;
00350             info.cancel_unlink = false;
00351 
00352             // look at the provides of the to-be-uninstalled resolvable and
00353             //   check if anyone (installed) needs it
00354 
00355             CapSet provides = _item->dep(Dep::PROVIDES);
00356             for (CapSet::const_iterator iter = provides.begin(); iter != provides.end() && ! info.cancel_unlink; iter++) {
00357 
00358                 //world()->foreachRequiringPoolItem (*iter, unlink_check_cb, &info);
00359 
00360                 Dep dep( Dep::REQUIRES);
00361 
00362                 invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00363                               pool().byCapabilityIndexEnd( iter->index(), dep ),
00364                               resfilter::ByCapMatch( *iter ),
00365                               functor::functorRef<bool,CapAndItem>(info) );
00366 
00367             }
00368 
00369             /* Set the status back to normal. */
00370 
00371             context->setStatus(_item, status);
00372 
00373             if (info.cancel_unlink) {
00374                 ResolverInfo_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_INSTALLED, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00375                 context->addInfo (misc_info);
00376                 goto finished;
00377             }
00378         }
00379 
00380     }
00381     
00382     this->logInfo (context);
00383     
00384     context->uninstall (_item, _upgraded_to /*bool*/, _due_to_obsolete, _unlink);
00385     if (status.staysInstalled()) {
00386         if (! _explicitly_requested
00387             && _item.status().isLocked()) {
00388 
00389             ResolverInfoMisc_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_LOCKED,
00390                                                                    _item, RESOLVER_INFO_PRIORITY_VERBOSE,
00391                                                                    _cap_leading_to_uninstall);
00392             if (_due_to_obsolete)
00393             {
00394                 misc_info->setOtherPoolItem (_obsoletes_item);
00395                 misc_info->addTrigger (ResolverInfoMisc::OBSOLETE);
00396             } else if (_due_to_conflict)
00397             {
00398                 misc_info->addTrigger (ResolverInfoMisc::CONFLICT);             
00399             }
00400             
00401             context->addError (misc_info);
00402             goto finished;
00403         }
00404 
00405         if (_cap_leading_to_uninstall != Capability()           // non-empty _cap_leading_to_uninstall
00406             && !_due_to_conflict
00407             && !_due_to_obsolete)
00408         {
00409             ResolverInfo_Ptr info = new ResolverInfoMissingReq (_item, _cap_leading_to_uninstall);
00410             context->addInfo (info);
00411         }
00412 
00413         // we're uninstalling an installed item
00414         //   loop over all its provides and check if any installed item requires
00415         //   one of these provides
00416         CapSet provides = _item->dep(Dep::PROVIDES);
00417 
00418         for (CapSet::const_iterator iter = provides.begin(); iter != provides.end(); iter++) {
00419             UninstallProcess info ( pool(), context, _item, _upgraded_to, qil, _remove_only, _soft);
00420 
00421             //world()->foreachRequiringPoolItem (*iter, uninstall_process_cb, &info);
00422             Dep dep( Dep::REQUIRES );
00423 
00424             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00425                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00426                           resfilter::ByCapMatch( *iter ),
00427                           functor::functorRef<bool,CapAndItem>(info) );
00428 
00429             // re-establish all which supplement or freshen a provides of the just uninstalled item
00430 
00431             UninstallEstablishItem establish( pool(), qil, _soft );
00432 
00433             dep = Dep::SUPPLEMENTS;
00434             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00435                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00436                           resfilter::ByCapMatch( *iter ),
00437                           functor::functorRef<bool,CapAndItem>( establish ) );
00438 
00439             dep = Dep::FRESHENS;
00440             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00441                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00442                           resfilter::ByCapMatch( *iter ),
00443                           functor::functorRef<bool,CapAndItem>( establish ) );
00444         }
00445 
00446         // if its a patch, uninstall all its atoms
00447 
00448         if (_item->kind() == ResTraits<Patch>::kind) {
00449             Patch::constPtr patch = asKind<Patch>( _item );
00450             Patch::AtomList atoms = patch->atoms();
00451             UninstallItem callback( pool(), context, qil, _soft );
00452             CapFactory factory;
00453             Dep dep(Dep::PROVIDES);
00454 
00455             // loop over atom, find matching installed PoolItem and schedule this for removal
00456 
00457             for (Patch::AtomList::const_iterator it = atoms.begin(); it != atoms.end(); ++it) {
00458                 Resolvable::constPtr res = *it;
00459                 Capability capAtom =  factory.parse ( res->kind(), res->name(), Rel::EQ, res->edition());
00460                 invokeOnEach( pool().byCapabilityIndexBegin( capAtom.index(), dep ),
00461                               pool().byCapabilityIndexEnd( capAtom.index(), dep ),
00462                               functor::chain( resfilter::ByCaIInstalled(), resfilter::ByCapMatch( capAtom ) ),
00463                               functor::functorRef<bool,CapAndItem>( callback ) );
00464             }
00465         }
00466 
00467         // soft-remove the installed items which have been recommended by the to-be-uninstalled
00468         // but not when upgrade, and not for packages
00469 
00470         if (_upgraded_to                                // its an upgrade
00471             || _item->kind() == ResTraits<Package>::kind)
00472         {
00473             goto finished;
00474         }
00475 
00476         CapSet recomments = _item->dep (Dep::RECOMMENDS);
00477         for (CapSet::const_iterator iter = recomments.begin(); iter != recomments.end(); iter++) {
00478             const Capability cap = *iter;
00479             _XDEBUG("this recommends " << cap);
00480             ProvidesItem provides( pool(), qil, true ); // soft     
00481 
00482             Dep dep(Dep::PROVIDES);
00483             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00484                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00485                           functor::chain( resfilter::ByCaIInstalled(), resfilter::ByCapMatch( *iter ) ),
00486                           functor::functorRef<bool,CapAndItem>( provides ) );
00487         }
00488 
00489     }
00490 
00491  finished:
00492     return true;
00493 }
00494 
00495 //---------------------------------------------------------------------------
00496 
00497 int
00498 QueueItemUninstall::cmp (QueueItem_constPtr item) const
00499 {
00500     int cmp = this->compare (item);             // assures equal type
00501     if (cmp != 0)
00502         return cmp;
00503 
00504     QueueItemUninstall_constPtr uninstall = dynamic_pointer_cast<const QueueItemUninstall>(item);
00505     return compareByNVRA (_item.resolvable(), uninstall->_item.resolvable());
00506 }
00507 
00508 
00509 QueueItem_Ptr
00510 QueueItemUninstall::copy (void) const
00511 {
00512     QueueItemUninstall_Ptr new_uninstall = new QueueItemUninstall (pool(), _item, _reason);
00513     new_uninstall->QueueItem::copy(this);
00514 
00515 
00516     new_uninstall->_item                      = _item;
00517     new_uninstall->_cap_leading_to_uninstall  = _cap_leading_to_uninstall;
00518     new_uninstall->_upgraded_to               = _upgraded_to;
00519 
00520     new_uninstall->_explicitly_requested      = _explicitly_requested;
00521     new_uninstall->_remove_only               = _remove_only;
00522     new_uninstall->_due_to_conflict           = _due_to_conflict;
00523     new_uninstall->_due_to_obsolete           = _due_to_obsolete;
00524     new_uninstall->_obsoletes_item            = _obsoletes_item;
00525     new_uninstall->_unlink                    = _unlink;
00526 
00527     return new_uninstall;
00528 }
00529 
00531     };// namespace detail
00534   };// namespace solver
00537 };// namespace zypp
00539 

Generated on Fri Jul 4 16:57:58 2008 for zypp by  doxygen 1.5.0