libzypp  13.10.6
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 
42 namespace zypp
43 {
44 
45  namespace
46  {
47  void sigsegvHandler( int sig );
48  ::sighandler_t lastSigsegvHandler = ::signal( SIGSEGV, sigsegvHandler );
49 
51  void sigsegvHandler( int sig )
52  {
53  INT << "Error: signal " << sig << endl << dumpBacktrace << endl;
54  ::signal( SIGSEGV, lastSigsegvHandler );
55  }
56  }
57 
58  namespace env
59  {
61  inline Pathname ZYPP_LOCKFILE_ROOT()
62  { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
63  }
64 
66  namespace zypp_readonly_hack
67  {
68 
69  static bool active = getenv("ZYPP_READONLY_HACK");
70 
71  void IWantIt()
72  {
73  active = true;
74  MIL << "ZYPP_READONLY promised." << endl;
75  }
76 
77  bool IGotIt()
78  {
79  return active;
80  }
81 
83  } // namespace zypp_readonly_hack
85 
92  {
93  public:
95  : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
96  , _zyppLockFile( NULL )
97  , _lockerPid( 0 )
98  , _cleanLock( false )
99  {
101  }
102 
104  {
105  if ( _cleanLock )
106  try {
107  // Exception safe access to the lockfile.
108  ScopedGuard closeOnReturn( accessLockFile() );
109  {
110  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
111  // Truncate the file rather than deleting it. Other processes may
112  // still use it to synchronsize.
113  ftruncate( fileno(_zyppLockFile), 0 );
114  }
115  MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
116  }
117  catch(...) {} // let no exception escape.
118  }
119 
120  pid_t lockerPid() const
121  { return _lockerPid; }
122 
123  const std::string & lockerName() const
124  { return _lockerName; }
125 
126  const Pathname & zyppLockFilePath() const
127  { return _zyppLockFilePath; }
128 
129 
130  private:
132  file_lock _zyppLockFileLock;
134 
135  pid_t _lockerPid;
136  std::string _lockerName;
138 
139  private:
140  typedef shared_ptr<void> ScopedGuard;
141 
149  {
150  _openLockFile();
151  return ScopedGuard( static_cast<void*>(0),
152  bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
153  }
154 
157  {
158  if ( _zyppLockFile != NULL )
159  return; // is open
160 
161  // open pid file rw so we are sure it exist when creating the flock
162  _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
163  if ( _zyppLockFile == NULL )
164  ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
166  MIL << "Open lockfile " << _zyppLockFilePath << endl;
167  }
168 
171  {
172  if ( _zyppLockFile == NULL )
173  return; // is closed
174 
175  clearerr( _zyppLockFile );
176  fflush( _zyppLockFile );
177  // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
178  // If you are using a std::fstream/native file handle to write to the file
179  // while using file locks on that file, don't close the file before releasing
180  // all the locks of the file.
181  _zyppLockFileLock = file_lock();
182  fclose( _zyppLockFile );
183  _zyppLockFile = NULL;
184  MIL << "Close lockfile " << _zyppLockFilePath << endl;
185  }
186 
187 
188  bool isProcessRunning( pid_t pid_r )
189  {
190  // it is another program, not me, see if it is still running
191  Pathname procdir( "/proc"/str::numstring(pid_r) );
192  PathInfo status( procdir );
193  MIL << "Checking " << status << endl;
194 
195  if ( ! status.isDir() )
196  {
197  DBG << "No such process." << endl;
198  return false;
199  }
200 
201  static char buffer[513];
202  buffer[0] = buffer[512] = 0;
203  // man proc(5): /proc/[pid]/cmdline is empty if zombie.
204  if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
205  {
206  _lockerName = buffer;
207  DBG << "Is running: " << _lockerName << endl;
208  return true;
209  }
210 
211  DBG << "In zombie state." << endl;
212  return false;
213  }
214 
215  pid_t readLockFile()
216  {
217  clearerr( _zyppLockFile );
218  fseek( _zyppLockFile, 0, SEEK_SET );
219  long readpid = 0;
220  fscanf( _zyppLockFile, "%ld", &readpid );
221  MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
222  return (pid_t)readpid;
223  }
224 
226  {
227  clearerr( _zyppLockFile );
228  fseek( _zyppLockFile, 0, SEEK_SET );
229  ftruncate( fileno(_zyppLockFile), 0 );
230  fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
231  fflush( _zyppLockFile );
232  _cleanLock = true; // cleanup on exit
233  MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
234  }
235 
236  public:
237 
241  bool zyppLocked()
242  {
243  if ( geteuid() != 0 )
244  return false; // no lock as non-root
245 
246  // Exception safe access to the lockfile.
247  ScopedGuard closeOnReturn( accessLockFile() );
248  {
249  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
250 
252  if ( _lockerPid == 0 )
253  {
254  // no or empty lock file
255  writeLockFile();
256  return false;
257  }
258  else if ( _lockerPid == getpid() )
259  {
260  // keep my own lock
261  return false;
262  }
263  else
264  {
265  // a foreign pid in lock
266  if ( isProcessRunning( _lockerPid ) )
267  {
268  WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
269  return true;
270  }
271  else
272  {
273  MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
274  writeLockFile();
275  return false;
276  }
277  }
278  }
279  INT << "Oops! We should not be here!" << std::endl;
280  return true;
281  }
282 
283  };
284 
285  namespace
286  {
287  static ZYppGlobalLock & globalLock()
288  {
289  static ZYppGlobalLock lock;
290  return lock;
291  }
292  bool _haveZYpp = false;
293  }
294 
296  //
297  // CLASS NAME : ZYppFactoryException
298  //
300 
301  ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
302  : Exception( msg_r )
303  , _lockerPid( lockerPid_r )
304  , _lockerName( lockerName_r )
305  {}
306 
308  {}
309 
311  //
312  // CLASS NAME : ZYppFactory
313  //
315 
317  //
318  // METHOD NAME : ZYppFactory::instance
319  // METHOD TYPE : ZYppFactory
320  //
322  {
323  return ZYppFactory();
324  }
325 
327  //
328  // METHOD NAME : ZYppFactory::ZYppFactory
329  // METHOD TYPE : Ctor
330  //
332  {
333 
334  }
335 
337  //
338  // METHOD NAME : ZYppFactory::~ZYppFactory
339  // METHOD TYPE : Dtor
340  //
342  {}
343 
345  //
347  {
348  static ZYpp::Ptr _instance;
349 
350  if ( ! _instance )
351  {
352  if ( geteuid() != 0 )
353  {
354  MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
355  }
356  else if ( zypp_readonly_hack::active )
357  {
358  MIL << "ZYPP_READONLY active." << endl;
359  }
360  else if ( globalLock().zyppLocked() )
361  {
362  bool failed = true;
363  const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
364  if ( LOCK_TIMEOUT > 0 )
365  {
366  MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
367  unsigned delay = 1;
368  Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
369  for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
370  {
371  if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
372  sleep( delay );
373  else
374  {
375  MIL << "Retry after " << i << " sec." << endl;
376  failed = globalLock().zyppLocked();
377  if ( failed )
378  {
379  // another proc locked faster. maybe it ends fast as well....
380  MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
381  procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
382  }
383  else
384  {
385  MIL << "Finally got the lock!" << endl;
386  break; // gotcha
387  }
388  }
389  }
390 
391  }
392  if ( failed )
393  {
394  std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
395  "Close this application before trying again."),
396  globalLock().lockerPid(),
397  globalLock().lockerName().c_str()
398  );
399  ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
400  }
401  }
402  // Here we go...
403  _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
404  if ( _instance )
405  _haveZYpp = true;
406  }
407 
408  return _instance;
409  }
410 
412  //
414  { return _haveZYpp; }
415 
416  /******************************************************************
417  **
418  ** FUNCTION NAME : operator<<
419  ** FUNCTION TYPE : std::ostream &
420  */
421  std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
422  {
423  return str << "ZYppFactory";
424  }
425 
427 } // namespace zypp
ScopedGuard accessLockFile()
Exception safe access to the lockfile.
Definition: ZYppFactory.cc:148
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:324
bool haveZYpp() const
Whether the ZYpp instance is already created.
Definition: ZYppFactory.cc:413
Interface to gettext.
#define MIL
Definition: Logger.h:47
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:320
Our broken global lock.
Definition: ZYppFactory.cc:91
void IWantIt() ZYPP_DEPRECATED
Definition: ZYppFactory.cc:71
#define INT
Definition: Logger.h:51
Pathname ZYPP_LOCKFILE_ROOT()
Hack to circumvent the currently poor –root support.
Definition: ZYppFactory.cc:61
ZYpp::Ptr getZYpp() const
Definition: ZYppFactory.cc:346
shared_ptr< Impl > Impl_Ptr
Definition: ZYpp.h:153
static ZYppFactory instance()
Singleton ctor.
Definition: ZYppFactory.cc:321
bool isProcessRunning(pid_t pid_r)
Definition: ZYppFactory.cc:188
Pathname _zyppLockFilePath
Definition: ZYppFactory.cc:131
std::ostream & operator<<(std::ostream &str, const Exception &obj)
Definition: Exception.cc:120
std::string _lockerName
Definition: ZYppFactory.cc:136
shared_ptr< void > ScopedGuard
Definition: ZYppFactory.cc:140
#define WAR
Definition: Logger.h:48
ZYppFactoryException(const std::string &msg_r, pid_t lockerPid_r, const std::string &lockerName_r)
Definition: ZYppFactory.cc:301
ZYpp factory class (Singleton)
Definition: ZYppFactory.h:43
const Pathname & zyppLockFilePath() const
Definition: ZYppFactory.cc:126
pid_t lockerPid() const
Definition: ZYppFactory.cc:120
#define _(MSG)
Return translated text.
Definition: Gettext.h:21
ZYppFactory()
Default ctor.
Definition: ZYppFactory.cc:331
std::string numstring(char n, int w=0)
Definition: String.h:219
std::string form(const char *format,...)
Printf style construction of std::string.
Definition: String.cc:34
void _closeLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:170
Base class for Exception.
Definition: Exception.h:143
intrusive_ptr< ZYpp > Ptr
Definition: ZYpp.h:59
bool zyppLocked()
Try to aquire a lock.
Definition: ZYppFactory.cc:241
const std::string & lockerName() const
Definition: ZYppFactory.cc:123
void _openLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:156
#define DBG
Definition: Logger.h:46
file_lock _zyppLockFileLock
Definition: ZYppFactory.cc:132