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>
30 #include <boost/interprocess/sync/file_lock.hpp>
31 #include <boost/interprocess/sync/scoped_lock.hpp>
32 #include <boost/interprocess/sync/sharable_lock.hpp>
34 using boost::interprocess::file_lock;
35 using boost::interprocess::scoped_lock;
36 using boost::interprocess::sharable_lock;
40 namespace zyppintern { void repoVariablesReset(); } // upon re-acquiring the lock...
42 ///////////////////////////////////////////////////////////////////
44 { /////////////////////////////////////////////////////////////////
48 void sigsegvHandler( int sig );
49 ::sighandler_t lastSigsegvHandler = ::signal( SIGSEGV, sigsegvHandler );
51 /** SIGSEGV handler to log stack trace */
52 void sigsegvHandler( int sig )
54 INT << "Error: signal " << sig << endl << dumpBacktrace << endl;
55 ::signal( SIGSEGV, lastSigsegvHandler );
61 /** Hack to circumvent the currently poor --root support. */
62 inline Pathname ZYPP_LOCKFILE_ROOT()
63 { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
66 ///////////////////////////////////////////////////////////////////
67 namespace zypp_readonly_hack
68 { /////////////////////////////////////////////////////////////////
70 static bool active = getenv("ZYPP_READONLY_HACK");
72 void IWantIt() // see zypp/zypp_detail/ZYppReadOnlyHack.h
75 MIL << "ZYPP_READONLY promised." << endl;
83 /////////////////////////////////////////////////////////////////
84 } // namespace zypp_readonly_hack
85 ///////////////////////////////////////////////////////////////////
87 ///////////////////////////////////////////////////////////////////
88 /// \class ZYppGlobalLock
89 /// \brief Our broken global lock
91 ///////////////////////////////////////////////////////////////////
96 : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
97 , _zyppLockFile( NULL )
101 filesystem::assert_dir(_zyppLockFilePath.dirname() );
108 // Exception safe access to the lockfile.
109 ScopedGuard closeOnReturn( accessLockFile() );
111 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
112 // Truncate the file rather than deleting it. Other processes may
113 // still use it to synchronsize.
114 ftruncate( fileno(_zyppLockFile), 0 );
116 MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
118 catch(...) {} // let no exception escape.
121 pid_t lockerPid() const
122 { return _lockerPid; }
124 const std::string & lockerName() const
125 { return _lockerName; }
127 const Pathname & zyppLockFilePath() const
128 { return _zyppLockFilePath; }
132 Pathname _zyppLockFilePath;
133 file_lock _zyppLockFileLock;
134 FILE * _zyppLockFile;
137 std::string _lockerName;
141 typedef shared_ptr<void> ScopedGuard;
143 /** Exception safe access to the lockfile.
145 * // Exception safe access to the lockfile.
146 * ScopedGuard closeOnReturn( accessLockFile() );
149 ScopedGuard accessLockFile()
152 return ScopedGuard( static_cast<void*>(0),
153 bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
156 /** Use \ref accessLockFile. */
159 if ( _zyppLockFile != NULL )
162 // open pid file rw so we are sure it exist when creating the flock
163 _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
164 if ( _zyppLockFile == NULL )
165 ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
166 _zyppLockFileLock = _zyppLockFilePath.c_str();
167 MIL << "Open lockfile " << _zyppLockFilePath << endl;
170 /** Use \ref accessLockFile. */
171 void _closeLockFile()
173 if ( _zyppLockFile == NULL )
176 clearerr( _zyppLockFile );
177 fflush( _zyppLockFile );
178 // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
179 // If you are using a std::fstream/native file handle to write to the file
180 // while using file locks on that file, don't close the file before releasing
181 // all the locks of the file.
182 _zyppLockFileLock = file_lock();
183 fclose( _zyppLockFile );
184 _zyppLockFile = NULL;
185 MIL << "Close lockfile " << _zyppLockFilePath << endl;
189 bool isProcessRunning( pid_t pid_r )
191 // it is another program, not me, see if it is still running
192 Pathname procdir( Pathname("/proc")/str::numstring(pid_r) );
193 PathInfo status( procdir );
194 MIL << "Checking " << status << endl;
196 if ( ! status.isDir() )
198 DBG << "No such process." << endl;
202 static char buffer[513];
203 buffer[0] = buffer[512] = 0;
204 // man proc(5): /proc/[pid]/cmdline is empty if zombie.
205 if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
207 _lockerName = buffer;
208 DBG << "Is running: " << _lockerName << endl;
212 DBG << "In zombie state." << endl;
218 clearerr( _zyppLockFile );
219 fseek( _zyppLockFile, 0, SEEK_SET );
221 fscanf( _zyppLockFile, "%ld", &readpid );
222 MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
223 return (pid_t)readpid;
228 clearerr( _zyppLockFile );
229 fseek( _zyppLockFile, 0, SEEK_SET );
230 ftruncate( fileno(_zyppLockFile), 0 );
231 fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
232 fflush( _zyppLockFile );
233 _cleanLock = true; // cleanup on exit
234 MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl;
239 /** Try to aquire a lock.
240 * \return \c true if zypp is already locked by another process.
244 if ( geteuid() != 0 )
245 return false; // no lock as non-root
247 // Exception safe access to the lockfile.
248 ScopedGuard closeOnReturn( accessLockFile() );
250 scoped_lock<file_lock> flock( _zyppLockFileLock ); // aquire write lock
252 _lockerPid = readLockFile();
253 if ( _lockerPid == 0 )
255 // no or empty lock file
259 else if ( _lockerPid == getpid() )
266 // a foreign pid in lock
267 if ( isProcessRunning( _lockerPid ) )
269 WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
274 MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
280 INT << "Oops! We should not be here!" << std::endl;
286 ///////////////////////////////////////////////////////////////////
289 static weak_ptr<ZYpp> _theZYppInstance;
290 static scoped_ptr<ZYppGlobalLock> _theGlobalLock; // on/off in sync with _theZYppInstance
292 ZYppGlobalLock & globalLock()
294 if ( !_theGlobalLock )
295 _theGlobalLock.reset( new ZYppGlobalLock );
296 return *_theGlobalLock;
299 ///////////////////////////////////////////////////////////////////
301 ///////////////////////////////////////////////////////////////////
305 ///////////////////////////////////////////////////////////////////
307 ZYpp::ZYpp( const Impl_Ptr & impl_r )
310 ::zyppintern::repoVariablesReset(); // upon re-acquiring the lock...
311 MIL << "ZYpp is on..." << endl;
316 _theGlobalLock.reset();
317 MIL << "ZYpp is off..." << endl;
320 ///////////////////////////////////////////////////////////////////
322 // CLASS NAME : ZYppFactoryException
324 ///////////////////////////////////////////////////////////////////
326 ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
328 , _lockerPid( lockerPid_r )
329 , _lockerName( lockerName_r )
332 ZYppFactoryException::~ZYppFactoryException() throw ()
335 ///////////////////////////////////////////////////////////////////
337 // CLASS NAME : ZYppFactory
339 ///////////////////////////////////////////////////////////////////
341 ZYppFactory ZYppFactory::instance()
342 { return ZYppFactory(); }
344 ZYppFactory::ZYppFactory()
347 ZYppFactory::~ZYppFactory()
350 ///////////////////////////////////////////////////////////////////
352 ZYpp::Ptr ZYppFactory::getZYpp() const
354 ZYpp::Ptr _instance = _theZYppInstance.lock();
357 if ( geteuid() != 0 )
359 MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
361 else if ( zypp_readonly_hack::active )
363 MIL << "ZYPP_READONLY active." << endl;
365 else if ( globalLock().zyppLocked() )
368 const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
369 if ( LOCK_TIMEOUT > 0 )
371 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
373 Pathname procdir( Pathname("/proc")/str::numstring(globalLock().lockerPid()) );
374 for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
376 if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear
380 MIL << "Retry after " << i << " sec." << endl;
381 failed = globalLock().zyppLocked();
384 // another proc locked faster. maybe it ends fast as well....
385 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
386 procdir = Pathname( Pathname("/proc")/str::numstring(globalLock().lockerPid()) );
390 MIL << "Finally got the lock!" << endl;
398 std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
399 "Close this application before trying again."),
400 globalLock().lockerPid(),
401 globalLock().lockerName().c_str()
403 ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
407 static ZYpp::Impl_Ptr _theImplInstance; // for now created once
408 if ( !_theImplInstance )
409 _theImplInstance.reset( new ZYpp::Impl );
410 _instance.reset( new ZYpp( _theImplInstance ) );
411 _theZYppInstance = _instance;
417 ///////////////////////////////////////////////////////////////////
419 bool ZYppFactory::haveZYpp() const
420 { return !_theZYppInstance.expired(); }
422 /******************************************************************
424 ** FUNCTION NAME : operator<<
425 ** FUNCTION TYPE : std::ostream &
427 std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
429 return str << "ZYppFactory";
432 /////////////////////////////////////////////////////////////////
434 ///////////////////////////////////////////////////////////////////