Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / Locks.cc
index 2656352..b4ff9d5 100644 (file)
 
 #include <set>
 #include <fstream>
-#include <boost/regex.hpp>
 #include <boost/function.hpp>
+#include <boost/function_output_iterator.hpp>
+#include <algorithm>
 
+#include "zypp/base/Regex.h"
+#include "zypp/base/String.h"
 #include "zypp/base/Logger.h"
 #include "zypp/base/IOStream.h"
 #include "zypp/PoolItem.h"
-#include "zypp/CapFactory.h"
-#include "zypp/CapMatchHelper.h"
-#include "zypp/capability/Capabilities.h"
+#include "zypp/PoolQueryUtil.tcc"
+#include "zypp/ZYppCallbacks.h"
+#include "zypp/sat/SolvAttr.h"
+#include "zypp/sat/Solvable.h"
+#include "zypp/PathInfo.h"
 
 #undef ZYPP_BASE_LOGGER_LOGGROUP
 #define ZYPP_BASE_LOGGER_LOGGROUP "locks"
 
 #include "zypp/Locks.h"
-#include "zypp/PathInfo.h"
 
-using namespace std;
-using namespace zypp;
-using namespace boost;
-using boost::regex;
+using std::endl;
 
 namespace zypp
 {
-namespace locks
+
+Locks& Locks::instance()
+{
+  static Locks _instance;
+  return _instance;
+}
+
+class Locks::Impl
 {
+public:
+  LockList locks;
+  LockList toAdd;
+  LockList toRemove;
+  bool     locksDirty;
+
+  bool mergeList(callback::SendReport<SavingLocksReport>& report);
+  
+  Impl():locksDirty(false){}
+};
+
+Locks::Locks() : _pimpl(new Impl){}
+
+Locks::const_iterator Locks::begin() const
+{ return _pimpl->locks.begin(); }
+
+Locks::const_iterator Locks::end() const
+{ return _pimpl->locks.end(); }
 
-//
-// collect matching names
-//
-// called by regexp matching, see 'Match' below
-//
+Locks::LockList::size_type Locks::size() const
+{ return _pimpl->locks.size(); }
+
+bool Locks::empty() const
+{ return _pimpl->locks.empty(); }
+
+struct ApplyLock
+{
+  void operator()(const PoolQuery& query) const
+  {
+    for ( const PoolItem & item : query.poolItem() )
+    {
+      item.status().setLock(true,ResStatus::USER);
+      DBG << "lock "<< item.name();
+    }
+  }
+};
 
-struct NameMatchCollectorFunc
+/**
+ * iterator that takes lock, lock all solvables from query 
+ * and send query to output iterator
+ */
+template <class OutputIterator>
+struct LockingOutputIterator
 {
-  set<string> matches;
+  LockingOutputIterator(OutputIterator& out_)
+    : out(out_)
+    {}
 
-  bool operator()( const PoolItem &item )
+  void operator()(const PoolQuery& query) const
   {
-    matches.insert( item.resolvable()->name() );
-    return true;
+    ApplyLock a;a(query);
+    *out++ = query;
   }
+  
+  private:
+  OutputIterator& out;
 };
 
+void Locks::readAndApply( const Pathname& file )
+{
+  MIL << "read and apply locks from "<<file << endl;
+  PathInfo pinfo(file);
+  if ( pinfo.isExist() )
+  {
+    std::insert_iterator<LockList> ii( _pimpl->locks, _pimpl->locks.end() );
+    LockingOutputIterator<std::insert_iterator<LockList> > lout(ii);
+    readPoolQueriesFromFile( file, boost::make_function_output_iterator(lout) );
+  }
+  else
+    MIL << "file not exist(or cannot be stat), no lock added." << endl;
+
+}
 
-// taken from zypper
-struct Match
+void Locks::read( const Pathname& file )
 {
-  const regex * _regex;
+  MIL << "read locks from "<<file << endl;
+  PathInfo pinfo(file);
+  if ( pinfo.isExist() )
+    readPoolQueriesFromFile( file, std::insert_iterator<LockList>(_pimpl->locks, _pimpl->locks.end()) );
+  else 
+    MIL << "file not exist(or cannot be stat), no lock added." << endl;
+}
+
+
+void Locks::apply() const
+{ 
+  DBG << "apply locks" << endl;
+  for_each(begin(), end(), ApplyLock());
+}
 
-  Match(const regex & regex ) :
-    _regex(&regex)
-  {}
 
-  bool operator()(const zypp::PoolItem & pi) const
+void Locks::addLock( const PoolQuery& query )
+{
+  MIL << "add new lock" << endl;
+  for_( it,query.begin(),query.end() )
   {
-    return
-    // match resolvable name
-    regex_match(pi.resolvable()->name(), *_regex);
+    PoolItem item(*it);
+    item.status().setLock(true,ResStatus::USER);
   }
-};
-
+  LockList::iterator i = find(_pimpl->toRemove.begin(),
+    _pimpl->toRemove.end(), query);
+  if ( i != _pimpl->toRemove.end() )
+  {
+    DBG << "query removed from toRemove" << endl;
+    _pimpl->toRemove.erase(i);
+  }
+  else
+  {
+    DBG << "query added as new" << endl;
+    _pimpl->toAdd.push_back( query );
+  }
+}
 
