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