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