libzypp  17.5.0
ZYppFactory.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 extern "C"
13 {
14 #include <sys/file.h>
15 }
16 #include <iostream>
17 #include <fstream>
18 #include <signal.h>
19 
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/Gettext.h"
22 #include "zypp/base/IOStream.h"
23 #include "zypp/base/Functional.h"
24 #include "zypp/base/Backtrace.h"
25 #include "zypp/PathInfo.h"
26 
27 #include "zypp/ZYppFactory.h"
30 
31 #include <boost/interprocess/sync/file_lock.hpp>
32 #include <boost/interprocess/sync/scoped_lock.hpp>
33 #include <boost/interprocess/sync/sharable_lock.hpp>
34 
35 using boost::interprocess::file_lock;
36 using boost::interprocess::scoped_lock;
37 using boost::interprocess::sharable_lock;
38 
39 using std::endl;
40 
41 namespace zyppintern { void repoVariablesReset(); } // upon re-acquiring the lock...
42 
44 namespace zypp
45 {
46 
47  namespace
48  {
49  void sigsegvHandler( int sig );
50  ::sighandler_t lastSigsegvHandler = ::signal( SIGSEGV, sigsegvHandler );
51 
53  void sigsegvHandler( int sig )
54  {
55  INT << "Error: signal " << sig << endl << dumpBacktrace << endl;
56  ::signal( SIGSEGV, lastSigsegvHandler );
57  }
58  }
59 
60  namespace env
61  {
64  { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
65  }
66 
68  namespace zypp_readonly_hack
69  {
70 
71  static bool active = getenv("ZYPP_READONLY_HACK");
72 
73  void IWantIt()
74  {
75  active = true;
76  MIL << "ZYPP_READONLY promised." << endl;
77  }
78 
79  bool IGotIt()
80  {
81  return active;
82  }
83 
85  } // namespace zypp_readonly_hack
87 
94  {
95  public:
97  : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
98  , _zyppLockFile( NULL )
99  , _lockerPid( 0 )
100  , _cleanLock( false )
101  {
103  }
104 
106  {
107  if ( _cleanLock )
108  try {
109  // Exception safe access to the lockfile.
110  ScopedGuard closeOnReturn( accessLockFile() );
111  {
112  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
113  // Truncate the file rather than deleting it. Other processes may
114  // still use it to synchronsize.
115  ftruncate( fileno(_zyppLockFile), 0 );
116  }
117  MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
118  }
119  catch(...) {} // let no exception escape.
120  }
121 
122  pid_t lockerPid() const
123  { return _lockerPid; }
124 
125  const std::string & lockerName() const
126  { return _lockerName; }
127 
128  const Pathname & zyppLockFilePath() const
129  { return _zyppLockFilePath; }
130 
131 
132  private:
134  file_lock _zyppLockFileLock;
136 
137  pid_t _lockerPid;
138  std::string _lockerName;
140 
141  private:
142  typedef shared_ptr<void> ScopedGuard;
143 
151  {
152  _openLockFile();
153  return ScopedGuard( static_cast<void*>(0),
154  bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
155  }
156 
159  {
160  if ( _zyppLockFile != NULL )
161  return; // is open
162 
163  // open pid file rw so we are sure it exist when creating the flock
164  _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
165  if ( _zyppLockFile == NULL )
166  ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
168  MIL << "Open lockfile " << _zyppLockFilePath << endl;
169  }
170 
173  {
174  if ( _zyppLockFile == NULL )
175  return; // is closed
176 
177  clearerr( _zyppLockFile );
178  fflush( _zyppLockFile );
179  // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
180  // If you are using a std::fstream/native file handle to write to the file
181  // while using file locks on that file, don't close the file before releasing
182  // all the locks of the file.
183  _zyppLockFileLock = file_lock();
184  fclose( _zyppLockFile );
185  _zyppLockFile = NULL;
186  MIL << "Close lockfile " << _zyppLockFilePath << endl;
187  }
188 
189 
190  bool isProcessRunning( pid_t pid_r )
191  {
192  // it is another program, not me, see if it is still running
193  Pathname procdir( "/proc"/str::numstring(pid_r) );
194  PathInfo status( procdir );
195  MIL << "Checking " << status << endl;
196 
197  if ( ! status.isDir() )
198  {
199  DBG << "No such process." << endl;
200  return false;
201  }
202 
203  static char buffer[513];
204  buffer[0] = buffer[512] = 0;
205  // man proc(5): /proc/[pid]/cmdline is empty if zombie.
206  if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
207  {
208  _lockerName = buffer;
209  DBG << "Is running: " << _lockerName << endl;
210  return true;
211  }
212 
213  DBG << "In zombie state." << endl;
214  return false;
215  }
216 
217  pid_t readLockFile()
218  {
219  clearerr( _zyppLockFile );
220  fseek( _zyppLockFile, 0, SEEK_SET );
221  long readpid = 0;
222  fscanf( _zyppLockFile, "%ld", &readpid );
223  MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
224  return (pid_t)readpid;
225  }
226 
228  {
229  clearerr( _zyppLockFile );
230  fseek( _zyppLockFile, 0, SEEK_SET );
231  ftruncate( fileno(_zyppLockFile), 0 );
232  fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
233  fflush( _zyppLockFile );
234  _cleanLock = true; // cleanup on exit
235  MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
236  }
237 
238  public:
239 
243  bool zyppLocked()
244  {
245  if ( geteuid() != 0 )
246  return false; // no lock as non-root
247 
248  // Exception safe access to the lockfile.
249  ScopedGuard closeOnReturn( accessLockFile() );
250  {
251  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
252 
254  if ( _lockerPid == 0 )
255  {
256  // no or empty lock file
257  writeLockFile();
258  return false;
259  }
260  else if ( _lockerPid == getpid() )
261  {
262  // keep my own lock
263  return false;
264  }
265  else
266  {
267  // a foreign pid in lock
268  if ( isProcessRunning( _lockerPid ) )
269  {
270  WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
271  return true;
272  }
273  else
274  {
275  MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
276  writeLockFile();
277  return false;
278  }
279  }
280  }
281  INT << "Oops! We should not be here!" << std::endl;
282  return true;
283  }
284 
285  };
286 
288  namespace
289  {
290  static weak_ptr<ZYpp> _theZYppInstance;
291  static scoped_ptr<ZYppGlobalLock> _theGlobalLock; // on/off in sync with _theZYppInstance
292 
293  ZYppGlobalLock & globalLock()
294  {
295  if ( !_theGlobalLock )
296  _theGlobalLock.reset( new ZYppGlobalLock );
297  return *_theGlobalLock;
298  }
299  } //namespace
301 
303  //
304  // CLASS NAME : ZYpp
305  //
307 
308  ZYpp::ZYpp( const Impl_Ptr & impl_r )
309  : _pimpl( impl_r )
310  {
311  ::zyppintern::repoVariablesReset(); // upon re-acquiring the lock...
312  MIL << "ZYpp is on..." << endl;
313  }
314 
316  {
317  _theGlobalLock.reset();
318  MIL << "ZYpp is off..." << endl;
319  }
320 
322  //
323  // CLASS NAME : ZYppFactoryException
324  //
326 
327  ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
328  : Exception( msg_r )
329  , _lockerPid( lockerPid_r )
330  , _lockerName( lockerName_r )
331  {}
332 
334  {}
335 
337  //
338  // CLASS NAME : ZYppFactory
339  //
341 
343  { return ZYppFactory(); }
344 
346  {}
347 
349  {}
350 
352  //
354  {
355  ZYpp::Ptr _instance = _theZYppInstance.lock();
356  if ( ! _instance )
357  {
358  if ( geteuid() != 0 )
359  {
360  MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
361  }
362  else if ( zypp_readonly_hack::active )
363  {
364  MIL << "ZYPP_READONLY active." << endl;
365  }
366  else if ( globalLock().zyppLocked() )
367  {
368  bool failed = true;
369  const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
370  if ( LOCK_TIMEOUT > 0 )
371  {
372  MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
373  unsigned delay = 1;
374  Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
375  for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
376  {
377  if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
378  sleep( delay );
379  else
380  {
381  MIL << "Retry after " << i << " sec." << endl;
382  failed = globalLock().zyppLocked();
383  if ( failed )
384  {
385  // another proc locked faster. maybe it ends fast as well....
386  MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
387  procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
388  }
389  else
390  {
391  MIL << "Finally got the lock!" << endl;
392  break; // gotcha
393  }
394  }
395  }
396  }
397  if ( failed )
398  {
399  std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
400  "Close this application before trying again."),
401  globalLock().lockerPid(),
402  globalLock().lockerName().c_str()
403  );
404  ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
405  }
406  }
407  // Here we go...
408  static ZYpp::Impl_Ptr _theImplInstance; // for now created once
409  if ( !_theImplInstance )
410  _theImplInstance.reset( new ZYpp::Impl );
411  _instance.reset( new ZYpp( _theImplInstance ) );
412  _theZYppInstance = _instance;
413  }
414 
415  return _instance;
416  }
417 
419  //
421  { return !_theZYppInstance.expired(); }
422 
423  /******************************************************************
424  **
425  ** FUNCTION NAME : operator<<
426  ** FUNCTION TYPE : std::ostream &
427  */
428  std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
429  {
430  return str << "ZYppFactory";
431  }
432 
434 } // namespace zypp
ScopedGuard accessLockFile()
Exception safe access to the lockfile.
Definition: ZYppFactory.cc:150
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:320
Interface to gettext.
#define MIL
Definition: Logger.h:64
std::ostream & dumpBacktrace(std::ostream &stream_r)
Dump current stack trace to a stream.
Definition: Backtrace.cc:24
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
bool haveZYpp() const
Whether the ZYpp instance is already created.
Definition: ZYppFactory.cc:420
~ZYpp()
Dtor.
Definition: ZYppFactory.cc:315
Our broken global lock.
Definition: ZYppFactory.cc:93
void IWantIt() ZYPP_DEPRECATED
Definition: ZYppFactory.cc:73
#define INT
Definition: Logger.h:68
const char * c_str() const
String representation.
Definition: Pathname.h:109
const std::string & lockerName() const
Definition: ZYppFactory.cc:125
String related utilities and Regular expression matching.
Pathname ZYPP_LOCKFILE_ROOT()
Hack to circumvent the currently poor –root support.
Definition: ZYppFactory.cc:63
shared_ptr< Impl > Impl_Ptr
Definition: ZYpp.h:147
static ZYppFactory instance()
Singleton ctor.
Definition: ZYppFactory.cc:342
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
ZYpp(const Impl_Ptr &impl_r)
Factory ctor.
Definition: ZYppFactory.cc:308
bool isProcessRunning(pid_t pid_r)
Definition: ZYppFactory.cc:190
Pathname _zyppLockFilePath
Definition: ZYppFactory.cc:133
pid_t lockerPid() const
Definition: ZYppFactory.cc:122
std::ostream & operator<<(std::ostream &str, const Exception &obj)
Definition: Exception.cc:147
std::string _lockerName
Definition: ZYppFactory.cc:138
const std::string & asString() const
String representation.
Definition: Pathname.h:90
shared_ptr< void > ScopedGuard
Definition: ZYppFactory.cc:142
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:123
::boost::shared_ptr< ZYpp > Ptr
Definition: ZYpp.h:60
#define WAR
Definition: Logger.h:65
ZYpp::Ptr getZYpp() const
Definition: ZYppFactory.cc:353
ZYppFactoryException(const std::string &msg_r, pid_t lockerPid_r, const std::string &lockerName_r)
Definition: ZYppFactory.cc:327
ZYpp factory class (Singleton)
Definition: ZYppFactory.h:43
#define _(MSG)
Definition: Gettext.h:29
ZYppFactory()
Default ctor.
Definition: ZYppFactory.cc:345
const Pathname & zyppLockFilePath() const
Definition: ZYppFactory.cc:128
std::string numstring(char n, int w=0)
Definition: String.h:288
void _closeLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:172
Base class for Exception.
Definition: Exception.h:145
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
bool zyppLocked()
Try to aquire a lock.
Definition: ZYppFactory.cc:243
void _openLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:158
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
#define DBG
Definition: Logger.h:63
void repoVariablesReset()
file_lock _zyppLockFileLock
Definition: ZYppFactory.cc:134