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

Generated on Thu Apr 24 02:24:53 2008 for zypp by  doxygen 1.4.6