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