implement remove duplicate entries in lock file (bnc#385967)
[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/Logger.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 namespace std;
33 using namespace zypp;
34 using namespace zypp::str;
35
36 namespace zypp
37 {
38
39 Locks& Locks::instance()
40 {
41   static Locks _instance;
42   return _instance;
43 }
44
45 class Locks::Impl
46 {
47 public:
48   LockList locks;
49   LockList toAdd;
50   LockList toRemove;
51   bool     locksDirty;
52
53   bool mergeList(callback::SendReport<SavingLocksReport>& report);
54   
55   Impl():locksDirty(false){}
56 };
57
58 Locks::Locks() : _pimpl(new Impl){}
59
60 Locks::const_iterator Locks::begin() const
61 { return _pimpl->locks.begin(); }
62
63 Locks::const_iterator Locks::end() const
64 { return _pimpl->locks.end(); }
65
66 Locks::LockList::size_type Locks::size() const
67 { return _pimpl->locks.size(); }
68
69 bool Locks::empty() const
70 { return _pimpl->locks.empty(); }
71
72 struct ApplyLock
73 {
74   void operator()(const PoolQuery& query) const
75   {
76     for_( it,query.begin(),query.end() )
77     {
78       PoolItem item(*it);
79       item.status().setLock(true,ResStatus::USER);
80       DBG << "lock "<< item.resolvable()->name();
81     }
82   }
83 };
84
85 /**
86  * iterator that takes lock, lock all solvables from query 
87  * and send query to output iterator
88  */
89 template <class OutputIterator>
90 struct LockingOutputIterator
91 {
92   LockingOutputIterator(OutputIterator& out_)
93     : out(out_)
94     {}
95
96   void operator()(const PoolQuery& query) const
97   {
98     ApplyLock a;a(query);
99     *out++ = query;
100   }
101   
102   private:
103   OutputIterator& out;
104 };
105
106 void Locks::readAndApply( const Pathname& file )
107 {
108   MIL << "read and apply locks from "<<file << endl;
109   insert_iterator<LockList> ii( _pimpl->locks,
110       _pimpl->locks.end() );
111   LockingOutputIterator<insert_iterator<LockList> > lout(ii);
112   readPoolQueriesFromFile( file, boost::make_function_output_iterator(lout) );
113 }
114
115 void Locks::read( const Pathname& file )
116 {
117   MIL << "read locks from "<<file << endl;
118   readPoolQueriesFromFile(
119     file, insert_iterator<LockList>(_pimpl->locks, _pimpl->locks.end()) );
120 }
121
122
123 void Locks::apply() const
124
125   DBG << "apply locks" << endl;
126   for_each(begin(), end(), ApplyLock());
127 }
128
129
130 void Locks::addLock( const PoolQuery& query )
131 {
132   MIL << "add new lock" << endl;
133   for_( it,query.begin(),query.end() )
134   {
135     PoolItem item(*it);
136     item.status().setLock(true,ResStatus::USER);
137   }
138   LockList::iterator i = find(_pimpl->toRemove.begin(),
139     _pimpl->toRemove.end(), query);
140   if ( i != _pimpl->toRemove.end() )
141   {
142     DBG << "query removed from toRemove" << endl;
143     _pimpl->toRemove.erase(i);
144   }
145   else
146   {
147     DBG << "query added as new" << endl;
148     _pimpl->toAdd.push_back( query );
149   }
150 }
151
152 void Locks::addLock( const IdString& ident_r )
153 {
154   sat::Solvable::SplitIdent id(ident_r);
155   addLock(id.kind(),id.name());
156 }
157
158 void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
159 {
160   addLock(kind_r,IdString(name_r));
161 }
162
163 void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
164 {
165   PoolQuery q;
166   q.addAttribute( sat::SolvAttr::name,name_r.asString() );
167   q.addKind( kind_r );
168   q.setMatchExact();
169   q.setCaseSensitive(true);
170   DBG << "add lock by identifier" << endl;
171   addLock( q );
172 }
173
174 void Locks::removeLock( const PoolQuery& query )
175 {
176   MIL << "remove lock" << endl;
177   for_( it,query.begin(),query.end() )
178   {
179     PoolItem item(*it);
180     item.status().setLock(false,ResStatus::USER);
181   }
182   
183   LockList::iterator i = find(_pimpl->toAdd.begin(),
184     _pimpl->toAdd.end(), query);
185   if ( i != _pimpl->toAdd.end() )
186   {
187     DBG << "query removed from added" << endl;
188     _pimpl->toAdd.erase(i);
189   }
190   else
191   {
192     DBG << "needed remove some old lock" << endl;
193     _pimpl->toRemove.push_back( query );
194   }
195 }
196
197 void Locks::removeLock( const IdString& ident_r )
198 {
199   sat::Solvable::SplitIdent id(ident_r);
200   removeLock(id.kind(),id.name());
201 }
202
203 void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
204 {
205   removeLock(kind_r,IdString(name_r));
206 }
207
208 void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
209 {
210   PoolQuery q;
211   q.addAttribute( sat::SolvAttr::name,name_r.asString() );
212   q.addKind( kind_r );
213   q.setMatchExact();
214   q.setCaseSensitive(true);
215   q.requireAll();
216   DBG << "remove lock by selectactable" << endl;
217   removeLock(q);
218 }
219
220 bool Locks::existEmpty() const
221 {
222   for_( it, _pimpl->locks.begin(), _pimpl->locks.end() )
223   {
224     if( it->empty() )
225       return true;
226   }
227
228   return false;
229 }
230
231 //handle locks during removing
232 class LocksCleanPredicate{
233 private:
234   bool skip_rest;
235   size_t searched;
236   size_t all;
237   callback::SendReport<CleanEmptyLocksReport> &report;
238
239 public:
240   LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
241
242   bool aborted(){ return skip_rest; }
243
244   bool operator()(PoolQuery& q)
245   {
246     if( skip_rest )
247       return false;
248     searched++;
249     if( !q.empty() )
250       return false;
251
252     if (!report->progress((100*searched)/all))
253     {
254       skip_rest = true;
255       return false;
256     }
257
258     switch (report->execute(q))
259     {
260     case CleanEmptyLocksReport::ABORT:
261       report->finish(CleanEmptyLocksReport::ABORTED);
262       skip_rest = true;
263       return false;
264     case CleanEmptyLocksReport::DELETE:
265       return true;
266     case CleanEmptyLocksReport::IGNORE:
267       return false;
268     default:
269       WAR << "Unknown returned value. Callback have more value then"
270           << " this switch. Need correct handle all enum values." << std::endl;
271     }
272
273     return false;
274   }
275
276 };
277
278 void Locks::removeEmpty()
279 {
280   MIL << "cleaning of locks" << endl;
281   callback::SendReport<CleanEmptyLocksReport> report;
282   report->start();
283   size_t sum = _pimpl->locks.size();
284   LocksCleanPredicate p(sum, report);
285
286   _pimpl->locks.remove_if(p);
287
288   if( p.aborted() )
289   {
290     MIL << "cleaning aborted" << endl;
291     report->finish(CleanEmptyLocksReport::ABORTED);
292   }
293   else 
294   {
295     report->finish(CleanEmptyLocksReport::NO_ERROR);
296
297   }
298
299   if ( sum != _pimpl->locks.size() ) //some locks has been removed
300     _pimpl->locksDirty = true;
301 }
302
303 class LocksRemovePredicate
304 {
305 private:
306   std::set<sat::Solvable>& solvs;
307   const PoolQuery& query;
308   callback::SendReport<SavingLocksReport>& report;
309   bool aborted_;
310
311   //1 for subset of set, 2 only intersect, 0 for not intersect
312   int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
313   {
314     bool intersect = false;
315     for_( it,q.begin(),q.end() )
316     {
317       if ( s.find(*it)!=s.end() )
318       {
319         intersect = true;
320       }
321       else
322       {
323         if (intersect)
324           return 2;
325       }
326     }
327     return intersect ? 1 : 0;
328   }
329
330 public:
331   LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
332       callback::SendReport<SavingLocksReport>& r)
333       : solvs(s), query(q),report(r),aborted_(false) {}
334
335   bool operator()(const PoolQuery& q)
336   {
337     if (aborted())
338       return false;
339     if( q==query )
340     {//identical
341       DBG << "identical queries" << endl;
342       return true;
343     }
344
345     SavingLocksReport::ConflictState cs;
346     switch( contains(q,solvs) )
347     {
348     case 0:
349       return false; //another lock
350     case 1:
351       cs = SavingLocksReport::SAME_RESULTS;
352       break;
353     case 2:
354       cs = SavingLocksReport::INTERSECT;
355       break;
356     default:
357       return true;
358     }
359     MIL << "find conflict: " << cs << endl;
360     switch (report->conflict(q,cs))
361     {
362     case SavingLocksReport::ABORT:
363       aborted_ = true;
364       DBG << "abort merging" << endl;
365       return false;
366     case SavingLocksReport::DELETE:
367       DBG << "force delete" << endl;
368       return true;
369     case SavingLocksReport::IGNORE:
370       DBG << "skip lock" << endl;
371       return false;
372     }
373     WAR << "should not reached, some state is missing" << endl;
374     return false;
375   }
376
377   bool aborted(){ return aborted_; }
378 };
379
380 bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
381 {
382   MIL << "merging list old: " << locks.size()
383     << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
384   for_(it,toRemove.begin(),toRemove.end())
385   {
386     std::set<sat::Solvable> s(it->begin(),it->end());
387     locks.remove_if(LocksRemovePredicate(s,*it, report));
388   }
389
390   if (!report->progress())
391     return false;
392
393   for_( it, toAdd.begin(), toAdd.end() )
394   {
395     if( std::find( locks.begin(), locks.end(), *it ) == locks.end() )
396       locks.push_back( *it );
397   }
398
399   toAdd.clear();
400   toRemove.clear();
401
402   return true;
403 }
404
405 void Locks::save( const Pathname& file )
406 {
407   if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
408       && !_pimpl->locksDirty )
409   {
410     DBG << "nothing changed in locks - no write to file" << endl;
411     return;
412   }
413
414   callback::SendReport<SavingLocksReport> report;
415   report->start();
416   if (!_pimpl->mergeList(report))
417   {
418     report->finish(SavingLocksReport::ABORTED);
419     return;
420   }
421   DBG << "writed "<< _pimpl->locks.size() << "locks" << endl;
422   writePoolQueriesToFile( file, _pimpl->locks.begin(), _pimpl->locks.end() );
423   report->finish(SavingLocksReport::NO_ERROR);
424 }
425
426 void Locks::removeDuplicates()
427 {
428   size_type sum = size();
429   for_(it,_pimpl->locks.begin(),_pimpl->locks.end())
430   {
431     if ( find(_pimpl->locks.begin(),it,*it) != it )
432       _pimpl->locks.erase(it--); //-- to avoid using break iterator
433   }
434   
435   if (sum!=size())
436     _pimpl->locksDirty = true;
437 }
438
439 } // ns zypp