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;
82 /////////////////////////////////////////////////////////////////
83 } // namespace zypp_readonly_hack
84 ///////////////////////////////////////////////////////////////////
86 ///////////////////////////////////////////////////////////////////
87 /// \class ZYppGlobalLock
88 /// \brief Our broken global lock
90 ///////////////////////////////////////////////////////////////////
95 : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
96 , _zyppLockFile( NULL )
100 filesystem::assert_dir(_zyppLockFilePath.dirname() );
107 // Exception safe access to the lockfile.
108 ScopedGuard closeOnReturn( accessLockFile() );
110 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
111 // Truncate the file rather than deleting it. Other processes may
112 // still use it to synchronsize.
113 ftruncate( fileno(_zyppLockFile), 0 );
115 MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
117 catch(...) {} // let no exception escape.
120 pid_t lockerPid() const
121 { return _lockerPid; }
123 const std::string & lockerName() const
124 { return _lockerName; }
126 const Pathname & zyppLockFilePath() const
127 { return _zyppLockFilePath; }
131 Pathname _zyppLockFilePath;
132 file_lock _zyppLockFileLock;
133 FILE * _zyppLockFile;
136 std::string _lockerName;
140 typedef shared_ptr<void> ScopedGuard;
142 /** Exception safe access to the lockfile.
144 * // Exception safe access to the lockfile.
145 * ScopedGuard closeOnReturn( accessLockFile() );
148 ScopedGuard accessLockFile()
151 return ScopedGuard( static_cast<void*>(0),
152 bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
155 /** Use \ref accessLockFile. */
158 if ( _zyppLockFile != NULL )
161 // open pid file rw so we are sure it exist when creating the flock
162 _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
163 if ( _zyppLockFile == NULL )
164 ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
165 _zyppLockFileLock = _zyppLockFilePath.c_str();
166 MIL << "Open lockfile " << _zyppLockFilePath << endl;
169 /** Use \ref accessLockFile. */
170 void _closeLockFile()
172 if ( _zyppLockFile == NULL )
175 clearerr( _zyppLockFile );
176 fflush( _zyppLockFile );
177 // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
178 // If you are using a std::fstream/native file handle to write to the file
179 // while using file locks on that file, don't close the file before releasing
180 // all the locks of the file.
181 _zyppLockFileLock = file_lock();
182 fclose( _zyppLockFile );
183 _zyppLockFile = NULL;
184 MIL << "Close lockfile " << _zyppLockFilePath << endl;
188 bool isProcessRunning( pid_t pid_r )
190 // it is another program, not me, see if it is still running
191 Pathname procdir( "/proc"/str::numstring(pid_r) );
192 PathInfo status( procdir );
193 MIL << "Checking " << status << endl;
195 if ( ! status.isDir() )
197 DBG << "No such process." << endl;
201 static char buffer[513];
202 buffer[0] = buffer[512] = 0;
203 // man proc(5): /proc/[pid]/cmdline is empty if zombie.
204 if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
206 _lockerName = buffer;
207 DBG << "Is running: " << _lockerName << endl;
211 DBG << "In zombie state." << endl;
217 clearerr( _zyppLockFile );
218 fseek( _zyppLockFile, 0, SEEK_SET );
220 fscanf( _zyppLockFile, "%ld", &readpid );
221 MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
222 return (pid_t)readpid;
227 clearerr( _zyppLockFile );
228 fseek( _zyppLockFile, 0, SEEK_SET );
229 ftruncate( fileno(_zyppLockFile), 0 );
230 fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
231 fflush( _zyppLockFile );
232 _cleanLock = true; // cleanup on exit
233 MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
238 /** Try to aquire a lock.
239 * \return \c true if zypp is already locked by another process.
243 if ( geteuid() != 0 )
244 return false; // no lock as non-root
246 // Exception safe access to the lockfile.
247 ScopedGuard closeOnReturn( accessLockFile() );
249 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
251 _lockerPid = readLockFile();
252 if ( _lockerPid == 0 )
254 // no or empty lock file
258 else if ( _lockerPid == getpid() )
265 // a foreign pid in lock
266 if ( isProcessRunning( _lockerPid ) )
268 WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
273 MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
279 INT << "Oops! We should not be here!" << std::endl;
285 ///////////////////////////////////////////////////////////////////
288 static weak_ptr<ZYpp> _theZYppInstance;
289 static scoped_ptr<ZYppGlobalLock> _theGlobalLock; // on/off in sync with _theZYppInstance
291 ZYppGlobalLock & globalLock()
293 if ( !_theGlobalLock )
294 _theGlobalLock.reset( new ZYppGlobalLock );
295 return *_theGlobalLock;
298 ///////////////////////////////////////////////////////////////////
300 ///////////////////////////////////////////////////////////////////
304 ///////////////////////////////////////////////////////////////////
306 ZYpp::ZYpp( const Impl_Ptr & impl_r )
313 _theGlobalLock.reset();
316 ///////////////////////////////////////////////////////////////////
318 // CLASS NAME : ZYppFactoryException
320 ///////////////////////////////////////////////////////////////////
322 ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
324 , _lockerPid( lockerPid_r )
325 , _lockerName( lockerName_r )
328 ZYppFactoryException::~ZYppFactoryException() throw ()
331 ///////////////////////////////////////////////////////////////////
333 // CLASS NAME : ZYppFactory
335 ///////////////////////////////////////////////////////////////////
337 ZYppFactory ZYppFactory::instance()
338 { return ZYppFactory(); }
340 ZYppFactory::ZYppFactory()
343 ZYppFactory::~ZYppFactory()
346 ///////////////////////////////////////////////////////////////////
348 ZYpp::Ptr ZYppFactory::getZYpp() const
350 ZYpp::Ptr _instance = _theZYppInstance.lock();
353 if ( geteuid() != 0 )
355 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
357 else if ( zypp_readonly_hack::active )
359 MIL << "ZYPP_READONLY active." << endl;
361 else if ( globalLock().zyppLocked() )
364 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
365 if ( LOCK_TIMEOUT > 0 )
367 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
369 Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
370 for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
372 if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
376 MIL << "Retry after " << i << " sec." << endl;
377 failed = globalLock().zyppLocked();
380 // another proc locked faster. maybe it ends fast as well....
381 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
382 procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
386 MIL << "Finally got the lock!" << endl;
394 std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
395 "Close this application before trying again."),
396 globalLock().lockerPid(),
397 globalLock().lockerName().c_str()
399 ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
403 static ZYpp::Impl_Ptr _theImplInstance; // for now created once
404 if ( !_theImplInstance )
405 _theImplInstance.reset( new ZYpp::Impl );
406 _instance.reset( new ZYpp( _theImplInstance ) );
407 _theZYppInstance = _instance;
413 ///////////////////////////////////////////////////////////////////
415 bool ZYppFactory::haveZYpp() const
416 { return !_theZYppInstance.expired(); }
418 /******************************************************************
420 ** FUNCTION NAME : operator<<
421 ** FUNCTION TYPE : std::ostream &
423 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
425 return str << "ZYppFactory";
428 /////////////////////////////////////////////////////////////////
430 ///////////////////////////////////////////////////////////////////