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