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