156dad069f98544f15686a7012f4e5eec4f16c91
[platform/upstream/libzypp.git] / zypp / Locks.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9
10 #include <set>
11 #include <fstream>
12 #include <boost/function.hpp>
13 #include <boost/function_output_iterator.hpp>
14 #include <algorithm>
15
16 #include "zypp/base/Regex.h"
17 #include "zypp/base/String.h"
18 #include "zypp/base/LogTools.h"
19 #include "zypp/base/IOStream.h"
20 #include "zypp/PoolItem.h"
21 #include "zypp/PoolQueryUtil.tcc"
22 #include "zypp/ZYppCallbacks.h"
23 #include "zypp/sat/SolvAttr.h"
24 #include "zypp/sat/Solvable.h"
25 #include "zypp/PathInfo.h"
26
27 #undef ZYPP_BASE_LOGGER_LOGGROUP
28 #define ZYPP_BASE_LOGGER_LOGGROUP "locks"
29
30 #include "zypp/Locks.h"
31
32 using std::endl;
33
34 namespace zypp
35 {
36
37 Locks& Locks::instance()
38 {
39   static Locks _instance;
40   return _instance;
41 }
42
43 typedef std::set<PoolQuery> LockSet;
44
45 template <typename TPredicate>
46 void remove_if( LockSet & lockset_r, TPredicate pred_r )
47 {
48   LockSet::iterator first = lockset_r.begin();
49   LockSet::iterator last = lockset_r.end();
50   while ( first != last )
51   {
52     LockSet::iterator next = first;
53     ++next;
54     if ( pred_r( *first ) )
55       lockset_r.erase( first );
56     first = next;
57   }
58 }
59
60 class Locks::Impl
61 {
62 public:
63   LockSet toAdd;
64   LockSet toRemove;
65   bool     locksDirty;
66
67   bool mergeList(callback::SendReport<SavingLocksReport>& report);
68   
69   Impl()
70   : locksDirty( false )
71   , _APIdirty( false )
72   {}
73
74
75   // need to control manip locks _locks to maintain the legacy API LockList::iterator begin/end
76
77   const LockSet & locks() const
78   { return _locks; }
79
80   LockSet & MANIPlocks()
81   { if ( !_APIdirty ) _APIdirty = true; return _locks; }
82
83   const LockList & APIlocks() const
84   {
85     if ( _APIdirty )
86     {
87       _APIlocks.clear();
88       _APIlocks.insert( _APIlocks.end(), _locks.begin(), _locks.end() );
89       _APIdirty = false;
90     }
91     return _APIlocks;
92   }
93
94 private:
95   // need to control manip in ordert to maintain the legacy API LockList::iterator begin/end
96   LockSet _locks;
97   mutable LockList _APIlocks;
98   mutable bool _APIdirty;
99 };
100
101 Locks::Locks() : _pimpl(new Impl){}
102
103 Locks::const_iterator Locks::begin() const
104 { return _pimpl->APIlocks().begin(); }
105
106 Locks::const_iterator Locks::end() const
107 { return _pimpl->APIlocks().end(); }
108
109 Locks::LockList::size_type Locks::size() const
110 { return _pimpl->locks().size(); }
111
112 bool Locks::empty() const
113 { return _pimpl->locks().empty(); }
114
115 struct ApplyLock
116 {
117   void operator()(const PoolQuery& query) const
118   {
119     for_( it,query.begin(),query.end() )
120     {
121       PoolItem item(*it);
122       item.status().setLock(true,ResStatus::USER);
123       DBG << "lock "<< item.resolvable()->name();
124     }
125   }
126 };
127
128 /**
129  * iterator that takes lock, lock all solvables from query 
130  * and send query to output iterator
131  */
132 template <class OutputIterator>
133 struct LockingOutputIterator
134 {
135   LockingOutputIterator(OutputIterator& out_)
136     : out(out_)
137     {}
138
139   void operator()(const PoolQuery& query) const
140   {
141     ApplyLock a;a(query);
142     *out++ = query;
143   }
144   
145   private:
146   OutputIterator& out;
147 };
148
149 void Locks::readAndApply( const Pathname& file )
150 {
151   MIL << "read and apply locks from "<<file << endl;
152   PathInfo pinfo(file);
153   if ( pinfo.isExist() )
154   {
155     std::insert_iterator<LockSet> ii( _pimpl->MANIPlocks(), _pimpl->MANIPlocks().end() );
156     LockingOutputIterator<std::insert_iterator<LockSet> > lout(ii);
157     readPoolQueriesFromFile( file, boost::make_function_output_iterator(lout) );
158   }
159   else
160     MIL << "file does not exist(or cannot be stat), no lock added." << endl;
161
162 }
163
164 void Locks::read( const Pathname& file )
165 {
166   MIL << "read locks from "<<file << endl;
167   PathInfo pinfo(file);
168   if ( pinfo.isExist() )
169     readPoolQueriesFromFile( file, std::insert_iterator<LockSet>(_pimpl->MANIPlocks(), _pimpl->MANIPlocks().end()) );
170   else 
171     MIL << "file does not exist(or cannot be stat), no lock added." << endl;
172 }
173
174
175 void Locks::apply() const
176
177   DBG << "apply locks" << endl;
178   for_each(_pimpl->locks().begin(), _pimpl->locks().end(), ApplyLock());
179 }
180
181
182 void Locks::addLock( const PoolQuery& query )
183 {
184   MIL << "add new lock" << endl;
185   for_( it,query.begin(),query.end() )
186   {
187     PoolItem item(*it);
188     item.status().setLock(true,ResStatus::USER);
189   }
190   if ( _pimpl->toRemove.erase( query ) )
191   {
192     DBG << "query removed from toRemove" << endl;
193   }
194   else
195   {
196     DBG << "query added as new" << endl;
197     _pimpl->toAdd.insert( query );
198   }
199 }
200
201 void Locks::addLock( const IdString& ident_r )
202 {
203   sat::Solvable::SplitIdent id(ident_r);
204   addLock(id.kind(),id.name());
205 }
206
207 void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
208 {
209   addLock(kind_r,IdString(name_r));
210 }
211
212 void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
213 {
214   PoolQuery q;
215   q.addAttribute( sat::SolvAttr::name,name_r.asString() );
216   q.addKind( kind_r );
217   q.setMatchExact();
218   q.setCaseSensitive(true);
219   DBG << "add lock by identifier" << endl;
220   addLock( q );
221 }
222
223 void Locks::removeLock( const PoolQuery& query )
224 {
225   MIL << "remove lock" << endl;
226   for_( it,query.begin(),query.end() )
227   {
228     PoolItem item(*it);
229     item.status().setLock(false,ResStatus::USER);
230   }
231   
232   if ( _pimpl->toAdd.erase( query ) )
233   {
234     DBG << "query removed from added" << endl;
235   }
236   else
237   {
238     DBG << "need to remove some old lock" << endl;
239     _pimpl->toRemove.insert( query );
240   }
241 }
242
243 void Locks::removeLock( const IdString& ident_r )
244 {
245   sat::Solvable::SplitIdent id(ident_r);
246   removeLock(id.kind(),id.name());
247 }
248
249 void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
250 {
251   removeLock(kind_r,IdString(name_r));
252 }
253
254 void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
255 {
256   PoolQuery q;
257   q.addAttribute( sat::SolvAttr::name,name_r.asString() );
258   q.addKind( kind_r );
259   q.setMatchExact();
260   q.setCaseSensitive(true);
261   DBG << "remove lock by Selectable" << endl;
262   removeLock(q);
263 }
264
265 bool Locks::existEmpty() const
266 {
267   for_( it, _pimpl->locks().begin(), _pimpl->locks().end() )
268   {
269     if( it->empty() )
270       return true;
271   }
272
273   return false;
274 }
275
276 //handle locks during removing
277 class LocksCleanPredicate{
278 private:
279   bool skip_rest;
280   size_t searched;
281   size_t all;
282   callback::SendReport<CleanEmptyLocksReport> &report;
283
284 public:
285   LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
286
287   bool aborted(){ return skip_rest; }
288
289   bool operator()( const PoolQuery & q )
290   {
291     if( skip_rest )
292       return false;
293     searched++;
294     if( !q.empty() )
295       return false;
296
297     if (!report->progress((100*searched)/all))
298     {
299       skip_rest = true;
300       return false;
301     }
302
303     switch (report->execute(q))
304     {
305     case CleanEmptyLocksReport::ABORT:
306       report->finish(CleanEmptyLocksReport::ABORTED);
307       skip_rest = true;
308       return false;
309     case CleanEmptyLocksReport::DELETE:
310       return true;
311     case CleanEmptyLocksReport::IGNORE:
312       return false;
313     default:
314       INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
315     }
316
317     return false;
318   }
319
320 };
321
322 void Locks::removeEmpty()
323 {
324   MIL << "clean of locks" << endl;
325   callback::SendReport<CleanEmptyLocksReport> report;
326   report->start();
327   size_t sum = _pimpl->locks().size();
328   LocksCleanPredicate p(sum, report);
329
330   remove_if( _pimpl->MANIPlocks(), p );
331
332   if( p.aborted() )
333   {
334     MIL << "cleaning aborted" << endl;
335     report->finish(CleanEmptyLocksReport::ABORTED);
336   }
337   else 
338   {
339     report->finish(CleanEmptyLocksReport::NO_ERROR);
340
341   }
342
343   if ( sum != _pimpl->locks().size() ) //some locks has been removed
344     _pimpl->locksDirty = true;
345 }
346
347 class LocksRemovePredicate
348 {
349 private:
350   std::set<sat::Solvable>& solvs;
351   const PoolQuery& query;
352   callback::SendReport<SavingLocksReport>& report;
353   bool aborted_;
354
355   //1 for subset of set, 2 only intersect, 0 for not intersect
356   int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
357   {
358     bool intersect = false;
359     for_( it,q.begin(),q.end() )
360     {
361       if ( s.find(*it)!=s.end() )
362       {
363         intersect = true;
364       }
365       else
366       {
367         if (intersect)
368           return 2;
369       }
370     }
371     return intersect ? 1 : 0;
372   }
373
374 public:
375   LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
376       callback::SendReport<SavingLocksReport>& r)
377       : solvs(s), query(q),report(r),aborted_(false) {}
378
379   bool operator()(const PoolQuery& q)
380   {
381     if (aborted())
382       return false;
383     if( q==query )
384     {//identical
385       DBG << "identical queries" << endl;
386       return true;
387     }
388
389     SavingLocksReport::ConflictState cs;
390     switch( contains(q,solvs) )
391     {
392     case 0:
393       return false; //another lock
394     case 1:
395       cs = SavingLocksReport::SAME_RESULTS;
396       break;
397     case 2:
398       cs = SavingLocksReport::INTERSECT;
399       break;
400     default:
401       return true;
402     }
403     MIL << "find conflict: " << cs << endl;
404     switch (report->conflict(q,cs))
405     {
406     case SavingLocksReport::ABORT:
407       aborted_ = true;
408       DBG << "abort merging" << endl;
409       return false;
410     case SavingLocksReport::DELETE:
411       DBG << "force delete" << endl;
412       return true;
413     case SavingLocksReport::IGNORE:
414       DBG << "skip lock" << endl;
415       return false;
416     }
417     INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
418     return false;
419   }
420
421   bool aborted(){ return aborted_; }
422 };
423
424 bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
425 {
426   MIL << "merge list old: " << locks().size()
427     << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
428   for_(it,toRemove.begin(),toRemove.end())
429   {
430     std::set<sat::Solvable> s(it->begin(),it->end());
431     remove_if( MANIPlocks(), LocksRemovePredicate(s,*it, report) );
432   }
433
434   if (!report->progress())
435     return false;
436
437   MANIPlocks().insert( toAdd.begin(), toAdd.end() );
438
439   toAdd.clear();
440   toRemove.clear();
441
442   return true;
443 }
444
445 void Locks::merge()
446 {
447   if( (_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
448   {
449     return; //nothing to merge
450   }
451
452   callback::SendReport<SavingLocksReport> report;
453   report->start();
454   if (!_pimpl->mergeList(report))
455   {
456     report->finish(SavingLocksReport::ABORTED);
457     return;
458   }
459   DBG << "locks merged" << endl;
460   report->finish(SavingLocksReport::NO_ERROR);
461   _pimpl->locksDirty = true;
462 }
463
464 void Locks::save( const Pathname& file )
465 {
466   if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
467       && !_pimpl->locksDirty )
468   {
469     DBG << "nothing changed in locks - no write to file" << endl;
470     return;
471   }
472
473   callback::SendReport<SavingLocksReport> report;
474   report->start();
475
476   if ((_pimpl->toAdd.size() | _pimpl->toRemove.size())!=0)
477   {
478     if (!_pimpl->mergeList(report))
479     {
480       report->finish(SavingLocksReport::ABORTED);
481       return;
482     }
483   }
484
485   DBG << "wrote "<< _pimpl->locks().size() << "locks" << endl;
486   writePoolQueriesToFile( file, _pimpl->locks().begin(), _pimpl->locks().end() );
487   report->finish(SavingLocksReport::NO_ERROR);
488 }
489
490 void Locks::removeDuplicates()
491 { /* NOP since implementation uses std::set */ }
492
493 } // ns zypp