-string
-wildcards2regex(const string & str)
+void Locks::addLock( const IdString& ident_r )
 {
-  string regexed;
+  sat::Solvable::SplitIdent id(ident_r);
+  addLock(id.kind(),id.name());
+}
 
-  regex all("\\*"); // regex to search for '*'
-  regex one("\\?"); // regex to search for '?'
-  string r_all(".*"); // regex equivalent of '*'
-  string r_one(".");  // regex equivalent of '?'
+void Locks::addLock( const ResKind& kind_r, const C_Str & name_r )
+{
+  addLock(kind_r,IdString(name_r));
+}
 
-  // replace all "*" in input with ".*"
-  regexed = regex_replace(str, all, r_all);
-  MIL << "wildcards2regex: " << str << " -> " << regexed;
+void Locks::addLock( const ResKind& kind_r, const IdString& name_r )
+{
+  PoolQuery q;
+  q.addAttribute( sat::SolvAttr::name,name_r.asString() );
+  q.addKind( kind_r );
+  q.setMatchExact();
+  q.setCaseSensitive(true);
+  DBG << "add lock by identifier" << endl;
+  addLock( q );
+}
 
-  // replace all "?" in input with "."
-  regexed = regex_replace(regexed, one, r_one);
-  MIL << " -> " << regexed << endl;
+void Locks::removeLock( const PoolQuery& query )
+{
+  MIL << "remove lock" << endl;
+  for_( it,query.begin(),query.end() )
+  {
+    PoolItem item(*it);
+    item.status().setLock(false,ResStatus::USER);
+  }
+  
+  LockList::iterator i = find(_pimpl->toAdd.begin(),
+    _pimpl->toAdd.end(), query);
+  if ( i != _pimpl->toAdd.end() )
+  {
+    DBG << "query removed from added" << endl;
+    _pimpl->toAdd.erase(i);
+  }
+  else
+  {
+    DBG << "needed remove some old lock" << endl;
+    _pimpl->toRemove.push_back( query );
+  }
+}
 
-  return regexed;
+void Locks::removeLock( const IdString& ident_r )
+{
+  sat::Solvable::SplitIdent id(ident_r);
+  removeLock(id.kind(),id.name());
 }
 
+void Locks::removeLock( const ResKind& kind_r, const C_Str & name_r )
+{
+  removeLock(kind_r,IdString(name_r));
+}
 
