libzypp  11.13.5
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 
19 #include "zypp/base/Logger.h"
20 #include "zypp/base/Gettext.h"
21 #include "zypp/base/IOStream.h"
22 #include "zypp/base/Functional.h"
23 #include "zypp/PathInfo.h"
24 
25 #include "zypp/ZYppFactory.h"
28 
29 #include <boost/interprocess/sync/file_lock.hpp>
30 #include <boost/interprocess/sync/scoped_lock.hpp>
31 #include <boost/interprocess/sync/sharable_lock.hpp>
32 
33 using boost::interprocess::file_lock;
34 using boost::interprocess::scoped_lock;
35 using boost::interprocess::sharable_lock;
36 
37 using std::endl;
38 
40 namespace zypp
41 {
42 
43  namespace env
44  {
46  inline Pathname ZYPP_LOCKFILE_ROOT()
47  { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
48  }
49 
51  namespace zypp_readonly_hack
52  {
53 
54  static bool active = false;
55 
56  void IWantIt()
57  {
58  active = true;
59  MIL << "ZYPP_READONLY promised." << endl;
60  }
61 
62  bool IGotIt()
63  {
64  return active;
65  }
66 
68  } // namespace zypp_readonly_hack
70 
77  {
78  public:
80  : _cleanLock( false )
81  , _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
82  , _zyppLockFile( NULL )
83  , _lockerPid( 0 )
84  {
86  }
87 
89  {
90  if ( _cleanLock )
91  try {
92  // Exception safe access to the lockfile.
93  ScopedGuard closeOnReturn( accessLockFile() );
94  {
95  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
96  // Truncate the file rather than deleting it. Other processes may
97  // still use it to synchronsize.
98  ftruncate( fileno(_zyppLockFile), 0 );
99  }
100  MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
101  }
102  catch(...) {} // let no exception escape.
103  }
104 
105  pid_t lockerPid() const
106  { return _lockerPid; }
107 
108  const std::string & lockerName() const
109  { return _lockerName; }
110 
111  const Pathname & zyppLockFilePath() const
112  { return _zyppLockFilePath; }
113 
114 
115  private:
117  file_lock _zyppLockFileLock;
119 
120  pid_t _lockerPid;
121  std::string _lockerName;
123 
124  private:
125  typedef shared_ptr<void> ScopedGuard;
126 
134  {
135  _openLockFile();
136  return ScopedGuard( static_cast<void*>(0),
137  bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
138  }
139 
142  {
143  if ( _zyppLockFile != NULL )
144  return; // is open
145 
146  // open pid file rw so we are sure it exist when creating the flock
147  _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
148  if ( _zyppLockFile == NULL )
149  ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
151  MIL << "Open lockfile " << _zyppLockFilePath << endl;
152  }
153 
156  {
157  if ( _zyppLockFile == NULL )
158  return; // is closed
159 
160  clearerr( _zyppLockFile );
161  fflush( _zyppLockFile );
162  // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
163  // If you are using a std::fstream/native file handle to write to the file
164  // while using file locks on that file, don't close the file before releasing
165  // all the locks of the file.
166  _zyppLockFileLock = file_lock();
167  fclose( _zyppLockFile );
168  _zyppLockFile = NULL;
169  MIL << "Close lockfile " << _zyppLockFilePath << endl;
170  }
171 
172 
173  bool isProcessRunning( pid_t pid_r )
174  {
175  // it is another program, not me, see if it is still running
176  Pathname procdir( "/proc"/str::numstring(pid_r) );
177  PathInfo status( procdir );
178  MIL << "Checking " << status << endl;
179 
180  if ( ! status.isDir() )
181  {
182  DBG << "No such process." << endl;
183  return false;
184  }
185 
186  static char buffer[513];
187  buffer[0] = buffer[512] = 0;
188  // man proc(5): /proc/[pid]/cmdline is empty if zombie.
189  if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
190  {
191  _lockerName = buffer;
192  DBG << "Is running: " << _lockerName << endl;
193  return true;
194  }
195 
196  DBG << "In zombie state." << endl;
197  return false;
198  }
199 
200  pid_t readLockFile()
201  {
202  clearerr( _zyppLockFile );
203  fseek( _zyppLockFile, 0, SEEK_SET );
204  long readpid = 0;
205  fscanf( _zyppLockFile, "%ld", &readpid );
206  MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
207  return (pid_t)readpid;
208  }
209 
211  {
212  clearerr( _zyppLockFile );
213  fseek( _zyppLockFile, 0, SEEK_SET );
214  ftruncate( fileno(_zyppLockFile), 0 );
215  fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
216  fflush( _zyppLockFile );
217  _cleanLock = true; // cleanup on exit
218  MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
219  }
220 
221  public:
222 
226  bool zyppLocked()
227  {
228  if ( geteuid() != 0 )
229  return false; // no lock as non-root
230 
231  // Exception safe access to the lockfile.
232  ScopedGuard closeOnReturn( accessLockFile() );
233  {
234  scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
235 
237  if ( _lockerPid == 0 )
238  {
239  // no or empty lock file
240  writeLockFile();
241  return false;
242  }
243  else if ( _lockerPid == getpid() )
244  {
245  // keep my own lock
246  return false;
247  }
248  else
249  {
250  // a foreign pid in lock
251  if ( isProcessRunning( _lockerPid ) )
252  {
253  WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
254  return true;
255  }
256  else
257  {
258  MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
259  writeLockFile();
260  return false;
261  }
262  }
263  }
264  INT << "Oops! We should not be here!" << std::endl;
265  return true;
266  }
267 
268  };
269 
270  namespace
271  {
272  static ZYppGlobalLock & globalLock()
273  {
274  static ZYppGlobalLock lock;
275  return lock;
276  }
277  bool _haveZYpp = false;
278  }
279 
281  //
282  // CLASS NAME : ZYppFactoryException
283  //
285 
286  ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
287  : Exception( msg_r )
288  , _lockerPid( lockerPid_r )
289  , _lockerName( lockerName_r )
290  {}
291 
293  {}
294 
296  //
297  // CLASS NAME : ZYppFactory
298  //
300 
302  //
303  // METHOD NAME : ZYppFactory::instance
304  // METHOD TYPE : ZYppFactory
305  //
307  {
308  return ZYppFactory();
309  }
310 
312  //
313  // METHOD NAME : ZYppFactory::ZYppFactory
314  // METHOD TYPE : Ctor
315  //
317  {
318 
319  }
320 
322  //
323  // METHOD NAME : ZYppFactory::~ZYppFactory
324  // METHOD TYPE : Dtor
325  //
327  {}
328 
330  //
332  {
333  static ZYpp::Ptr _instance;
334 
335  if ( ! _instance )
336  {
337  if ( geteuid() != 0 )
338  {
339  MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
340  }
341  else if ( zypp_readonly_hack::active )
342  {
343  MIL << "ZYPP_READONLY active." << endl;
344  }
345  else if ( globalLock().zyppLocked() )
346  {
347  bool failed = true;
348  const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
349  if ( LOCK_TIMEOUT > 0 )
350  {
351  MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
352  unsigned delay = 1;
353  Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
354  for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
355  {
356  if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
357  sleep( delay );
358  else
359  {
360  MIL << "Retry after " << i << " sec." << endl;
361  failed = globalLock().zyppLocked();
362  if ( failed )
363  {
364  // another proc locked faster. maybe it ends fast as well....
365  MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
366  procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
367  }
368  else
369  {
370  MIL << "Finally got the lock!" << endl;
371  break; // gotcha
372  }
373  }
374  }
375 
376  }
377  if ( failed )
378  {
379  std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
380  "Close this application before trying again."),
381  globalLock().lockerPid(),
382  globalLock().lockerName().c_str()
383  );
384  ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
385  }
386  }
387  // Here we go...
388  _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
389  if ( _instance )
390  _haveZYpp = true;
391  }
392 
393  return _instance;
394  }
395 
397  //
399  { return _haveZYpp; }
400 
401  /******************************************************************
402  **
403  ** FUNCTION NAME : operator<<
404  ** FUNCTION TYPE : std::ostream &
405  */
406  std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
407  {
408  return str << "ZYppFactory";
409  }
410 
412 } // namespace zypp