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