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