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

Generated on Thu Jul 6 00:07:23 2006 for zypp by  doxygen 1.4.6