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