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