-//
-// assign Lock to installed pool item
-//
+void Locks::removeLock( const ResKind &kind_r, const IdString &name_r )
+{
+  PoolQuery q;
+  q.addAttribute( sat::SolvAttr::name,name_r.asString() );
+  q.addKind( kind_r );
+  q.setMatchExact();
+  q.setCaseSensitive(true);
+  q.requireAll();
+  DBG << "remove lock by selectactable" << endl;
+  removeLock(q);
+}
 
-struct ItemLockerFunc
+bool Locks::existEmpty() const
 {
-  ItemLockerFunc( const string lock_str )
-    : _lock_str(lock_str)
-  {}
+  for_( it, _pimpl->locks.begin(), _pimpl->locks.end() )
+  {
+    if( it->empty() )
+      return true;
+  }
+
+  return false;
+}
+
+//handle locks during removing
+class LocksCleanPredicate{
+private:
+  bool skip_rest;
+  size_t searched;
+  size_t all;
+  callback::SendReport<CleanEmptyLocksReport> &report;
+
+public:
+  LocksCleanPredicate(size_t count, callback::SendReport<CleanEmptyLocksReport> &_report): skip_rest(false),searched(0),all(count), report(_report){}
 
-  bool operator()( const CapAndItem &cai_r )
+  bool aborted(){ return skip_rest; }
+
+  bool operator()(PoolQuery& q)
   {
-    PoolItem_Ref item(cai_r.item);
-    MIL << "Locking " << cai_r.item << "(matched by " << _lock_str << ")" << endl;
-    item.status().setLock( true, ResStatus::USER);
-    return true;
+    if( skip_rest )
+      return false;
+    searched++;
+    if( !q.empty() )
+      return false;
+
+    if (!report->progress((100*searched)/all))
+    {
+      skip_rest = true;
+      return false;
+    }
+
+    switch (report->execute(q))
+    {
+    case CleanEmptyLocksReport::ABORT:
+      report->finish(CleanEmptyLocksReport::ABORTED);
+      skip_rest = true;
+      return false;
+    case CleanEmptyLocksReport::DELETE:
+      return true;
+    case CleanEmptyLocksReport::IGNORE:
+      return false;
+    default:
+      WAR << "Unknown returned value. Callback have more value then"
+          << " this switch. Need correct handle all enum values." << std::endl;
+    }
+
+    return false;
   }
 
-  string _lock_str;
 };
 
