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