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 (1); // Should be one more then Uninstall due dependency errors (Conflict/Unfulfilled)
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         PoolItem provider( cai.item );  
00148         if (cancel_unlink)                              // already cancelled
00149             return true;
00150 
00151         if (! context->isPresent (cai.item))            // item is not (to-be-)installed
00152             return true;
00153 
00154         if (context->requirementIsMet (cai.cap, provider, Dep::REQUIRES))       // another resolvable provided match
00155             return true;
00156 
00157         cancel_unlink = true;                           // cancel, as this would break dependencies
00158 
00159         return true;
00160     }
00161 };
00162 
00163 //---------------------------------------------------------------------------
00164 
00165 
00166 struct UninstallProcess
00167 {
00168     ResPool pool;
00169     ResolverContext_Ptr context;
00170     PoolItem_Ref uninstalled_item;
00171     PoolItem_Ref upgraded_item;
00172     QueueItemList & qil;
00173     bool remove_only;
00174     bool soft;
00175 
00176     UninstallProcess (const ResPool & p, ResolverContext_Ptr ct, PoolItem_Ref u1, PoolItem_Ref u2, QueueItemList & l, bool ro, bool s)
00177         : pool (p)
00178         , context (ct)
00179         , uninstalled_item (u1)
00180         , upgraded_item (u2)
00181         , qil (l)
00182         , remove_only (ro)
00183         , soft (s)
00184     { }
00185 
00186     // the uninstall of uninstalled_item breaks the dependency 'match' of resolvable 'requirer'
00187 
00188     bool operator()( const CapAndItem & cai )
00189     {
00190         PoolItem requirer( cai.item );
00191         if (! context->isPresent (requirer))                            // its not installed -> dont care
00192             return true;
00193 
00194         if (context->requirementIsMet( cai.cap, requirer, Dep::REQUIRES ))              // its provided by another installed resolvable -> dont care
00195             return true;
00196 
00197         if (context->getStatus(requirer).isSatisfied()) {               // it is just satisfied, check freshens and supplements
00198 #warning If an uninstall incompletes a satisfied, the uninstall should be cancelled
00199             QueueItemEstablish_Ptr establish_item = new QueueItemEstablish (pool, requirer, soft);      // re-check if its still needed
00200             qil.push_back (establish_item);
00201             return true;
00202         }
00203         QueueItemRequire_Ptr require_item = new QueueItemRequire( pool, cai.cap );      // issue a new require to fulfill this dependency
00204         require_item->addPoolItem (requirer);
00205         if (remove_only) {
00206             require_item->setRemoveOnly ();
00207         }
00208         require_item->setUpgradedPoolItem (upgraded_item);
00209         require_item->setLostPoolItem (uninstalled_item);                               // this is what we lost, dont re-install it
00210 
00211         qil.push_front (require_item);
00212 
00213         return true;
00214     }
00215 };
00216 
00217 
00218 // Handle items which freshen or supplement us -> re-establish them
00219 
00220 struct UninstallEstablishItem
00221 {
00222     const ResPool & pool;
00223     QueueItemList & qil;
00224     bool soft;
00225 
00226     UninstallEstablishItem (const ResPool & p, QueueItemList &l, bool s)
00227         : pool(p)
00228         , qil(l)
00229         , soft(s)
00230     { }
00231 
00232 
00233     // provider has a freshens on a just to-be-installed item
00234     //   re-establish provider, maybe its incomplete now
00235 
00236     bool operator()( const CapAndItem & cai )
00237     {
00238         _XDEBUG("QueueItemUninstall::UninstallEstablishItem (" << cai.item << ", " << cai.cap << ")");
00239 
00240         // re-establish only installed items which are not scheduled for removal yet.
00241 
00242         if (cai.item.status().staysInstalled()) {
00243             QueueItemEstablish_Ptr establish_item = new QueueItemEstablish (pool, cai.item, soft);
00244             qil.push_back (establish_item);
00245         }
00246         return true;
00247     }
00248 };
00249 
00250 // Handle installed items which provides a recommend -> remove it soft
00251 
00252 struct ProvidesItem
00253 {
00254     const ResPool & pool;
00255     QueueItemList & qil;
00256     bool soft;
00257 
00258     ProvidesItem (const ResPool & p, QueueItemList &l, bool s)    
00259         : pool(p)
00260         , qil(l)
00261         , soft(s)
00262     { }
00263 
00264 
00265     bool operator()( const CapAndItem & cai )
00266     {
00267         _XDEBUG("remove soft item (" << cai.item << ", " << cai.cap << ")");
00268         PoolItem_Ref item( cai.item );
00269         if (!item.status().transacts() // not scheduled for transaction yet
00270             && item.status().maySetToBeUninstalledSoft()) // checking the permission to delete it (Bug 217574)      
00271         {
00272             QueueItemUninstall_Ptr uninstall_item = new QueueItemUninstall (pool, item, QueueItemUninstall::EXPLICIT, soft);
00273             uninstall_item->setUnlink ();
00274             qil.push_back (uninstall_item);
00275         } else {
00276             _XDEBUG(" ---> do not remove cause it has been set for transaction or can not set for uninstallation due right problems.");     
00277         }
00278         return true;
00279     }
00280 };
00281 
00282 
00283 // Uninstall atom of a to-be-uninstalled patch
00284 
00285 struct UninstallItem
00286 {
00287     ResPool pool;
00288     ResolverContext_Ptr context;
00289     QueueItemList & qil;
00290     bool soft;
00291 
00292     UninstallItem( const ResPool & p, ResolverContext_Ptr ct, QueueItemList & l, bool s )
00293         : pool( p )
00294         , context( ct )
00295         , qil( l )
00296         , soft( s )
00297     { }
00298 
00299     bool operator()( const CapAndItem & cai )
00300     {
00301         PoolItem item( cai.item );
00302 
00303         _XDEBUG( "UninstallItem (unlink) " << item );
00304         QueueItemUninstall_Ptr uninstall_item = new QueueItemUninstall( pool, item, QueueItemUninstall::EXPLICIT, soft );
00305         uninstall_item->setUnlink();
00306         qil.push_front( uninstall_item );
00307 
00308         return true;
00309     }
00310 };
00311 
00312 
00313 //-----------------------------------------------------------------------------
00314 
00315 
00316 bool
00317 QueueItemUninstall::process (const QueueItemList & mainQueue, ResolverContext_Ptr context, QueueItemList & qil)
00318 {
00319     ResStatus status = context->getStatus(_item);
00320 
00321     _XDEBUG("QueueItemUninstall::process(<" << status << ">" << _item << ( _unlink ? "[unlink]" : ""));
00322 
00323     /* In the case of an unlink, we only want to uninstall the item if it is
00324        being used by something else.  We can't really determine this with 100%
00325        accuracy, since some later queue item could cause something that requires
00326        the item to be uninstalled.  The alternative is to try to do something
00327        really clever... but I'm not clever enough to think of an algorithm that
00328          (1) Would do the right thing.
00329          (2) Is guaranteed to terminate. (!)
00330        so this will have to do.  In practice, I don't think that this is a serious
00331        problem. */
00332 
00333     if (_unlink) {
00334         /* If the item is to-be-installed, obviously it is being use! */
00335         if (status.isToBeInstalled()) {
00336             ResolverInfo_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_TO_BE_INSTALLED, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00337             context->addInfo (misc_info);
00338             goto finished;
00339 
00340         }
00341         else if (status.staysInstalled()) {
00342 
00343             UnlinkCheck info;
00344 
00345             /* Flag the item as to-be-uninstalled so that it won't
00346                satisfy any other item's deps during this check. */
00347 
00348             context->setStatus(_item, ResStatus::toBeUninstalled);
00349 
00350             info.context = context;
00351             info.cancel_unlink = false;
00352 
00353             // look at the provides of the to-be-uninstalled resolvable and
00354             //   check if anyone (installed) needs it
00355 
00356             CapSet provides = _item->dep(Dep::PROVIDES);
00357             for (CapSet::const_iterator iter = provides.begin(); iter != provides.end() && ! info.cancel_unlink; iter++) {
00358 
00359                 //world()->foreachRequiringPoolItem (*iter, unlink_check_cb, &info);
00360 
00361                 Dep dep( Dep::REQUIRES);
00362 
00363                 invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00364                               pool().byCapabilityIndexEnd( iter->index(), dep ),
00365                               resfilter::ByCapMatch( *iter ),
00366                               functor::functorRef<bool,CapAndItem>(info) );
00367 
00368             }
00369 
00370             /* Set the status back to normal. */
00371 
00372             context->setStatus(_item, status);
00373 
00374             if (info.cancel_unlink) {
00375                 ResolverInfo_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_INSTALLED, _item, RESOLVER_INFO_PRIORITY_VERBOSE);
00376                 context->addInfo (misc_info);
00377                 goto finished;
00378             }
00379         }
00380 
00381     }
00382     
00383     this->logInfo (context);
00384     
00385     context->uninstall (_item, _upgraded_to , _due_to_obsolete, _unlink, _explicitly_requested);
00386     if (status.staysInstalled()) {
00387         if (! _explicitly_requested
00388             && _item.status().isLocked()) {
00389 
00390             ResolverInfoMisc_Ptr misc_info = new ResolverInfoMisc (RESOLVER_INFO_TYPE_UNINSTALL_LOCKED,
00391                                                                    _item, RESOLVER_INFO_PRIORITY_VERBOSE,
00392                                                                    _cap_leading_to_uninstall);
00393             if (_due_to_obsolete)
00394             {
00395                 misc_info->setOtherPoolItem (_obsoletes_item);
00396                 misc_info->addTrigger (ResolverInfoMisc::OBSOLETE);
00397             } else if (_due_to_conflict)
00398             {
00399                 misc_info->addTrigger (ResolverInfoMisc::CONFLICT);             
00400             }
00401             
00402             context->addError (misc_info);
00403             goto finished;
00404         }
00405 
00406         if (_cap_leading_to_uninstall != Capability()           // non-empty _cap_leading_to_uninstall
00407             && !_due_to_conflict
00408             && !_due_to_obsolete)
00409         {
00410             ResolverInfo_Ptr info = new ResolverInfoMissingReq (_item, _cap_leading_to_uninstall);
00411             context->addInfo (info);
00412         }
00413 
00414         // we're uninstalling an installed item
00415         //   loop over all its provides and check if any installed item requires
00416         //   one of these provides
00417         CapSet provides = _item->dep(Dep::PROVIDES);
00418 
00419         for (CapSet::const_iterator iter = provides.begin(); iter != provides.end(); iter++) {
00420             UninstallProcess info ( pool(), context, _item, _upgraded_to, qil, _remove_only, _soft);
00421 
00422             //world()->foreachRequiringPoolItem (*iter, uninstall_process_cb, &info);
00423             Dep dep( Dep::REQUIRES );
00424 
00425             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00426                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00427                           resfilter::ByCapMatch( *iter ),
00428                           functor::functorRef<bool,CapAndItem>(info) );
00429 
00430             // re-establish all which supplement or freshen a provides of the just uninstalled item
00431 
00432             UninstallEstablishItem establish( pool(), qil, _soft );
00433 
00434             dep = Dep::SUPPLEMENTS;
00435             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00436                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00437                           resfilter::ByCapMatch( *iter ),
00438                           functor::functorRef<bool,CapAndItem>( establish ) );
00439 
00440             dep = Dep::FRESHENS;
00441             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00442                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00443                           resfilter::ByCapMatch( *iter ),
00444                           functor::functorRef<bool,CapAndItem>( establish ) );
00445         }
00446 
00447         // if its a patch, uninstall all its atoms
00448 
00449         if (_item->kind() == ResTraits<Patch>::kind) {
00450             Patch::constPtr patch = asKind<Patch>( _item );
00451             Patch::AtomList atoms = patch->atoms();
00452             UninstallItem callback( pool(), context, qil, _soft );
00453             CapFactory factory;
00454             Dep dep(Dep::PROVIDES);
00455 
00456             // loop over atom, find matching installed PoolItem and schedule this for removal
00457 
00458             for (Patch::AtomList::const_iterator it = atoms.begin(); it != atoms.end(); ++it) {
00459                 Resolvable::constPtr res = *it;
00460                 Capability capAtom =  factory.parse ( res->kind(), res->name(), Rel::EQ, res->edition());
00461                 invokeOnEach( pool().byCapabilityIndexBegin( capAtom.index(), dep ),
00462                               pool().byCapabilityIndexEnd( capAtom.index(), dep ),
00463                               functor::chain( resfilter::ByCaIInstalled(), resfilter::ByCapMatch( capAtom ) ),
00464                               functor::functorRef<bool,CapAndItem>( callback ) );
00465             }
00466         }
00467 
00468         // soft-remove the installed items which have been recommended by the to-be-uninstalled
00469         // but not when upgrade, and not for packages
00470 
00471         if (_upgraded_to                                // its an upgrade
00472             || _item->kind() == ResTraits<Package>::kind)
00473         {
00474             goto finished;
00475         }
00476 
00477         CapSet recomments = _item->dep (Dep::RECOMMENDS);
00478         for (CapSet::const_iterator iter = recomments.begin(); iter != recomments.end(); iter++) {
00479             const Capability cap = *iter;
00480             _XDEBUG("this recommends " << cap);
00481             ProvidesItem provides( pool(), qil, true ); // soft     
00482 
00483             Dep dep(Dep::PROVIDES);
00484             invokeOnEach( pool().byCapabilityIndexBegin( iter->index(), dep ),
00485                           pool().byCapabilityIndexEnd( iter->index(), dep ),
00486                           functor::chain( resfilter::ByCaIInstalled(), resfilter::ByCapMatch( *iter ) ),
00487                           functor::functorRef<bool,CapAndItem>( provides ) );
00488         }
00489 
00490     }
00491 
00492  finished:
00493     return true;
00494 }
00495 
00496 //---------------------------------------------------------------------------
00497 
00498 int
00499 QueueItemUninstall::cmp (QueueItem_constPtr item) const
00500 {
00501     int cmp = this->compare (item);             // assures equal type
00502     if (cmp != 0)
00503         return cmp;
00504 
00505     QueueItemUninstall_constPtr uninstall = dynamic_pointer_cast<const QueueItemUninstall>(item);
00506     return compareByNVRA (_item.resolvable(), uninstall->_item.resolvable());
00507 }
00508 
00509 
00510 QueueItem_Ptr
00511 QueueItemUninstall::copy (void) const
00512 {
00513     QueueItemUninstall_Ptr new_uninstall = new QueueItemUninstall (pool(), _item, _reason);
00514     new_uninstall->QueueItem::copy(this);
00515 
00516 
00517     new_uninstall->_item                      = _item;
00518     new_uninstall->_cap_leading_to_uninstall  = _cap_leading_to_uninstall;
00519     new_uninstall->_upgraded_to               = _upgraded_to;
00520 
00521     new_uninstall->_explicitly_requested      = _explicitly_requested;
00522     new_uninstall->_remove_only               = _remove_only;
00523     new_uninstall->_due_to_conflict           = _due_to_conflict;
00524     new_uninstall->_due_to_obsolete           = _due_to_obsolete;
00525     new_uninstall->_obsoletes_item            = _obsoletes_item;
00526     new_uninstall->_unlink                    = _unlink;
00527 
00528     return new_uninstall;
00529 }
00530 
00532     };// namespace detail
00535   };// namespace solver
00538 };// namespace zypp
00540 

Generated on Tue Sep 25 19:23:07 2007 for libzypp by  doxygen 1.5.3