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 ( const PoolItem & item : query.poolItem() )
121 item.status().setLock(true,ResStatus::USER);
122 DBG << "lock "<< item.name();
128 * iterator that takes lock, lock all solvables from query
129 * and send query to output iterator
131 template <class OutputIterator>
132 struct LockingOutputIterator
134 LockingOutputIterator(OutputIterator& out_)
138 void operator()(const PoolQuery& query) const
140 ApplyLock a;a(query);
148 void Locks::readAndApply( const Pathname& file )
150 MIL << "read and apply locks from "<<file << endl;
151 PathInfo pinfo(file);
152 if ( pinfo.isExist() )
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) );
159 MIL << "file does not exist(or cannot be stat), no lock added." << endl;
163 void Locks::read( const Pathname& file )
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()) );
170 MIL << "file does not exist(or cannot be stat), no lock added." << endl;
174 void Locks::apply() const
176 DBG << "apply locks" << endl;
177 for_each(_pimpl->locks().begin(), _pimpl->locks().end(), ApplyLock());
181 void Locks::addLock( const PoolQuery& query )
183 MIL << "add new lock" << endl;
184 for_( it,query.begin(),query.end() )
187 item.status().setLock(true,ResStatus::USER);
189 if ( _pimpl->toRemove.erase( query ) )
191 DBG << "query removed from toRemove" << endl;
195 DBG << "query added as new" << endl;
196 _pimpl->toAdd.insert( query );
200 void Locks::addLock( const IdString& ident_r )
202 sat::Solvable::SplitIdent id(ident_r);
203 addLock(id.kind(),id.name());
206 void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
208 addLock(kind_r,IdString(name_r));
211 void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
214 q.addAttribute( sat::SolvAttr::name,name_r.asString() );
217 q.setCaseSensitive(true);
218 DBG << "add lock by identifier" << endl;
222 void Locks::removeLock( const PoolQuery& query )
224 MIL << "remove lock" << endl;
225 for_( it,query.begin(),query.end() )
228 item.status().setLock(false,ResStatus::USER);
231 if ( _pimpl->toAdd.erase( query ) )
233 DBG << "query removed from added" << endl;
237 DBG << "need to remove some old lock" << endl;
238 _pimpl->toRemove.insert( query );
242 void Locks::removeLock( const IdString& ident_r )
244 sat::Solvable::SplitIdent id(ident_r);
245 removeLock(id.kind(),id.name());
248 void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
250 removeLock(kind_r,IdString(name_r));
253 void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
256 q.addAttribute( sat::SolvAttr::name,name_r.asString() );
259 q.setCaseSensitive(true);
260 DBG << "remove lock by Selectable" << endl;
264 bool Locks::existEmpty() const
266 for_( it, _pimpl->locks().begin(), _pimpl->locks().end() )
275 //handle locks during removing
276 class LocksCleanPredicate{
281 callback::SendReport<CleanEmptyLocksReport> &report;
284 LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
286 bool aborted(){ return skip_rest; }
288 bool operator()( const PoolQuery & q )
296 if (!report->progress((100*searched)/all))
302 switch (report->execute(q))
304 case CleanEmptyLocksReport::ABORT:
305 report->finish(CleanEmptyLocksReport::ABORTED);
308 case CleanEmptyLocksReport::DELETE:
310 case CleanEmptyLocksReport::IGNORE:
313 INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
321 void Locks::removeEmpty()
323 MIL << "clean of locks" << endl;
324 callback::SendReport<CleanEmptyLocksReport> report;
326 size_t sum = _pimpl->locks().size();
327 LocksCleanPredicate p(sum, report);
329 remove_if( _pimpl->MANIPlocks(), p );
333 MIL << "cleaning aborted" << endl;
334 report->finish(CleanEmptyLocksReport::ABORTED);
338 report->finish(CleanEmptyLocksReport::NO_ERROR);
342 if ( sum != _pimpl->locks().size() ) //some locks has been removed
343 _pimpl->locksDirty = true;
346 class LocksRemovePredicate
349 std::set<sat::Solvable>& solvs;
350 const PoolQuery& query;
351 callback::SendReport<SavingLocksReport>& report;
354 //1 for subset of set, 2 only intersect, 0 for not intersect
355 int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
357 bool intersect = false;
358 for_( it,q.begin(),q.end() )
360 if ( s.find(*it)!=s.end() )
370 return intersect ? 1 : 0;
374 LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
375 callback::SendReport<SavingLocksReport>& r)
376 : solvs(s), query(q),report(r),aborted_(false) {}
378 bool operator()(const PoolQuery& q)
384 DBG << "identical queries" << endl;
388 SavingLocksReport::ConflictState cs;
389 switch( contains(q,solvs) )
392 return false; //another lock
394 cs = SavingLocksReport::SAME_RESULTS;
397 cs = SavingLocksReport::INTERSECT;
402 MIL << "find conflict: " << cs << endl;
403 switch (report->conflict(q,cs))
405 case SavingLocksReport::ABORT:
407 DBG << "abort merging" << endl;
409 case SavingLocksReport::DELETE:
410 DBG << "force delete" << endl;
412 case SavingLocksReport::IGNORE:
413 DBG << "skip lock" << endl;
416 INT << "Unexpected return value from callback. Need to adapt switch statement." << std::endl;
420 bool aborted(){ return aborted_; }
423 bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
425 MIL << "merge list old: " << locks().size()
426 << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
427 for_(it,toRemove.begin(),toRemove.end())
429 std::set<sat::Solvable> s(it->begin(),it->end());
430 remove_if( MANIPlocks(), LocksRemovePredicate(s,*it, report) );
433 if (!report->progress())
436 MANIPlocks().insert( toAdd.begin(), toAdd.end() );
446 if( (_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
448 return; //nothing to merge
451 callback::SendReport<SavingLocksReport> report;
453 if (!_pimpl->mergeList(report))
455 report->finish(SavingLocksReport::ABORTED);
458 DBG << "locks merged" << endl;
459 report->finish(SavingLocksReport::NO_ERROR);
460 _pimpl->locksDirty = true;
463 void Locks::save( const Pathname& file )
465 if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
466 && !_pimpl->locksDirty )
468 DBG << "nothing changed in locks - no write to file" << endl;
472 callback::SendReport<SavingLocksReport> report;
475 if ((_pimpl->toAdd.size() | _pimpl->toRemove.size())!=0)
477 if (!_pimpl->mergeList(report))
479 report->finish(SavingLocksReport::ABORTED);
484 DBG << "wrote "<< _pimpl->locks().size() << "locks" << endl;
485 writePoolQueriesToFile( file, _pimpl->locks().begin(), _pimpl->locks().end() );
486 report->finish(SavingLocksReport::NO_ERROR);
489 void Locks::removeDuplicates()
490 { /* NOP since implementation uses std::set */ }