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;
287 static ZYppGlobalLock & globalLock()
289 static ZYppGlobalLock lock;
292 bool _haveZYpp = false;
295 ///////////////////////////////////////////////////////////////////
297 // CLASS NAME : ZYppFactoryException
299 ///////////////////////////////////////////////////////////////////
301 ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
303 , _lockerPid( lockerPid_r )
304 , _lockerName( lockerName_r )
307 ZYppFactoryException::~ZYppFactoryException() throw ()
310 ///////////////////////////////////////////////////////////////////
312 // CLASS NAME : ZYppFactory
314 ///////////////////////////////////////////////////////////////////
316 ///////////////////////////////////////////////////////////////////
318 // METHOD NAME : ZYppFactory::instance
319 // METHOD TYPE : ZYppFactory
321 ZYppFactory ZYppFactory::instance()
323 return ZYppFactory();
326 ///////////////////////////////////////////////////////////////////
328 // METHOD NAME : ZYppFactory::ZYppFactory
329 // METHOD TYPE : Ctor
331 ZYppFactory::ZYppFactory()
336 ///////////////////////////////////////////////////////////////////
338 // METHOD NAME : ZYppFactory::~ZYppFactory
339 // METHOD TYPE : Dtor
341 ZYppFactory::~ZYppFactory()
344 ///////////////////////////////////////////////////////////////////
346 ZYpp::Ptr ZYppFactory::getZYpp() const
348 static ZYpp::Ptr _instance;
352 if ( geteuid() != 0 )
354 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
356 else if ( zypp_readonly_hack::active )
358 MIL << "ZYPP_READONLY active." << endl;
360 else if ( globalLock().zyppLocked() )
363 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
364 if ( LOCK_TIMEOUT > 0 )
366 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
368 Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
369 for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
371 if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
375 MIL << "Retry after " << i << " sec." << endl;
376 failed = globalLock().zyppLocked();
379 // another proc locked faster. maybe it ends fast as well....
380 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
381 procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
385 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 _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
411 ///////////////////////////////////////////////////////////////////
413 bool ZYppFactory::haveZYpp() const
414 { return _haveZYpp; }
416 /******************************************************************
418 ** FUNCTION NAME : operator<<
419 ** FUNCTION TYPE : std::ostream &
421 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
423 return str << "ZYppFactory";
426 /////////////////////////////////////////////////////////////////
428 ///////////////////////////////////////////////////////////////////