1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/ZYppFactory.cc
20 #include "zypp/base/Logger.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"
27 #include "zypp/ZYppFactory.h"
28 #include "zypp/zypp_detail/ZYppImpl.h"
29 #include "zypp/zypp_detail/ZYppReadOnlyHack.h"
31 #include <boost/interprocess/sync/file_lock.hpp>
32 #include <boost/interprocess/sync/scoped_lock.hpp>
33 #include <boost/interprocess/sync/sharable_lock.hpp>
35 using boost::interprocess::file_lock;
36 using boost::interprocess::scoped_lock;
37 using boost::interprocess::sharable_lock;
41 ///////////////////////////////////////////////////////////////////
43 { /////////////////////////////////////////////////////////////////
47 void sigsegvHandler( int sig );
48 ::sighandler_t lastSigsegvHandler = ::signal( SIGSEGV, sigsegvHandler );
50 /** SIGSEGV handler to log stack trace */
51 void sigsegvHandler( int sig )
53 INT << "Error: signal " << sig << endl << dumpBacktrace << endl;
54 ::signal( SIGSEGV, lastSigsegvHandler );
60 /** Hack to circumvent the currently poor --root support. */
61 inline Pathname ZYPP_LOCKFILE_ROOT()
62 { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
65 ///////////////////////////////////////////////////////////////////
66 namespace zypp_readonly_hack
67 { /////////////////////////////////////////////////////////////////
69 static bool active = getenv("ZYPP_READONLY_HACK");
74 MIL << "ZYPP_READONLY promised." << endl;
77 /////////////////////////////////////////////////////////////////
78 } // namespace zypp_readonly_hack
79 ///////////////////////////////////////////////////////////////////
81 ///////////////////////////////////////////////////////////////////
82 /// \class ZYppGlobalLock
83 /// \brief Our broken global lock
85 ///////////////////////////////////////////////////////////////////
90 : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
91 , _zyppLockFile( NULL )
95 filesystem::assert_dir(_zyppLockFilePath.dirname() );
102 // Exception safe access to the lockfile.
103 ScopedGuard closeOnReturn( accessLockFile() );
105 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
106 // Truncate the file rather than deleting it. Other processes may
107 // still use it to synchronsize.
108 ftruncate( fileno(_zyppLockFile), 0 );
110 MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
112 catch(...) {} // let no exception escape.
115 pid_t lockerPid() const
116 { return _lockerPid; }
118 const std::string & lockerName() const
119 { return _lockerName; }
121 const Pathname & zyppLockFilePath() const
122 { return _zyppLockFilePath; }
126 Pathname _zyppLockFilePath;
127 file_lock _zyppLockFileLock;
128 FILE * _zyppLockFile;
131 std::string _lockerName;
135 typedef shared_ptr<void> ScopedGuard;
137 /** Exception safe access to the lockfile.
139 * // Exception safe access to the lockfile.
140 * ScopedGuard closeOnReturn( accessLockFile() );
143 ScopedGuard accessLockFile()
146 return ScopedGuard( static_cast<void*>(0),
147 bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
150 /** Use \ref accessLockFile. */
153 if ( _zyppLockFile != NULL )
156 // open pid file rw so we are sure it exist when creating the flock
157 _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
158 if ( _zyppLockFile == NULL )
159 ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
160 _zyppLockFileLock = _zyppLockFilePath.c_str();
161 MIL << "Open lockfile " << _zyppLockFilePath << endl;
164 /** Use \ref accessLockFile. */
165 void _closeLockFile()
167 if ( _zyppLockFile == NULL )
170 clearerr( _zyppLockFile );
171 fflush( _zyppLockFile );
172 // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
173 // If you are using a std::fstream/native file handle to write to the file
174 // while using file locks on that file, don't close the file before releasing
175 // all the locks of the file.
176 _zyppLockFileLock = file_lock();
177 fclose( _zyppLockFile );
178 _zyppLockFile = NULL;
179 MIL << "Close lockfile " << _zyppLockFilePath << endl;
183 bool isProcessRunning( pid_t pid_r )
185 // it is another program, not me, see if it is still running
186 Pathname procdir( "/proc"/str::numstring(pid_r) );
187 PathInfo status( procdir );
188 MIL << "Checking " << status << endl;
190 if ( ! status.isDir() )
192 DBG << "No such process." << endl;
196 static char buffer[513];
197 buffer[0] = buffer[512] = 0;
198 // man proc(5): /proc/[pid]/cmdline is empty if zombie.
199 if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
201 _lockerName = buffer;
202 DBG << "Is running: " << _lockerName << endl;
206 DBG << "In zombie state." << endl;
212 clearerr( _zyppLockFile );
213 fseek( _zyppLockFile, 0, SEEK_SET );
215 fscanf( _zyppLockFile, "%ld", &readpid );
216 MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
217 return (pid_t)readpid;
222 clearerr( _zyppLockFile );
223 fseek( _zyppLockFile, 0, SEEK_SET );
224 ftruncate( fileno(_zyppLockFile), 0 );
225 fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
226 fflush( _zyppLockFile );
227 _cleanLock = true; // cleanup on exit
228 MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
233 /** Try to aquire a lock.
234 * \return \c true if zypp is already locked by another process.
238 if ( geteuid() != 0 )
239 return false; // no lock as non-root
241 // Exception safe access to the lockfile.
242 ScopedGuard closeOnReturn( accessLockFile() );
244 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
246 _lockerPid = readLockFile();
247 if ( _lockerPid == 0 )
249 // no or empty lock file
253 else if ( _lockerPid == getpid() )
260 // a foreign pid in lock
261 if ( isProcessRunning( _lockerPid ) )
263 WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
268 MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
274 INT << "Oops! We should not be here!" << std::endl;
282 static ZYppGlobalLock & globalLock()
284 static ZYppGlobalLock lock;
287 bool _haveZYpp = false;
290 ///////////////////////////////////////////////////////////////////
292 // CLASS NAME : ZYppFactoryException
294 ///////////////////////////////////////////////////////////////////
296 ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
298 , _lockerPid( lockerPid_r )
299 , _lockerName( lockerName_r )
302 ZYppFactoryException::~ZYppFactoryException() throw ()
305 ///////////////////////////////////////////////////////////////////
307 // CLASS NAME : ZYppFactory
309 ///////////////////////////////////////////////////////////////////
311 ///////////////////////////////////////////////////////////////////
313 // METHOD NAME : ZYppFactory::instance
314 // METHOD TYPE : ZYppFactory
316 ZYppFactory ZYppFactory::instance()
318 return ZYppFactory();
321 ///////////////////////////////////////////////////////////////////
323 // METHOD NAME : ZYppFactory::ZYppFactory
324 // METHOD TYPE : Ctor
326 ZYppFactory::ZYppFactory()
331 ///////////////////////////////////////////////////////////////////
333 // METHOD NAME : ZYppFactory::~ZYppFactory
334 // METHOD TYPE : Dtor
336 ZYppFactory::~ZYppFactory()
339 ///////////////////////////////////////////////////////////////////
341 ZYpp::Ptr ZYppFactory::getZYpp() const
343 static ZYpp::Ptr _instance;
347 if ( geteuid() != 0 )
349 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
351 else if ( zypp_readonly_hack::active )
353 MIL << "ZYPP_READONLY active." << endl;
355 else if ( globalLock().zyppLocked() )
358 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
359 if ( LOCK_TIMEOUT > 0 )
361 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
363 Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
364 for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
366 if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
370 MIL << "Retry after " << i << " sec." << endl;
371 failed = globalLock().zyppLocked();
374 // another proc locked faster. maybe it ends fast as well....
375 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
376 procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
380 MIL << "Finally got the lock!" << endl;
389 std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
390 "Close this application before trying again."),
391 globalLock().lockerPid(),
392 globalLock().lockerName().c_str()
394 ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
398 _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
406 ///////////////////////////////////////////////////////////////////
408 bool ZYppFactory::haveZYpp() const
409 { return _haveZYpp; }
411 /******************************************************************
413 ** FUNCTION NAME : operator<<
414 ** FUNCTION TYPE : std::ostream &
416 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
418 return str << "ZYppFactory";
421 /////////////////////////////////////////////////////////////////
423 ///////////////////////////////////////////////////////////////////