95aa127bc93034673925f82f6278f71c9b46c55c
[platform/upstream/libzypp.git] / zypp / target / TargetImpl.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/target/TargetImpl.cc
10  *
11 */
12 #include <iostream>
13 #include <string>
14 #include <list>
15 #include <set>
16
17 #include "zypp/base/Logger.h"
18 #include "zypp/base/Exception.h"
19 #include "zypp/base/Gettext.h"
20 #include "zypp/PoolItem.h"
21 #include "zypp/Resolvable.h"
22 #include "zypp/ResObject.h"
23 #include "zypp/Package.h"
24
25 #include "zypp/target/TargetImpl.h"
26 #include "zypp/target/TargetCallbackReceiver.h"
27
28 #include "zypp/solver/detail/InstallOrder.h"
29
30 using namespace std;
31 using zypp::solver::detail::InstallOrder;
32
33 ///////////////////////////////////////////////////////////////////
34 namespace zypp
35 { /////////////////////////////////////////////////////////////////
36   ///////////////////////////////////////////////////////////////////
37   namespace target
38   { /////////////////////////////////////////////////////////////////
39
40     IMPL_PTR_TYPE(TargetImpl);
41
42     TargetImpl_Ptr TargetImpl::_nullimpl;
43
44     /** Null implementation */
45     TargetImpl_Ptr TargetImpl::nullimpl()
46     {
47       if (_nullimpl == 0)
48         _nullimpl = new TargetImpl;
49       return _nullimpl;
50     }
51
52
53     ///////////////////////////////////////////////////////////////////
54     //
55     //  METHOD NAME : TargetImpl::TargetImpl
56     //  METHOD TYPE : Ctor
57     //
58     TargetImpl::TargetImpl(const Pathname & root_r)
59     : _root(root_r)
60     {
61       _rpm.initDatabase(_root);
62       _storage_enabled = false;
63       MIL << "Initialized target on " << _root << endl;
64     }
65
66     ///////////////////////////////////////////////////////////////////
67     //
68     //  METHOD NAME : TargetImpl::~TargetImpl
69     //  METHOD TYPE : Dtor
70     //
71     TargetImpl::~TargetImpl()
72     {
73       _rpm.closeDatabase();
74       MIL << "Targets closed" << endl;
75     }
76
77     bool TargetImpl::isStorageEnabled() const
78     {
79       return _storage_enabled;
80     }
81
82     
83     void TargetImpl::enableStorage(const Pathname &root_r)
84     {
85       _storage.init(root_r);
86       _storage_enabled = true;
87     }
88
89     Pathname TargetImpl::root() const
90     {
91       return _root;
92     }
93   
94     const ResStore & TargetImpl::resolvables()
95     {
96       _store.clear();
97       // RPM objects
98       std::list<Package::Ptr> packages = _rpm.getPackages();
99       for (std::list<Package::Ptr>::const_iterator it = packages.begin();
100            it != packages.end();
101            it++)
102       {
103         _store.insert(*it);
104       }
105
106       if ( isStorageEnabled() )
107       {
108         // resolvables stored in the zypp storage database
109         std::list<ResObject::Ptr> resolvables = _storage.storedObjects();
110         for (std::list<ResObject::Ptr>::iterator it = resolvables.begin();
111             it != resolvables.end();
112             it++)
113         {
114           _store.insert(*it);
115         }
116       }
117       else
118       {
119         WAR << "storage target not enabled" << std::endl;
120       }
121
122       return _store;
123     }
124
125     
126     Pathname TargetImpl::getRpmFile(Package::constPtr package)
127     {
128         callback::SendReport<source::DownloadResolvableReport> report;
129
130 #warning FIXME: error handling
131 #warning FIXME: Url     
132         report->start( package, Url() );
133
134         Pathname file = package->getPlainRpm();
135
136         report->finish( package, source::DownloadResolvableReport::NO_ERROR, "" );
137         
138         return file;
139     }
140
141
142     int TargetImpl::commit(ResPool pool_r, unsigned int medianr, TargetImpl::PoolItemList & errors_r, TargetImpl::PoolItemList & remaining_r, TargetImpl::PoolItemList & srcremaining_r)
143     {
144       MIL << "TargetImpl::commit(<pool>, " << medianr << ")" << endl;
145
146       errors_r.clear();
147       remaining_r.clear();
148       srcremaining_r.clear();
149
150       TargetImpl::PoolItemList to_uninstall;
151       TargetImpl::PoolItemList to_install;
152       TargetImpl::PoolItemList to_srcinstall;
153       getResolvablesToInsDel( pool_r, to_uninstall, to_install, to_srcinstall );
154
155       if ( medianr ) {
156         MIL << "Restrict to media number " << medianr << endl;
157       }
158
159       commit (to_uninstall);
160
161       if (medianr == 0) {                       // commit all
162         remaining_r = commit( to_install );
163         srcremaining_r = commit( to_srcinstall );
164       }
165       else
166       {
167         TargetImpl::PoolItemList current_install;
168         TargetImpl::PoolItemList current_srcinstall;
169
170         for (TargetImpl::PoolItemList::iterator it = to_install.begin(); it != to_install.end(); ++it)
171         {
172           Resolvable::constPtr res( it->resolvable() );
173           Package::constPtr pkg( asKind<Package>(res) );
174           if (pkg && medianr != pkg->mediaId())                                                         // check medianr for packages only
175           {
176             XXX << "Package " << *pkg << ", wrong media " << pkg->mediaId() << endl;
177             remaining_r.push_back( *it );
178           }
179           else
180           {
181             current_install.push_back( *it );
182           }
183         }
184         TargetImpl::PoolItemList bad = commit (current_install);
185         remaining_r.insert(remaining_r.end(), bad.begin(), bad.end());
186
187         for (TargetImpl::PoolItemList::iterator it = to_srcinstall.begin(); it != to_srcinstall.end(); ++it)
188         {
189           Resolvable::constPtr res( it->resolvable() );
190           Package::constPtr pkg( asKind<Package>(res) );
191           if (pkg && medianr != pkg->mediaId()) // check medianr for packages only
192           {
193             XXX << "Package " << *pkg << ", wrong media " << pkg->mediaId() << endl;
194             srcremaining_r.push_back( *it );
195           }
196           else {
197             current_srcinstall.push_back( *it );
198           }
199         }
200         bad = commit (current_srcinstall);
201         srcremaining_r.insert(srcremaining_r.end(), bad.begin(), bad.end());
202       }
203       return to_install.size() - remaining_r.size();
204     }
205
206
207     TargetImpl::PoolItemList
208     TargetImpl::commit( const TargetImpl::PoolItemList & items_r)
209     {
210       TargetImpl::PoolItemList remaining;
211
212       MIL << "TargetImpl::commit(<list>)" << endl;
213       
214       bool abort = false;
215
216       // remember the last used source (if any)
217       Source_Ref lastUsedSource;
218
219       for (TargetImpl::PoolItemList::const_iterator it = items_r.begin(); it != items_r.end(); it++)
220       {
221         if (isKind<Package>(it->resolvable()))
222         {
223           Package::constPtr p = dynamic_pointer_cast<const Package>(it->resolvable());
224           if (it->status().isToBeInstalled())
225           {
226             Pathname localfile = getRpmFile( p );
227             lastUsedSource = p->source();                       // remember the package source
228
229 #warning Exception handling
230             // create a installation progress report proxy
231             RpmInstallPackageReceiver progress( it->resolvable() );
232             progress.connect();
233             bool success = true;
234
235             try {
236               progress.tryLevel( target::rpm::InstallResolvableReport::RPM );
237                 
238               rpm().installPackage(localfile,
239                   p->installOnly() ? rpm::RpmDb::RPMINST_NOUPGRADE : 0);
240
241               if( progress.aborted() )
242               {
243                 WAR << "commit aborted by the user" << endl;
244                 progress.disconnect(); 
245                 abort = true;
246                 break;
247               }
248
249             }
250             catch (Exception & excpt_r) {
251               ZYPP_CAUGHT(excpt_r);
252               WAR << "Install failed, retrying with --nodeps" << endl;
253               try {
254                 progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS );
255                 rpm().installPackage(localfile,
256                 p->installOnly() ? rpm::RpmDb::RPMINST_NOUPGRADE : rpm::RpmDb::RPMINST_NODEPS);
257
258                 if( progress.aborted() )
259                 {
260                   WAR << "commit aborted by the user" << endl;
261                   abort = true;
262                   progress.disconnect(); 
263                   break;
264                 }
265               }
266               catch (Exception & excpt_r) 
267               {
268                 ZYPP_CAUGHT(excpt_r);
269                 WAR << "Install failed again, retrying with --force --nodeps" << endl;
270     
271                 try {
272                   progress.tryLevel( target::rpm::InstallResolvableReport::RPM_NODEPS_FORCE );
273                   rpm().installPackage(localfile,
274                       p->installOnly() ? rpm::RpmDb::RPMINST_NOUPGRADE : (rpm::RpmDb::RPMINST_NODEPS|rpm::RpmDb::RPMINST_FORCE));
275                 }
276                 catch (Exception & excpt_r) {
277                   remaining.push_back( *it );
278                   success = false;
279                   ZYPP_CAUGHT(excpt_r);
280                 }
281                 
282                 if( progress.aborted() )
283                 {
284                     WAR << "commit aborted by the user" << endl;
285                     abort = true;
286                     progress.disconnect(); 
287                     break;
288                 }
289               }
290             }
291             if (success) {
292               it->status().setTransact( false, ResStatus::USER );
293             }
294             progress.disconnect();
295           }
296           else
297           {
298             bool success = true;
299
300             RpmRemovePackageReceiver progress(it->resolvable());
301             progress.connect();
302             try {
303               rpm().removePackage( p, rpm::RpmDb::RPMINST_NODEPS );
304             }
305             catch (Exception & excpt_r) {
306               WAR << "removal of " << p << " failed";
307               success = false;
308               ZYPP_CAUGHT( excpt_r );
309             }
310             if (success) {
311               it->status().setTransact( false, ResStatus::USER );
312             }
313             progress.disconnect();
314           }
315         }
316         else // other resolvables
317         {
318           if ( isStorageEnabled() )
319           {
320             if (it->status().isToBeInstalled())
321             { 
322               bool success = false;
323               try
324               {
325                 _storage.storeObject(it->resolvable());
326                 success = true;
327               }
328               catch (Exception & excpt_r)
329               {
330                 ZYPP_CAUGHT(excpt_r);
331                 WAR << "Install of Resolvable from storage failed" << endl;
332               }
333               if (success)
334                 it->status().setTransact( false, ResStatus::USER );
335             }
336             else
337             {
338               bool success = false;
339               try
340               {
341                 _storage.deleteObject(it->resolvable());
342                 success = true;
343               }
344               catch (Exception & excpt_r)
345               {
346                 ZYPP_CAUGHT(excpt_r);
347                 WAR << "Uninstall of Resolvable from storage failed" << endl;
348               }
349               if (success)
350                 it->status().setTransact( false, ResStatus::USER );
351             }
352           }
353           else
354           {
355             WAR << "storage target disabled" << std::endl;
356           }
357
358         }  // other resolvables
359
360       } // for
361
362       // we're done with the commit, release the source media
363       //   In the case of a single media, end of commit means we don't need _this_
364       //   media any more.
365       //   In the case of 'commit any media', end of commit means we're completely
366       //   done and don't need the source's media anyways.
367
368       if (lastUsedSource) {             // if a source was used
369         lastUsedSource.release();       //  release their medias
370       }
371
372       if( abort ) 
373         ZYPP_THROW( Exception( N_("Target commit aborted by user.") ) );
374
375       return remaining;
376     }
377
378     rpm::RpmDb & TargetImpl::rpm()
379     { return _rpm; }
380
381     bool TargetImpl::providesFile (const std::string & path_str, const std::string & name_str) const
382     { return _rpm.hasFile(path_str, name_str); }
383
384       /** Return the resolvable which provides path_str (rpm -qf)
385           return NULL if no resolvable provides this file  */
386     ResObject::constPtr TargetImpl::whoOwnsFile (const std::string & path_str) const
387     {
388         string name = _rpm.whoOwnsFile (path_str);
389         if (name.empty())
390             return NULL;
391
392         for (ResStore::const_iterator it = _store.begin(); it != _store.end(); ++it) {
393             if ((*it)->name() == name) {
394                 return *it;
395             }
396         }
397         return NULL;
398     }
399
400     /** Set the log file for target */
401     bool TargetImpl::setInstallationLogfile(const Pathname & path_r)
402     {
403       return rpm::RpmDb::setInstallationLogfile(path_r);
404     }
405
406 //-----------------------------------------------------------------------------
407 /******************************************************************
408 **
409 **
410 **      FUNCTION NAME : strip_obsoleted_to_delete
411 **      FUNCTION TYPE : void
412 **
413 ** strip packages to_delete which get obsoleted by
414 ** to_install (i.e. delay deletion in case the
415 ** obsoleting package likes to save whatever...
416 */
417
418 static void
419 strip_obsoleted_to_delete( TargetImpl::PoolItemList & deleteList_r,
420                                 const TargetImpl::PoolItemList & instlist_r )
421 {
422   if ( deleteList_r.size() == 0 || instlist_r.size() == 0 )
423     return; // ---> nothing to do
424
425   // build obsoletes from instlist_r
426   CapSet obsoletes;
427   for ( TargetImpl::PoolItemList::const_iterator it = instlist_r.begin();
428         it != instlist_r.end(); ++it )
429   {
430     PoolItem_Ref item( *it );
431     obsoletes.insert( item->dep(Dep::OBSOLETES).begin(), item->dep(Dep::OBSOLETES).end() );
432   }
433   if ( obsoletes.size() == 0 )
434     return; // ---> nothing to do
435
436   // match them... ;(
437   TargetImpl::PoolItemList undelayed;
438   // forall applDelete Packages...
439   for ( TargetImpl::PoolItemList::iterator it = deleteList_r.begin();
440         it != deleteList_r.end(); ++it )
441   {
442     PoolItem_Ref ipkg( *it );
443     bool delayPkg = false;
444     // ...check whether an obsoletes....
445     for ( CapSet::iterator obs = obsoletes.begin();
446           ! delayPkg && obs != obsoletes.end(); ++obs )
447     {
448       // ...matches anything provided by the package?
449       for ( CapSet::const_iterator prov = ipkg->dep(Dep::PROVIDES).begin();
450             prov != ipkg->dep(Dep::PROVIDES).end(); ++prov )
451       {
452         if ( obs->matches( *prov ) == CapMatch::yes )
453         {
454           // if so, delay package deletion
455           DBG << "Ignore appl_delete (should be obsoleted): " << ipkg << endl;
456           delayPkg = true;
457           ipkg.status().setTransact( false, ResStatus::USER );
458           break;
459         }
460       }
461     }
462     if ( ! delayPkg ) {
463       DBG << "undelayed " << ipkg << endl;
464       undelayed.push_back( ipkg );
465     }
466   }
467   // Puhh...
468   deleteList_r.swap( undelayed );
469 }
470
471
472
473
474 void
475 TargetImpl::getResolvablesToInsDel ( const ResPool pool_r,
476                                     TargetImpl::PoolItemList & dellist_r,
477                                     TargetImpl::PoolItemList & instlist_r,
478                                     TargetImpl::PoolItemList & srclist_r ) const
479 {
480     dellist_r.clear();
481     instlist_r.clear();
482     srclist_r.clear();
483     TargetImpl::PoolItemList nonpkglist;
484
485     for ( ResPool::const_iterator it = pool_r.begin(); it != pool_r.end(); ++it )
486     {
487         if (it->status().isToBeInstalled())
488         {
489             if ((*it)->kind() == ResTraits<SrcPackage>::kind) {
490                 srclist_r.push_back( *it );
491             }
492             else if ((*it)->kind() != ResTraits<Package>::kind) {
493                 nonpkglist.push_back( *it );
494             }
495             else
496                 instlist_r.push_back( *it );
497         }
498         else if (it->status().isToBeUninstalled())
499         {
500             if ( it->status().isToBeUninstalledDueToObsolete() )
501             {
502                 DBG << "Ignore auto_delete (should be obsoleted): " << *it << endl;
503             }
504             else if ( it->status().isToBeUninstalledDueToUpgrade() )
505             {
506                 DBG << "Ignore auto_delete (should be upgraded): " << *it << endl;
507             }
508             else {
509                 dellist_r.push_back( *it );
510             }
511         }
512     }
513
514     MIL << "ResolvablesToInsDel: delete " << dellist_r.size()
515       << ", install " << instlist_r.size()
516         << ", srcinstall " << srclist_r.size()
517           << ", nonpkg " << nonpkglist.size() << endl;
518
519     ///////////////////////////////////////////////////////////////////
520     //
521     // strip packages to_delete which get obsoleted by
522     // to_install (i.e. delay deletion in case the
523     // obsoleting package likes to save whatever...
524     //
525     ///////////////////////////////////////////////////////////////////
526     strip_obsoleted_to_delete( dellist_r, instlist_r );
527
528     if ( dellist_r.size() ) {
529       ///////////////////////////////////////////////////////////////////
530       //
531       // sort delete list...
532       //
533       ///////////////////////////////////////////////////////////////////
534       TargetImpl::PoolItemSet delset;  // for delete order
535       TargetImpl::PoolItemSet dummy; // dummy, empty, should contain already installed
536       for ( TargetImpl::PoolItemList::iterator pkgIt = dellist_r.begin();
537             pkgIt != dellist_r.end(); ++pkgIt )
538       {
539         delset.insert( *pkgIt );
540       }
541
542       InstallOrder order( pool_r, delset, dummy ); // sort according top prereq
543       order.init();
544       const TargetImpl::PoolItemList dsorted( order.getTopSorted() );
545
546       dellist_r.clear();
547       for ( TargetImpl::PoolItemList::const_reverse_iterator cit = dsorted.rbegin();
548             cit != dsorted.rend(); ++cit )
549       {
550         dellist_r.push_back( *cit );
551       }
552     }
553
554     ///////////////////////////////////////////////////////////////////
555     //
556     // sort installed list...
557     //
558     ///////////////////////////////////////////////////////////////////
559     if ( instlist_r.empty() ) {
560       instlist_r.splice( instlist_r.end(), nonpkglist );
561
562       return;
563     }
564 #warning Source Rank Priority ?
565 #if 0
566     ///////////////////////////////////////////////////////////////////
567     // Get desired order of InstSrc'es to install from.
568     ///////////////////////////////////////////////////////////////////
569     typedef map<unsigned,unsigned> RankPriority;
570
571     RankPriority rankPriority;
572     {
573       InstSrcManager::ISrcIdList sourcerank( Y2PM::instSrcManager().instOrderSources() );
574       // map InstSrc rank to install priority
575       unsigned prio = 0;
576       for ( InstSrcManager::ISrcIdList::const_iterator it = sourcerank.begin();
577             it != sourcerank.end(); ++it, ++prio ) {
578         rankPriority[(*it)->descr()->default_rank()] = prio;
579       }
580     }
581 #endif
582
583     ///////////////////////////////////////////////////////////////////
584     // Compute install order according to packages prereq.
585     // Try to group packages with respect to the desired install order
586     ///////////////////////////////////////////////////////////////////
587     // backup list for debug purpose.
588     // You can as well build the set, clear the list and rebuild it in install order.
589     TargetImpl::PoolItemList instbackup_r;
590     instbackup_r.swap( instlist_r );
591
592     TargetImpl::PoolItemSet insset; // for install order
593     TargetImpl::PoolItemSet installed; // dummy, empty, should contain already installed
594     for ( TargetImpl::PoolItemList::iterator resIt = instbackup_r.begin(); resIt != instbackup_r.end(); ++resIt ) {
595       insset.insert( *resIt );
596     }
597     InstallOrder order( pool_r, insset, installed );
598     // start recursive depth-first-search
599     order.init();
600 MIL << "order.init() done" << endl;
601     order.printAdj( XXX, false );
602     ///////////////////////////////////////////////////////////////////
603     // build install list in install order
604     ///////////////////////////////////////////////////////////////////
605     TargetImpl::PoolItemList best_list;
606 //    unsigned best_prio     = 0;
607     unsigned best_medianum = 0;
608
609     TargetImpl::PoolItemList last_list;
610 //    unsigned last_prio     = 0;
611     unsigned last_medianum = 0;
612
613     TargetImpl::PoolItemList other_list;
614
615     for ( TargetImpl::PoolItemList items = order.computeNextSet(); ! items.empty(); items = order.computeNextSet() )
616     {
617 MIL << "order.computeNextSet: " << items.size() << " resolvables" << endl;
618       ///////////////////////////////////////////////////////////////////
619       // items contains all packages we could install now. Pick all packages
620       // from current media, or best media if none for current.
621       ///////////////////////////////////////////////////////////////////
622
623       best_list.clear();
624       last_list.clear();
625       other_list.clear();
626
627       for ( TargetImpl::PoolItemList::iterator cit = items.begin(); cit != items.end(); ++cit )
628       {
629         Resolvable::constPtr res( cit->resolvable() );
630         if (!res) continue;
631         Package::constPtr cpkg( asKind<Package>(res) );
632         if (!cpkg) {
633             XXX << "Not a package " << *cit << endl;
634             order.setInstalled( *cit );
635             other_list.push_back( *cit );
636             continue;
637         }
638         XXX << "Package " << *cpkg << ", media " << cpkg->mediaId() << " last_medianum " << last_medianum << " best_medianum " << best_medianum << endl;
639         if (                                                                    //  rankPriority[cpkg->instSrcRank()] == last_prio &&
640              cpkg->mediaId() == last_medianum ) {
641           // prefer packages on current media.
642           last_list.push_back( *cit );
643           continue;
644         }
645
646         if ( last_list.empty() ) {
647           // check for best media as long as there are no packages for current media.
648
649           if ( ! best_list.empty() ) {
650
651 #if 0
652             if ( rankPriority[cpkg->instSrcRank()] < best_prio ) {
653               best_list.clear(); // new best
654             } else if ( rankPriority[cpkg->instSrcRank()] == best_prio ) {
655 #endif
656
657               if ( cpkg->mediaId() < best_medianum ) {
658                 best_list.clear(); // new best
659               } else if ( cpkg->mediaId() == best_medianum ) {
660                 best_list.push_back( *cit ); // same as best -> add
661                 continue;
662               } else {
663                 continue; // worse
664               }
665 #if 0
666             } else {
667               continue; // worse
668             }
669 #endif
670           }
671
672           if ( best_list.empty() )
673           {
674             // first package or new best
675             best_list.push_back( *cit );
676 //          best_prio     = rankPriority[cpkg->instSrcRank()];
677             best_medianum = cpkg->mediaId();
678             continue;
679           }
680         }
681
682       } // for all packages in current set
683
684       ///////////////////////////////////////////////////////////////////
685       // remove packages picked from install order and append them to
686       // install list.
687       ///////////////////////////////////////////////////////////////////
688       TargetImpl::PoolItemList & take_list( last_list.empty() ? best_list : last_list );
689       if ( last_list.empty() )
690       {
691         MIL << "SET NEW media " << best_medianum << endl;
692 //      last_prio     = best_prio;
693         last_medianum = best_medianum;
694       }
695       else
696       {
697         MIL << "SET CONTINUE" << endl;
698       }
699
700       for ( TargetImpl::PoolItemList::iterator it = take_list.begin(); it != take_list.end(); ++it )
701       {
702         order.setInstalled( *it );
703         XXX << "SET isrc " << (*it)->name() << endl;
704       }
705       // move everthing from take_list to the end of instlist_r, clean take_list
706       instlist_r.splice( instlist_r.end(), take_list );
707       // same for other_list
708       instlist_r.splice( instlist_r.end(), other_list );
709
710     } // for all sets computed
711
712
713     if ( instbackup_r.size() != instlist_r.size() )
714     {
715         ERR << "***************** Lost packages in InstallOrder sort." << endl;
716     }
717     instlist_r.splice( instlist_r.end(), nonpkglist );
718 }
719
720
721     /////////////////////////////////////////////////////////////////
722   } // namespace target
723   ///////////////////////////////////////////////////////////////////
724   /////////////////////////////////////////////////////////////////
725 } // namespace zypp
726 ///////////////////////////////////////////////////////////////////