-struct AddLockToPool
+void Locks::removeEmpty()
 {
-  AddLockToPool( const ResPool &pool )
-  : _pool(pool)
-  , _count(0)
+  MIL << "cleaning of locks" << endl;
+  callback::SendReport<CleanEmptyLocksReport> report;
+  report->start();
+  size_t sum = _pimpl->locks.size();
+  LocksCleanPredicate p(sum, report);
+
+  _pimpl->locks.remove_if(p);
+
+  if( p.aborted() )
   {
-  
+    MIL << "cleaning aborted" << endl;
+    report->finish(CleanEmptyLocksReport::ABORTED);
   }
-  
-  bool operator()( const std::string & str_r )
+  else 
   {
-    CapFactory cap_factory;
-    
-    std::string line( str::trim( str_r ) );
-    
-    if ( line.empty() || line[0] == '#')
-      return true;
-    
-    MIL << "Applying locks from pattern '" << str_r << "'" << endl;
-    
-    // zypp does not provide wildcard or regex support in the Capability matching helpers
-    // but it still parses the index if it contains wildcards.
-    // so we decompose the capability, and keep the op and edition, while, the name
-    // is converted to a regexp and matched against all possible names in the _pool
-    // Then these names are combined with the original edition and relation and we
-    // got a new capability for matching wildcard to use with the capability match
-    // helpers
-
-    Rel rel;
-    Edition edition;
-    string name;
-
-    try
+    report->finish(CleanEmptyLocksReport::NO_ERROR);
+
+  }
+
+  if ( sum != _pimpl->locks.size() ) //some locks has been removed
+    _pimpl->locksDirty = true;
+}
+
+class LocksRemovePredicate
+{
+private:
+  std::set<sat::Solvable>& solvs;
+  const PoolQuery& query;
+  callback::SendReport<SavingLocksReport>& report;
+  bool aborted_;
+
+  //1 for subset of set, 2 only intersect, 0 for not intersect
+  int contains(const PoolQuery& q, std::set<sat::Solvable>& s)
+  {
+    bool intersect = false;
+    for_( it,q.begin(),q.end() )
     {
-      Capability capability = cap_factory.parse( ResTraits<zypp::Package>::kind, line );
-      
-      capability::NamedCap::constPtr named = capability::asKind<capability::NamedCap>(capability);
-      if ( named )
+      if ( s.find(*it)!=s.end() )
       {
-        rel = named->op();
-        edition = named->edition();
-        name = named->index();
+        intersect = true;
       }
       else
       {
-        ERR << "Not a named capability in: '" << line << "' skipping" << std::endl;
-        return true;
+        if (intersect)
+          return 2;
       }
     }
-    catch ( const Exception &e )
-    {
-      ERR << "Can't parse capability in: '" << line << "' (" << e.msg() << ") skipping" << std::endl;
-      return true;
-    }
-
-    // Operator NONE is not allowed in Capability
-    if (rel == Rel::NONE) rel = Rel::ANY;
+    return intersect ? 1 : 0;
+  }
 
-    NameMatchCollectorFunc nameMatchFunc;
+public:
+  LocksRemovePredicate(std::set<sat::Solvable>& s, const PoolQuery& q,
+      callback::SendReport<SavingLocksReport>& r)
+      : solvs(s), query(q),report(r),aborted_(false) {}
 
-    // regex flags
-    unsigned int flags = regex::normal;
-    flags |= regex_constants::icase;
-    regex reg;
+  bool operator()(const PoolQuery& q)
+  {
+    if (aborted())
+      return false;
+    if( q==query )
+    {//identical
+      DBG << "identical queries" << endl;
+      return true;
+    }
 
-    // create regex object
-    string regstr( wildcards2regex( name ) );
-    MIL << "regstr '" << regstr << "'" << endl;
-    try
+    SavingLocksReport::ConflictState cs;
+    switch( contains(q,solvs) )
     {
-      reg.assign( regstr, flags );
+    case 0:
+      return false; //another lock
+    case 1:
+      cs = SavingLocksReport::SAME_RESULTS;
+      break;
+    case 2:
+      cs = SavingLocksReport::INTERSECT;
+      break;
+    default:
+      return true;
     }
-    catch (regex_error & e)
+    MIL << "find conflict: " << cs << endl;
+    switch (report->conflict(q,cs))
     {
-      ERR << "locks: " << regstr << " is not a valid regular expression: \"" << e.what() << "\"" << endl;
-      ERR << "This is a bug, please file a bug report against libzypp-zmd-backend" << endl;
-      // ignore this lock and continue
-      return true;;
+    case SavingLocksReport::ABORT:
+      aborted_ = true;
+      DBG << "abort merging" << endl;
+      return false;
+    case SavingLocksReport::DELETE:
+      DBG << "force delete" << endl;
+      return true;
+    case SavingLocksReport::IGNORE:
+      DBG << "skip lock" << endl;
+      return false;
     }
+    WAR << "should not reached, some state is missing" << endl;
+    return false;
+  }
 
-    invokeOnEach( _pool.begin(), _pool.end(), Match(reg), functor::functorRef<bool, const PoolItem &>(nameMatchFunc) );
+  bool aborted(){ return aborted_; }
+};
 
