libzypp  17.31.31
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 sighandler
51  {
53  template <int SIG>
55  {
56  static void backtraceHandler( int sig ) {
57  INT << "Error: signal " << SIG << endl << dumpBacktrace << endl;
59  ::signal( SIG, lastSigHandler );
60  }
61  static ::sighandler_t lastSigHandler;
62  };
63  template <int SIG>
65 
66  // Explicit instantiation installs the handler:
67  template class SigBacktraceHandler<SIGSEGV>;
68  template class SigBacktraceHandler<SIGABRT>;
69  }
70 
71  namespace env
72  {
75  { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
76  }
77 
79  namespace zypp_readonly_hack
80  {
81 
82  static bool active = getenv("ZYPP_READONLY_HACK");
83 
84  void IWantIt() // see zypp/zypp_detail/ZYppReadOnlyHack.h
85  {
86  active = true;
87  MIL << "ZYPP_READONLY promised." << endl;
88  }
89 
90  bool IGotIt()
91  {
92  return active;
93  }
94 
96  } // namespace zypp_readonly_hack
98 
105  {
106  public:
107  ZYppGlobalLock( Pathname &&lFilePath )
108  : _zyppLockFilePath( std::move(lFilePath) )
109  , _zyppLockFile( NULL )
110  , _lockerPid( 0 )
111  , _cleanLock( false )
112  {
113  filesystem::assert_dir(_zyppLockFilePath.dirname() );
114  }
115 
117  {
118  if ( _cleanLock )
119  try {
120  // Exception safe access to the lockfile.
121  ScopedGuard closeOnReturn( accessLockFile() );
122  {
123  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
124  // Truncate the file rather than deleting it. Other processes may
125  // still use it to synchronsize.
126  ftruncate( fileno(_zyppLockFile), 0 );
127  }
128  MIL << "Cleaned lock file. (" << getpid() << ")" << std::endl;
129  }
130  catch(...) {} // let no exception escape.
131  }
132 
133  pid_t lockerPid() const
134  { return _lockerPid; }
135 
136  const std::string & lockerName() const
137  { return _lockerName; }
138 
139  const Pathname & zyppLockFilePath() const
140  { return _zyppLockFilePath; }
141 
142 
143  private:
145  file_lock _zyppLockFileLock;
147 
148  pid_t _lockerPid;
149  std::string _lockerName;
151 
152  private:
153  typedef shared_ptr<void> ScopedGuard;
154 
162  {
163  _openLockFile();
164  return ScopedGuard( static_cast<void*>(0),
165  std::bind( std::mem_fn( &ZYppGlobalLock::_closeLockFile ), this ) );
166  }
167 
170  {
171  if ( _zyppLockFile != NULL )
172  return; // is open
173 
174  // open pid file rw so we are sure it exist when creating the flock
175  _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
176  if ( _zyppLockFile == NULL )
177  ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
178  _zyppLockFileLock = _zyppLockFilePath.c_str();
179  MIL << "Open lockfile " << _zyppLockFilePath << endl;
180  }
181 
184  {
185  if ( _zyppLockFile == NULL )
186  return; // is closed
187 
188  clearerr( _zyppLockFile );
189  fflush( _zyppLockFile );
190  // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
191  // If you are using a std::fstream/native file handle to write to the file
192  // while using file locks on that file, don't close the file before releasing
193  // all the locks of the file.
194  _zyppLockFileLock = file_lock();
195  fclose( _zyppLockFile );
196  _zyppLockFile = NULL;
197  MIL << "Close lockfile " << _zyppLockFilePath << endl;
198  }
199 
200 
201  bool isProcessRunning( pid_t pid_r )
202  {
203  // it is another program, not me, see if it is still running
204  Pathname procdir( Pathname("/proc")/str::numstring(pid_r) );
205  PathInfo status( procdir );
206  MIL << "Checking " << status << endl;
207 
208  if ( ! status.isDir() )
209  {
210  DBG << "No such process." << endl;
211  return false;
212  }
213 
214  static char buffer[513];
215  buffer[0] = buffer[512] = 0;
216  // man proc(5): /proc/[pid]/cmdline is empty if zombie.
217  if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
218  {
219  _lockerName = buffer;
220  DBG << "Is running: " << _lockerName << endl;
221  return true;
222  }
223 
224  DBG << "In zombie state." << endl;
225  return false;
226  }
227 
228  pid_t readLockFile()
229  {
230  clearerr( _zyppLockFile );
231  fseek( _zyppLockFile, 0, SEEK_SET );
232  long readpid = 0;
233  fscanf( _zyppLockFile, "%ld", &readpid );
234  MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
235  return (pid_t)readpid;
236  }
237 
239  {
240  clearerr( _zyppLockFile );
241  fseek( _zyppLockFile, 0, SEEK_SET );
242  ftruncate( fileno(_zyppLockFile), 0 );
243  fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
244  fflush( _zyppLockFile );
245  _cleanLock = true; // cleanup on exit
246  MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
247  }
248 
253  {
254  _lockerPid = readLockFile();
255  if ( _lockerPid == 0 ) {
256  // no or empty lock file
257  return false;
258  } else if ( _lockerPid == getpid() ) {
259  // keep my own lock
260  return false;
261  } else {
262  // a foreign pid in lock
263  if ( isProcessRunning( _lockerPid ) ) {
264  WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
265  return true;
266  } else {
267  MIL << _lockerPid << " is dead. Ignoring the existing lock file." << std::endl;
268  return false;
269  }
270  }
271  }
272 
273  public:
274 
276  {
277  if ( geteuid() != 0 )
278  return false; // no lock as non-root
279 
280  // Exception safe access to the lockfile.
281  ScopedGuard closeOnReturn( accessLockFile() );
282  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
283  return safeCheckIsLocked ();
284  }
285 
289  bool zyppLocked()
290  {
291  if ( geteuid() != 0 )
292  return false; // no lock as non-root
293 
294  // Exception safe access to the lockfile.
295  ScopedGuard closeOnReturn( accessLockFile() );
296  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
297  if ( !safeCheckIsLocked() ) {
298  writeLockFile();
299  return false;
300  }
301  return true;
302  }
303 
304  };
305 
307  namespace
308  {
309  static weak_ptr<ZYpp> _theZYppInstance;
310  static scoped_ptr<ZYppGlobalLock> _theGlobalLock; // on/off in sync with _theZYppInstance
311 
312  ZYppGlobalLock & globalLock()
313  {
314  if ( !_theGlobalLock )
315  _theGlobalLock.reset( new ZYppGlobalLock( ZYppFactory::lockfileDir() / "zypp.pid" ) );
316  return *_theGlobalLock;
317  }
318  } //namespace
320 
322  //
323  // CLASS NAME : ZYpp
324  //
326 
327  ZYpp::ZYpp( const Impl_Ptr & impl_r )
328  : _pimpl( impl_r )
329  {
330  ::zyppintern::repoVariablesReset(); // upon re-acquiring the lock...
331  MIL << "ZYpp is on..." << endl;
332  }
333 
335  {
336  _theGlobalLock.reset();
337  MIL << "ZYpp is off..." << endl;
338  }
339 
341  //
342  // CLASS NAME : ZYppFactoryException
343  //
345 
346  ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
347  : Exception( msg_r )
348  , _lockerPid( lockerPid_r )
349  , _lockerName( lockerName_r )
350  {}
351 
353  {}
354 
356  //
357  // CLASS NAME : ZYppFactory
358  //
360 
362  { return ZYppFactory(); }
363 
365  {}
366 
368  {}
369 
371  //
373  {
374 
375  const auto &makeLockedError = []( pid_t pid, const std::string &lockerName ){
376  const std::string &t = str::form(_("System management is locked by the application with pid %d (%s).\n"
377  "Close this application before trying again."), pid, lockerName.c_str() );
378  return ZYppFactoryException(t, pid, lockerName );
379  };
380 
381  ZYpp::Ptr _instance = _theZYppInstance.lock();
382  if ( ! _instance )
383  {
384  if ( geteuid() != 0 )
385  {
386  MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
387  }
388  else if ( zypp_readonly_hack::active )
389  {
390  MIL << "ZYPP_READONLY active." << endl;
391  }
392  else if ( globalLock().zyppLocked() )
393  {
394  bool failed = true;
395  // bsc#1184399,1213231: A negative ZYPP_LOCK_TIMEOUT will wait forever.
396  const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
397  if ( LOCK_TIMEOUT != 0 )
398  {
399  Date logwait = Date::now();
400  Date giveup; /* 0 = forever */
401  if ( LOCK_TIMEOUT > 0 ) {
402  giveup = logwait+LOCK_TIMEOUT;
403  MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Waiting for the zypp lock until " << giveup << endl;
404  }
405  else
406  MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Waiting for the zypp lock..." << endl;
407 
408  unsigned delay = 0;
409  do {
410  if ( delay < 60 )
411  delay += 1;
412  else {
413  Date now { Date::now() };
414  if ( now - logwait > Date::day ) {
415  WAR << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Another day has passed waiting for the zypp lock..." << endl;
416  logwait = now;
417  }
418  }
419  sleep( delay );
420  {
421  zypp::base::LogControl::TmpLineWriter shutUp; // be quiet
422  failed = globalLock().zyppLocked();
423  }
424  } while ( failed && ( not giveup || Date::now() <= giveup ) );
425 
426  if ( failed ) {
427  MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Gave up waiting for the zypp lock." << endl;
428  }
429  else {
430  MIL << "$ZYPP_LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec. Finally got the zypp lock." << endl;
431  }
432  }
433  if ( failed )
434  ZYPP_THROW( makeLockedError( globalLock().lockerPid(), globalLock().lockerName() ));
435 
436  // we got the global lock, now make sure zypp-rpm is not still running
437  {
438  ZYppGlobalLock zyppRpmLock( ZYppFactory::lockfileDir() / "zypp-rpm.pid" );
439  if ( zyppRpmLock.isZyppLocked () ) {
440  // release global lock, we will exit now
441  _theGlobalLock.reset();
442  ZYPP_THROW( makeLockedError( zyppRpmLock.lockerPid(), zyppRpmLock.lockerName() ));
443  }
444  }
445  }
446 
447  // Here we go...
448  static ZYpp::Impl_Ptr _theImplInstance; // for now created once
449  if ( !_theImplInstance )
450  _theImplInstance.reset( new ZYpp::Impl );
451  _instance.reset( new ZYpp( _theImplInstance ) );
452  _theZYppInstance = _instance;
453  }
454 
455  return _instance;
456  }
457 
459  //
461  { return !_theZYppInstance.expired(); }
462 
464  {
465  return env::ZYPP_LOCKFILE_ROOT() / "run";
466  }
467 
468  /******************************************************************
469  **
470  ** FUNCTION NAME : operator<<
471  ** FUNCTION TYPE : std::ostream &
472  */
473  std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
474  {
475  return str << "ZYppFactory";
476  }
477 
479 } // namespace zypp
ScopedGuard accessLockFile()
Exception safe access to the lockfile.
Definition: ZYppFactory.cc:161
static const ValueType day
Definition: Date.h:44
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:460
~ZYpp()
Dtor.
Definition: ZYppFactory.cc:334
Our broken global lock.
Definition: ZYppFactory.cc:104
void IWantIt() ZYPP_DEPRECATED
Definition: ZYppFactory.cc:84
#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:136
String related utilities and Regular expression matching.
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
Definition: SerialNumber.cc:52
Definition: Arch.h:363
Pathname ZYPP_LOCKFILE_ROOT()
Hack to circumvent the currently poor –root support.
Definition: ZYppFactory.cc:74
shared_ptr< Impl > Impl_Ptr
Definition: ZYpp.h:161
static ZYppFactory instance()
Singleton ctor.
Definition: ZYppFactory.cc:361
Exchange LineWriter for the lifetime of this object.
Definition: LogControl.h:190
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:327
bool isProcessRunning(pid_t pid_r)
Definition: ZYppFactory.cc:201
static LogControl instance()
Singleton access.
Definition: LogControl.h:102
Pathname _zyppLockFilePath
Definition: ZYppFactory.cc:144
pid_t lockerPid() const
Definition: ZYppFactory.cc:133
Store and operate on date (time_t).
Definition: Date.h:32
std::string _lockerName
Definition: ZYppFactory.cc:149
const std::string & asString() const
String representation.
Definition: Pathname.h:91
shared_ptr< void > ScopedGuard
Definition: ZYppFactory.cc:153
::boost::shared_ptr< ZYpp > Ptr
Definition: ZYpp.h:63
#define WAR
Definition: Logger.h:97
ZYpp::Ptr getZYpp() const
Definition: ZYppFactory.cc:372
ZYppFactoryException(const std::string &msg_r, pid_t lockerPid_r, const std::string &lockerName_r)
Definition: ZYppFactory.cc:346
ZYpp factory class (Singleton)
Definition: ZYppFactory.h:43
ZYppFactory()
Default ctor.
Definition: ZYppFactory.cc:364
const Pathname & zyppLockFilePath() const
Definition: ZYppFactory.cc:139
ZYppGlobalLock(Pathname &&lFilePath)
Definition: ZYppFactory.cc:107
std::string numstring(char n, int w=0)
Definition: String.h:289
static void backtraceHandler(int sig)
Definition: ZYppFactory.cc:56
void _closeLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:183
Base class for Exception.
Definition: Exception.h:145
static Date now()
Return the current time.
Definition: Date.h:78
Signal handler logging a stack trace.
Definition: ZYppFactory.cc:54
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
bool zyppLocked()
Try to aquire a lock.
Definition: ZYppFactory.cc:289
static zypp::Pathname lockfileDir()
Definition: ZYppFactory.cc:463
void _openLockFile()
Use accessLockFile.
Definition: ZYppFactory.cc:169
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:145