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/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"
27 #undef ZYPP_BASE_LOGGER_LOGGROUP
28 #define ZYPP_BASE_LOGGER_LOGGROUP "locks"
30 #include "zypp/Locks.h"
34 using namespace zypp::str;
39 Locks& Locks::instance()
41 static Locks _instance;
53 bool mergeList(callback::SendReport<SavingLocksReport>& report);
55 Impl():locksDirty(false){}
58 Locks::Locks() : _pimpl(new Impl){}
60 Locks::const_iterator Locks::begin() const
61 { return _pimpl->locks.begin(); }
63 Locks::const_iterator Locks::end() const
64 { return _pimpl->locks.end(); }
66 Locks::LockList::size_type Locks::size() const
67 { return _pimpl->locks.size(); }
69 bool Locks::empty() const
70 { return _pimpl->locks.empty(); }
74 void operator()(const PoolQuery& query) const
76 for_( it,query.begin(),query.end() )
79 item.status().setLock(true,ResStatus::USER);
80 DBG << "lock "<< item.resolvable()->name();
86 * iterator that takes lock, lock all solvables from query
87 * and send query to output iterator
89 template <class OutputIterator>
90 struct LockingOutputIterator
92 LockingOutputIterator(OutputIterator& out_)
96 void operator()(const PoolQuery& query) const
106 void Locks::readAndApply( const Pathname& file )
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) );
115 void Locks::read( const Pathname& file )
117 MIL << "read locks from "<<file << endl;
118 readPoolQueriesFromFile(
119 file, insert_iterator<LockList>(_pimpl->locks, _pimpl->locks.end()) );
123 void Locks::apply() const
125 DBG << "apply locks" << endl;
126 for_each(begin(), end(), ApplyLock());
130 void Locks::addLock( const PoolQuery& query )
132 MIL << "add new lock" << endl;
133 for_( it,query.begin(),query.end() )
136 item.status().setLock(true,ResStatus::USER);
138 LockList::iterator i = find(_pimpl->toRemove.begin(),
139 _pimpl->toRemove.end(), query);
140 if ( i != _pimpl->toRemove.end() )
142 DBG << "query removed from toRemove" << endl;
143 _pimpl->toRemove.erase(i);
147 DBG << "query added as new" << endl;
148 _pimpl->toAdd.push_back( query );
152 void Locks::addLock( const IdString& ident_r )
154 sat::Solvable::SplitIdent id(ident_r);
155 addLock(id.kind(),id.name());
158 void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
160 addLock(kind_r,IdString(name_r));
163 void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
166 q.addAttribute( sat::SolvAttr::name,name_r.asString() );
169 q.setCaseSensitive(true);
170 DBG << "add lock by identifier" << endl;
174 void Locks::removeLock( const PoolQuery& query )
176 MIL << "remove lock" << endl;
177 for_( it,query.begin(),query.end() )
180 item.status().setLock(false,ResStatus::USER);
183 LockList::iterator i = find(_pimpl->toAdd.begin(),
184 _pimpl->toAdd.end(), query);
185 if ( i != _pimpl->toAdd.end() )
187 DBG << "query removed from added" << endl;
188 _pimpl->toAdd.erase(i);
192 DBG << "needed remove some old lock" << endl;
193 _pimpl->toRemove.push_back( query );
197 void Locks::removeLock( const IdString& ident_r )
199 sat::Solvable::SplitIdent id(ident_r);
200 removeLock(id.kind(),id.name());
203 void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
205 removeLock(kind_r,IdString(name_r));
208 void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
211 q.addAttribute( sat::SolvAttr::name,name_r.asString() );
214 q.setCaseSensitive(true);
216 DBG << "remove lock by selectactable" << endl;
220 bool Locks::existEmpty() const
222 for_( it, _pimpl->locks.begin(), _pimpl->locks.end() )
231 //handle locks during removing
232 class LocksCleanPredicate{
237 callback::SendReport<CleanEmptyLocksReport> &report;
240 LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
242 bool aborted(){ return skip_rest; }
244 bool operator()(PoolQuery& q)
252 if (!report->progress((100*searched)/all))
258 switch (report->execute(q))
260 case CleanEmptyLocksReport::ABORT:
261 report->finish(CleanEmptyLocksReport::ABORTED);
264 case CleanEmptyLocksReport::DELETE:
266 case CleanEmptyLocksReport::IGNORE:
269 WAR << "Unknown returned value. Callback have more value then"
270 << " this switch. Need correct handle all enum values." << std::endl;
278 void Locks::removeEmpty()
280 MIL << "cleaning of locks" << endl;
281 callback::SendReport<CleanEmptyLocksReport> report;
283 size_t sum = _pimpl->locks.size();
284 LocksCleanPredicate p(sum, report);
286 _pimpl->locks.remove_if(p);
290 MIL << "cleaning aborted" << endl;
291 report->finish(CleanEmptyLocksReport::ABORTED);
295 report->finish(CleanEmptyLocksReport::NO_ERROR);
299 if ( sum != _pimpl->locks.size() ) //some locks has been removed
300 _pimpl->locksDirty = true;
303 class LocksRemovePredicate
306 std::set<sat::Solvable>& solvs;
307 const PoolQuery& query;
308 callback::SendReport<SavingLocksReport>& report;
311 //1 for subset of set, 2 only intersect, 0 for not intersect
312 int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
314 bool intersect = false;
315 for_( it,q.begin(),q.end() )
317 if ( s.find(*it)!=s.end() )
327 return intersect ? 1 : 0;
331 LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
332 callback::SendReport<SavingLocksReport>& r)
333 : solvs(s), query(q),report(r),aborted_(false) {}
335 bool operator()(const PoolQuery& q)
341 DBG << "identical queries" << endl;
345 SavingLocksReport::ConflictState cs;
346 switch( contains(q,solvs) )
349 return false; //another lock
351 cs = SavingLocksReport::SAME_RESULTS;
354 cs = SavingLocksReport::INTERSECT;
359 MIL << "find conflict: " << cs << endl;
360 switch (report->conflict(q,cs))
362 case SavingLocksReport::ABORT:
364 DBG << "abort merging" << endl;
366 case SavingLocksReport::DELETE:
367 DBG << "force delete" << endl;
369 case SavingLocksReport::IGNORE:
370 DBG << "skip lock" << endl;
373 WAR << "should not reached, some state is missing" << endl;
377 bool aborted(){ return aborted_; }
380 bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
382 MIL << "merging list old: " << locks.size()
383 << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
384 for_(it,toRemove.begin(),toRemove.end())
386 std::set<sat::Solvable> s(it->begin(),it->end());
387 locks.remove_if(LocksRemovePredicate(s,*it, report));
390 if (!report->progress())
393 for_( it, toAdd.begin(), toAdd.end() )
395 if( std::find( locks.begin(), locks.end(), *it ) == locks.end() )
396 locks.push_back( *it );
405 void Locks::save( const Pathname& file )
407 if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
408 && !_pimpl->locksDirty )
410 DBG << "nothing changed in locks - no write to file" << endl;
414 callback::SendReport<SavingLocksReport> report;
416 if (!_pimpl->mergeList(report))
418 report->finish(SavingLocksReport::ABORTED);
421 DBG << "writed "<< _pimpl->locks.size() << "locks" << endl;
422 writePoolQueriesToFile( file, _pimpl->locks.begin(), _pimpl->locks.end() );
423 report->finish(SavingLocksReport::NO_ERROR);
426 void Locks::removeDuplicates()
428 size_type sum = size();
429 for_(it,_pimpl->locks.begin(),_pimpl->locks.end())
431 if ( find(_pimpl->locks.begin(),it,*it) != it )
432 _pimpl->locks.erase(it--); //-- to avoid using break iterator
436 _pimpl->locksDirty = true;