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