d8c65f0c8cd25a78290e2c4939d5e8c906b89d0e
[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 ( const PoolItem & item : query.poolItem() )
120     {
121       item.status().setLock(true,ResStatus::USER);
122       DBG << "lock "<< item.name();
123     }
124   }
125 };
126
127 /**
128  * iterator that takes lock, lock all solvables from query 
129  * and send query to output iterator
130  */
131 template <class OutputIterator>
132 struct LockingOutputIterator
133 {
134   LockingOutputIterator(OutputIterator& out_)
135     : out(out_)
136     {}
137
138   void operator()(const PoolQuery& query) const
139   {
140     ApplyLock a;a(query);
141     *out++ = query;
142   }
143   
144   private:
145   OutputIterator& out;
146 };
147
148 void Locks::readAndApply( const Pathname& file )
149 {
150   MIL << "read and apply locks from "<<file << endl;
151   PathInfo pinfo(file);
152   if ( pinfo.isExist() )
153   {
154     std::insert_iterator<LockSet> ii( _pimpl->MANIPlocks(), _pimpl->MANIPlocks().end() );
155     LockingOutputIterator<std::insert_iterator<LockSet> > lout(ii);
156     readPoolQueriesFromFile( file, boost::make_function_output_iterator(lout) );
157   }
158   else
159     MIL << "file does not exist(or cannot be stat), no lock added." << endl;
160
161 }
162
163 void Locks::read( const Pathname& file )
164 {
165   MIL << "read locks from "<<file << endl;
166   PathInfo pinfo(file);
167   if ( pinfo.isExist() )
168     readPoolQueriesFromFile( file, std::insert_iterator<LockSet>(_pimpl->MANIPlocks(), _pimpl->MANIPlocks().end()) );
169   else 
170     MIL << "file does not exist(or cannot be stat), no lock added." << endl;
171 }
172
173
174 void Locks::apply() const
175
176   DBG << "apply locks" << endl;
177   for_each(_pimpl->locks().begin(), _pimpl->locks().end(), ApplyLock());
178 }
179
180
181 void Locks::addLock( const PoolQuery& query )
182 {
183   MIL << "add new lock" << endl;
184   for_( it,query.begin(),query.end() )
185   {
186     PoolItem item(*it);
187     item.status().setLock(true,ResStatus::USER);
188   }
189   if ( _pimpl->toRemove.erase( query ) )
190   {
191     DBG << "query removed from toRemove" << endl;
192   }
193   else
194   {
195     DBG << "query added as new" << endl;
196     _pimpl->toAdd.insert( query );
197   }
198 }
199
200 void Locks::addLock( const IdString& ident_r )
201 {
202   sat::Solvable::SplitIdent id(ident_r);
203   addLock(id.kind(),id.name());
204 }
205
206 void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
207 {
208   addLock(kind_r,IdString(name_r));
209 }
210
211 void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
212 {
213   PoolQuery q;
214   q.addAttribute( sat::SolvAttr::name,name_r.asString() );
215   q.addKind( kind_r );
216   q.setMatchExact();
217   q.setCaseSensitive(true);
218   DBG << "add lock by identifier" << endl;
219   addLock( q );
220 }
221
222 void Locks::removeLock( const PoolQuery& query )
223 {
224   MIL << "remove lock" << endl;
225   for_( it,query.begin(),query.end() )
226   {
227     PoolItem item(*it);
228     item.status().setLock(false,ResStatus::USER);
229   }
230   
231   if ( _pimpl->toAdd.erase( query ) )
232   {
233     DBG << "query removed from added" << endl;
234   }
235   else
236   {
237     DBG << "need to remove some old lock" << endl;
238     _pimpl->toRemove.insert( query );
239   }
240 }
241
242 void Locks::removeLock( const IdString& ident_r )
243 {
244   sat::Solvable::SplitIdent id(ident_r);
245   removeLock(id.kind(),id.name());
246 }
247
248 void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
249 {
250   removeLock(kind_r,IdString(name_r));
251 }
252
253 void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
254 {
255   PoolQuery q;
256   q.addAttribute( sat::SolvAttr::name,name_r.asString() );
257   q.addKind( kind_r );
258   q.setMatchExact();
259   q.setCaseSensitive(true);
260   DBG << "remove lock by Selectable" << endl;
261   removeLock(q);
262 }
263
264 bool Locks::existEmpty() const
265 {
266   for_( it, _pimpl->locks().begin(), _pimpl->locks().end() )
267   {
268     if( it->empty() )
269       return true;
270   }
271
272   return false;
273 }
274
275 //handle locks during removing
276 class LocksCleanPredicate{
277 private:
278   bool skip_rest;
279   size_t searched;
280   size_t all;
281   callback::SendReport<CleanEmptyLocksReport> &report;
282
283 public:
284   LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
285
286   bool aborted(){ return skip_rest; }
287
288   bool operator()( const PoolQuery & q )
289   {
290     if( skip_rest )
291       return false;
292     searched++;
293     if( !q.empty() )
294       return false;
295
296     if (!report->progress((100*searched)/all))
297     {
298       skip_rest = true;
299       return false;
300     }
301
302     switch (report->execute(q))
303     {
304     case CleanEmptyLocksReport::ABORT:
305       report->finish(CleanEmptyLocksReport::ABORTED);
306       skip_rest = true;
307       return false;
308     case CleanEmptyLocksReport::DELETE:
309       return true;
310     case CleanEmptyLocksReport::IGNORE:
311       return false;
312     default:
313       INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
314     }
315
316     return false;
317   }
318
319 };
320
321 void Locks::removeEmpty()
322 {
323   MIL << "clean of locks" << endl;
324   callback::SendReport<CleanEmptyLocksReport> report;
325   report->start();
326   size_t sum = _pimpl->locks().size();
327   LocksCleanPredicate p(sum, report);
328
329   remove_if( _pimpl->MANIPlocks(), p );
330
331   if( p.aborted() )
332   {
333     MIL << "cleaning aborted" << endl;
334     report->finish(CleanEmptyLocksReport::ABORTED);
335   }
336   else 
337   {
338     report->finish(CleanEmptyLocksReport::NO_ERROR);
339
340   }
341
342   if ( sum != _pimpl->locks().size() ) //some locks has been removed
343     _pimpl->locksDirty = true;
344 }
345
346 class LocksRemovePredicate
347 {
348 private:
349   std::set<sat::Solvable>& solvs;
350   const PoolQuery& query;
351   callback::SendReport<SavingLocksReport>& report;
352   bool aborted_;
353
354   //1 for subset of set, 2 only intersect, 0 for not intersect
355   int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
356   {
357     bool intersect = false;
358     for_( it,q.begin(),q.end() )
359     {
360       if ( s.find(*it)!=s.end() )
361       {
362         intersect = true;
363       }
364       else
365       {
366         if (intersect)
367           return 2;
368       }
369     }
370     return intersect ? 1 : 0;
371   }
372
373 public:
374   LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
375       callback::SendReport<SavingLocksReport>& r)
376       : solvs(s), query(q),report(r),aborted_(false) {}
377
378   bool operator()(const PoolQuery& q)
379   {
380     if (aborted())
381       return false;
382     if( q==query )
383     {//identical
384       DBG << "identical queries" << endl;
385       return true;
386     }
387
388     SavingLocksReport::ConflictState cs;
389     switch( contains(q,solvs) )
390     {
391     case 0:
392       return false; //another lock
393     case 1:
394       cs = SavingLocksReport::SAME_RESULTS;
395       break;
396     case 2:
397       cs = SavingLocksReport::INTERSECT;
398       break;
399     default:
400       return true;
401     }
402     MIL << "find conflict: " << cs << endl;
403     switch (report->conflict(q,cs))
404     {
405     case SavingLocksReport::ABORT:
406       aborted_ = true;
407       DBG << "abort merging" << endl;
408       return false;
409     case SavingLocksReport::DELETE:
410       DBG << "force delete" << endl;
411       return true;
412     case SavingLocksReport::IGNORE:
413       DBG << "skip lock" << endl;
414       return false;
415     }
416     INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
417     return false;
418   }
419
420   bool aborted(){ return aborted_; }
421 };
422
423 bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
424 {
425   MIL << "merge list old: " << locks().size()
426     << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
427   for_(it,toRemove.begin(),toRemove.end())
428   {
429     std::set<sat::Solvable> s(it->begin(),it->end());
430     remove_if( MANIPlocks(), LocksRemovePredicate(s,*it, report) );
431   }
432
433   if (!report->progress())
434     return false;
435
436   MANIPlocks().insert( toAdd.begin(), toAdd.end() );
437
438   toAdd.clear();
439   toRemove.clear();
440
441   return true;
442 }
443
444 void Locks::merge()
445 {
446   if( (_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
447   {
448     return; //nothing to merge
449   }
450
451   callback::SendReport<SavingLocksReport> report;
452   report->start();
453   if (!_pimpl->mergeList(report))
454   {
455     report->finish(SavingLocksReport::ABORTED);
456     return;
457   }
458   DBG << "locks merged" << endl;
459   report->finish(SavingLocksReport::NO_ERROR);
460   _pimpl->locksDirty = true;
461 }
462
463 void Locks::save( const Pathname& file )
464 {
465   if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
466       && !_pimpl->locksDirty )
467   {
468     DBG << "nothing changed in locks - no write to file" << endl;
469     return;
470   }
471
472   callback::SendReport<SavingLocksReport> report;
473   report->start();
474
475   if ((_pimpl->toAdd.size() | _pimpl->toRemove.size())!=0)
476   {
477     if (!_pimpl->mergeList(report))
478     {
479       report->finish(SavingLocksReport::ABORTED);
480       return;
481     }
482   }
483
484   DBG << "wrote "<< _pimpl->locks().size() << "locks" << endl;
485   writePoolQueriesToFile( file, _pimpl->locks().begin(), _pimpl->locks().end() );
486   report->finish(SavingLocksReport::NO_ERROR);
487 }
488
489 void Locks::removeDuplicates()
490 { /* NOP since implementation uses std::set */ }
491
492 } // ns zypp