1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
12 #include <boost/function.hpp>
13 #include <boost/function_output_iterator.hpp>
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"
27 #undef ZYPP_BASE_LOGGER_LOGGROUP
28 #define ZYPP_BASE_LOGGER_LOGGROUP "locks"
30 #include "zypp/Locks.h"
37 Locks& Locks::instance()
39 static Locks _instance;
43 typedef std::set<PoolQuery> LockSet;
45 template <typename TPredicate>
46 void remove_if( LockSet & lockset_r, TPredicate pred_r )
48 LockSet::iterator first = lockset_r.begin();
49 LockSet::iterator last = lockset_r.end();
50 while ( first != last )
52 LockSet::iterator next = first;
54 if ( pred_r( *first ) )
55 lockset_r.erase( first );
67 bool mergeList(callback::SendReport<SavingLocksReport>& report);
75 // need to control manip locks _locks to maintain the legacy API LockList::iterator begin/end
77 const LockSet & locks() const
80 LockSet & MANIPlocks()
81 { if ( !_APIdirty ) _APIdirty = true; return _locks; }
83 const LockList & APIlocks() const
88 _APIlocks.insert( _APIlocks.end(), _locks.begin(), _locks.end() );
95 // need to control manip in ordert to maintain the legacy API LockList::iterator begin/end
97 mutable LockList _APIlocks;
98 mutable bool _APIdirty;
101 Locks::Locks() : _pimpl(new Impl){}
103 Locks::const_iterator Locks::begin() const
104 { return _pimpl->APIlocks().begin(); }
106 Locks::const_iterator Locks::end() const
107 { return _pimpl->APIlocks().end(); }
109 Locks::LockList::size_type Locks::size() const
110 { return _pimpl->locks().size(); }
112 bool Locks::empty() const
113 { return _pimpl->locks().empty(); }
117 void operator()(const PoolQuery& query) const
119 for_( it,query.begin(),query.end() )
122 item.status().setLock(true,ResStatus::USER);
123 DBG << "lock "<< item.resolvable()->name();
129 * iterator that takes lock, lock all solvables from query
130 * and send query to output iterator
132 template <class OutputIterator>
133 struct LockingOutputIterator
135 LockingOutputIterator(OutputIterator& out_)
139 void operator()(const PoolQuery& query) const
141 ApplyLock a;a(query);
149 void Locks::readAndApply( const Pathname& file )
151 MIL << "read and apply locks from "<<file << endl;
152 PathInfo pinfo(file);
153 if ( pinfo.isExist() )
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) );
160 MIL << "file does not exist(or cannot be stat), no lock added." << endl;
164 void Locks::read( const Pathname& file )
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()) );
171 MIL << "file does not exist(or cannot be stat), no lock added." << endl;
175 void Locks::apply() const
177 DBG << "apply locks" << endl;
178 for_each(_pimpl->locks().begin(), _pimpl->locks().end(), ApplyLock());
182 void Locks::addLock( const PoolQuery& query )
184 MIL << "add new lock" << endl;
185 for_( it,query.begin(),query.end() )
188 item.status().setLock(true,ResStatus::USER);
190 if ( _pimpl->toRemove.erase( query ) )
192 DBG << "query removed from toRemove" << endl;
196 DBG << "query added as new" << endl;
197 _pimpl->toAdd.insert( query );
201 void Locks::addLock( const IdString& ident_r )
203 sat::Solvable::SplitIdent id(ident_r);
204 addLock(id.kind(),id.name());
207 void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
209 addLock(kind_r,IdString(name_r));
212 void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
215 q.addAttribute( sat::SolvAttr::name,name_r.asString() );
218 q.setCaseSensitive(true);
219 DBG << "add lock by identifier" << endl;
223 void Locks::removeLock( const PoolQuery& query )
225 MIL << "remove lock" << endl;
226 for_( it,query.begin(),query.end() )
229 item.status().setLock(false,ResStatus::USER);
232 if ( _pimpl->toAdd.erase( query ) )
234 DBG << "query removed from added" << endl;
238 DBG << "need to remove some old lock" << endl;
239 _pimpl->toRemove.insert( query );
243 void Locks::removeLock( const IdString& ident_r )
245 sat::Solvable::SplitIdent id(ident_r);
246 removeLock(id.kind(),id.name());
249 void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
251 removeLock(kind_r,IdString(name_r));
254 void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
257 q.addAttribute( sat::SolvAttr::name,name_r.asString() );
260 q.setCaseSensitive(true);
261 DBG << "remove lock by Selectable" << endl;
265 bool Locks::existEmpty() const
267 for_( it, _pimpl->locks().begin(), _pimpl->locks().end() )
276 //handle locks during removing
277 class LocksCleanPredicate{
282 callback::SendReport<CleanEmptyLocksReport> &report;
285 LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
287 bool aborted(){ return skip_rest; }
289 bool operator()( const PoolQuery & q )
297 if (!report->progress((100*searched)/all))
303 switch (report->execute(q))
305 case CleanEmptyLocksReport::ABORT:
306 report->finish(CleanEmptyLocksReport::ABORTED);
309 case CleanEmptyLocksReport::DELETE:
311 case CleanEmptyLocksReport::IGNORE:
314 INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
322 void Locks::removeEmpty()
324 MIL << "clean of locks" << endl;
325 callback::SendReport<CleanEmptyLocksReport> report;
327 size_t sum = _pimpl->locks().size();
328 LocksCleanPredicate p(sum, report);
330 remove_if( _pimpl->MANIPlocks(), p );
334 MIL << "cleaning aborted" << endl;
335 report->finish(CleanEmptyLocksReport::ABORTED);
339 report->finish(CleanEmptyLocksReport::NO_ERROR);
343 if ( sum != _pimpl->locks().size() ) //some locks has been removed
344 _pimpl->locksDirty = true;
347 class LocksRemovePredicate
350 std::set<sat::Solvable>& solvs;
351 const PoolQuery& query;
352 callback::SendReport<SavingLocksReport>& report;
355 //1 for subset of set, 2 only intersect, 0 for not intersect
356 int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
358 bool intersect = false;
359 for_( it,q.begin(),q.end() )
361 if ( s.find(*it)!=s.end() )
371 return intersect ? 1 : 0;
375 LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
376 callback::SendReport<SavingLocksReport>& r)
377 : solvs(s), query(q),report(r),aborted_(false) {}
379 bool operator()(const PoolQuery& q)
385 DBG << "identical queries" << endl;
389 SavingLocksReport::ConflictState cs;
390 switch( contains(q,solvs) )
393 return false; //another lock
395 cs = SavingLocksReport::SAME_RESULTS;
398 cs = SavingLocksReport::INTERSECT;
403 MIL << "find conflict: " << cs << endl;
404 switch (report->conflict(q,cs))
406 case SavingLocksReport::ABORT:
408 DBG << "abort merging" << endl;
410 case SavingLocksReport::DELETE:
411 DBG << "force delete" << endl;
413 case SavingLocksReport::IGNORE:
414 DBG << "skip lock" << endl;
417 INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
421 bool aborted(){ return aborted_; }
424 bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
426 MIL << "merge list old: " << locks().size()
427 << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
428 for_(it,toRemove.begin(),toRemove.end())
430 std::set<sat::Solvable> s(it->begin(),it->end());
431 remove_if( MANIPlocks(), LocksRemovePredicate(s,*it, report) );
434 if (!report->progress())
437 MANIPlocks().insert( toAdd.begin(), toAdd.end() );
447 if( (_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
449 return; //nothing to merge
452 callback::SendReport<SavingLocksReport> report;
454 if (!_pimpl->mergeList(report))
456 report->finish(SavingLocksReport::ABORTED);
459 DBG << "locks merged" << endl;
460 report->finish(SavingLocksReport::NO_ERROR);
461 _pimpl->locksDirty = true;
464 void Locks::save( const Pathname& file )
466 if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
467 && !_pimpl->locksDirty )
469 DBG << "nothing changed in locks - no write to file" << endl;
473 callback::SendReport<SavingLocksReport> report;
476 if ((_pimpl->toAdd.size() | _pimpl->toRemove.size())!=0)
478 if (!_pimpl->mergeList(report))
480 report->finish(SavingLocksReport::ABORTED);
485 DBG << "wrote "<< _pimpl->locks().size() << "locks" << endl;
486 writePoolQueriesToFile( file, _pimpl->locks().begin(), _pimpl->locks().end() );
487 report->finish(SavingLocksReport::NO_ERROR);
490 void Locks::removeDuplicates()
491 { /* NOP since implementation uses std::set */ }