-    MIL << "Found " << nameMatchFunc.matches.size() << " matches." << endl;
+bool Locks::Impl::mergeList(callback::SendReport<SavingLocksReport>& report)
+{
+  MIL << "merging list old: " << locks.size()
+    << " to add: " << toAdd.size() << "to remove: " << toRemove.size() << endl;
+  for_(it,toRemove.begin(),toRemove.end())
+  {
+    std::set<sat::Solvable> s(it->begin(),it->end());
+    locks.remove_if(LocksRemovePredicate(s,*it, report));
+  }
 
-    // now we have all the names matching
+  if (!report->progress())
+    return false;
 
-    // for each name matching try to match a capability
+  for_( it, toAdd.begin(), toAdd.end() )
+  {
+    if( std::find( locks.begin(), locks.end(), *it ) == locks.end() )
+      locks.push_back( *it );
+  }
 
-    ItemLockerFunc lockItemFunc( line );
+  toAdd.clear();
+  toRemove.clear();
 
-    for ( set<string>::const_iterator it = nameMatchFunc.matches.begin(); it != nameMatchFunc.matches.end(); ++it )
-    {
-      string matched_name = *it;
+  return true;
+}
 
-      try
-      {
-        Capability capability = cap_factory.parse( ResTraits<zypp::Package>::kind, matched_name, rel, edition );
-        MIL << "Locking capability " << capability << endl;
-        forEachMatchIn( _pool, Dep::PROVIDES, capability, functor::functorRef<bool, const CapAndItem &>(lockItemFunc) );
-      }
-      catch ( const Exception &e )
-      {
-        ERR << "Invalid lock: " << e.msg() << std::endl;
-      }
-      ++_count;
+void Locks::merge()
+{
+  if( (_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
+  {
+    return; //nothing to merge
+  }
+
+  callback::SendReport<SavingLocksReport> report;
+  report->start();
+  if (!_pimpl->mergeList(report))
+  {
+    report->finish(SavingLocksReport::ABORTED);
+    return;
+  }
+  DBG << "locks merged" << endl;
+  report->finish(SavingLocksReport::NO_ERROR);
+  _pimpl->locksDirty = true;
+}
+
+void Locks::save( const Pathname& file )
+{
+  if( ((_pimpl->toAdd.size() | _pimpl->toRemove.size())==0)
+      && !_pimpl->locksDirty )
+  {
+    DBG << "nothing changed in locks - no write to file" << endl;
+    return;
+  }
+
+  callback::SendReport<SavingLocksReport> report;
+  report->start();
+
+  if ((_pimpl->toAdd.size() | _pimpl->toRemove.size())!=0)
+  {
+    if (!_pimpl->mergeList(report))
+    {
+      report->finish(SavingLocksReport::ABORTED);
+      return;
     }
-    return true;
-  } // end operator()()
-        
-  ResPool _pool;
-  int _count;
-};
+  }
+
+  DBG << "writed "<< _pimpl->locks.size() << "locks" << endl;
+  writePoolQueriesToFile( file, _pimpl->locks.begin(), _pimpl->locks.end() );
+  report->finish(SavingLocksReport::NO_ERROR);
+}
 
-//
-// read 'locks' table, evaluate 'glob' column, assign locks to pool
-//
-int
-readLocks(const ResPool & pool, const Pathname &file )
+void Locks::removeDuplicates()
 {
-  PathInfo lockrc( file );
-  if ( lockrc.isFile() )
+  size_type sum = size();
+  for_(it,_pimpl->locks.begin(),_pimpl->locks.end())
   {
-    MIL << "Reading " << lockrc << endl;
-    ifstream inp( file.c_str() );
-    AddLockToPool addlock(pool);
-    iostr::forEachLine( inp, addlock);
-    MIL << addlock._count << " locks." << endl;
-    return addlock._count;
+    if ( find(_pimpl->locks.begin(),it,*it) != it )
+      _pimpl->locks.erase(it--); //-- to avoid using break iterator
   }
-  return 0;
+  
+  if (sum!=size())
+    _pimpl->locksDirty = true;
 }
 
-} // ns locks
 } // ns zypp