X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=zypp%2FZYppFactory.cc;h=03ea8973b450d94b6f2c1861fed894e5ec7951f1;hb=0691d32a75e797f3d616a7f880d2be1e0a7025b0;hp=8429224f6f52f2b34f690147c10f5630eab41314;hpb=ad6312d49d0cd5f226247151df78bda2e25cb879;p=platform%2Fupstream%2Flibzypp.git diff --git a/zypp/ZYppFactory.cc b/zypp/ZYppFactory.cc index 8429224..03ea897 100644 --- a/zypp/ZYppFactory.cc +++ b/zypp/ZYppFactory.cc @@ -9,36 +9,64 @@ /** \file zypp/ZYppFactory.cc * */ - +extern "C" +{ #include -#include -#include -#include +} #include -#include +#include +#include #include "zypp/base/Logger.h" #include "zypp/base/Gettext.h" +#include "zypp/base/IOStream.h" +#include "zypp/base/Functional.h" +#include "zypp/base/Backtrace.h" #include "zypp/PathInfo.h" #include "zypp/ZYppFactory.h" #include "zypp/zypp_detail/ZYppImpl.h" #include "zypp/zypp_detail/ZYppReadOnlyHack.h" -#define ZYPP_LOCK_FILE "/var/run/zypp.pid" +#include +#include +#include + +using boost::interprocess::file_lock; +using boost::interprocess::scoped_lock; +using boost::interprocess::sharable_lock; using std::endl; -using namespace std; /////////////////////////////////////////////////////////////////// namespace zypp { ///////////////////////////////////////////////////////////////// + namespace + { + void sigsegvHandler( int sig ); + ::sighandler_t lastSigsegvHandler = ::signal( SIGSEGV, sigsegvHandler ); + + /** SIGSEGV handler to log stack trace */ + void sigsegvHandler( int sig ) + { + INT << "Error: signal " << sig << endl << dumpBacktrace << endl; + ::signal( SIGSEGV, lastSigsegvHandler ); + } + } + + namespace env + { + /** Hack to circumvent the currently poor --root support. */ + inline Pathname ZYPP_LOCKFILE_ROOT() + { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; } + } + /////////////////////////////////////////////////////////////////// namespace zypp_readonly_hack { ///////////////////////////////////////////////////////////////// - static bool active = false; + static bool active = getenv("ZYPP_READONLY_HACK"); void IWantIt() { @@ -46,276 +74,272 @@ namespace zypp MIL << "ZYPP_READONLY promised." << endl; } + bool IGotIt() + { + return active; + } + ///////////////////////////////////////////////////////////////// } // namespace zypp_readonly_hack /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// - // - // CLASS NAME : ZYppGlobalLock - // + /// \class ZYppGlobalLock + /// \brief Our broken global lock + /// /////////////////////////////////////////////////////////////////// - class ZYppGlobalLock { - public: - - ZYppGlobalLock() - : _clean_lock(false) - , _zypp_lockfile(0) - , _locker_pid(0) - {} + public: + ZYppGlobalLock() + : _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" ) + , _zyppLockFile( NULL ) + , _lockerPid( 0 ) + , _cleanLock( false ) + { + filesystem::assert_dir(_zyppLockFilePath.dirname() ); + } ~ZYppGlobalLock() { - try - { - pid_t curr_pid = getpid(); - if ( _zypp_lockfile ) - { - Pathname lock_file = Pathname(ZYPP_LOCK_FILE); - unLockFile(); - closeLockFile(); - - if ( _clean_lock ) - { - MIL << "Cleaning lock file. (" << curr_pid << ")" << std::endl; - if ( filesystem::unlink(lock_file) == 0 ) - MIL << "Lockfile cleaned. (" << curr_pid << ")" << std::endl; - else - ERR << "Cant clean lockfile. (" << curr_pid << ")" << std::endl; - } - } - } - catch(...) {} // let no exception escape. + if ( _cleanLock ) + try { + // Exception safe access to the lockfile. + ScopedGuard closeOnReturn( accessLockFile() ); + { + scoped_lock flock( _zyppLockFileLock ); // aquire write lock + // Truncate the file rather than deleting it. Other processes may + // still use it to synchronsize. + ftruncate( fileno(_zyppLockFile), 0 ); + } + MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl; + } + catch(...) {} // let no exception escape. } - pid_t locker_pid() const { return _locker_pid; } + pid_t lockerPid() const + { return _lockerPid; } - bool _clean_lock; + const std::string & lockerName() const + { return _lockerName; } - private: - FILE *_zypp_lockfile; - pid_t _locker_pid; + const Pathname & zyppLockFilePath() const + { return _zyppLockFilePath; } - void openLockFile(const char *mode) - { - Pathname lock_file = Pathname(ZYPP_LOCK_FILE); - _zypp_lockfile = fopen(lock_file.asString().c_str(), mode); - if (_zypp_lockfile == 0) - ZYPP_THROW (Exception( "Cant open " + lock_file.asString() + " in mode " + std::string(mode) ) ); - } - void closeLockFile() - { - fclose(_zypp_lockfile); - } + private: + Pathname _zyppLockFilePath; + file_lock _zyppLockFileLock; + FILE * _zyppLockFile; - void shLockFile() - { - int fd = fileno(_zypp_lockfile); - int lock_error = flock(fd, LOCK_SH); - if (lock_error != 0) - ZYPP_THROW (Exception( "Cant get shared lock")); - else - MIL << "locked (shared)" << std::endl; - } + pid_t _lockerPid; + std::string _lockerName; + bool _cleanLock; - void exLockFile() - { - int fd = fileno(_zypp_lockfile); - // lock access to the file - int lock_error = flock(fd, LOCK_EX); - if (lock_error != 0) - ZYPP_THROW (Exception( "Cant get exclusive lock" )); - else - MIL << "locked (exclusive)" << std::endl; - } + private: + typedef shared_ptr ScopedGuard; - void unLockFile() + /** Exception safe access to the lockfile. + * \code + * // Exception safe access to the lockfile. + * ScopedGuard closeOnReturn( accessLockFile() ); + * \endcode + */ + ScopedGuard accessLockFile() { - int fd = fileno(_zypp_lockfile); - // lock access to the file - int lock_error = flock(fd, LOCK_UN); - if (lock_error != 0) - ZYPP_THROW (Exception( "Cant release lock" )); - else - MIL << "unlocked" << std::endl; + _openLockFile(); + return ScopedGuard( static_cast(0), + bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) ); } - bool lockFileExists() + /** Use \ref accessLockFile. */ + void _openLockFile() { - Pathname lock_file = Pathname(ZYPP_LOCK_FILE); - // check if the file already existed. - bool exists = PathInfo(lock_file).isExist(); - return exists; + if ( _zyppLockFile != NULL ) + return; // is open + + // open pid file rw so we are sure it exist when creating the flock + _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" ); + if ( _zyppLockFile == NULL ) + ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) ); + _zyppLockFileLock = _zyppLockFilePath.c_str(); + MIL << "Open lockfile " << _zyppLockFilePath << endl; } - void createLockFile() + /** Use \ref accessLockFile. */ + void _closeLockFile() { - pid_t curr_pid = getpid(); - openLockFile("w"); - exLockFile(); - fprintf(_zypp_lockfile, "%ld\n", (long) curr_pid); - fflush(_zypp_lockfile); - unLockFile(); - MIL << "written lockfile with pid " << curr_pid << std::endl; - closeLockFile(); + if ( _zyppLockFile == NULL ) + return; // is closed + + clearerr( _zyppLockFile ); + fflush( _zyppLockFile ); + // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html + // If you are using a std::fstream/native file handle to write to the file + // while using file locks on that file, don't close the file before releasing + // all the locks of the file. + _zyppLockFileLock = file_lock(); + fclose( _zyppLockFile ); + _zyppLockFile = NULL; + MIL << "Close lockfile " << _zyppLockFilePath << endl; } - bool isProcessRunning(pid_t pid) + + bool isProcessRunning( pid_t pid_r ) { - // it is another program, not me, see if it is still running - stringstream ss; - ss << "/proc/" << pid << "/status"; - Pathname procfile = Pathname(ss.str()); - MIL << "Checking " << procfile << " to determine if pid is running: " << pid << std::endl; - bool still_running = PathInfo(procfile).isExist(); - return still_running; + // it is another program, not me, see if it is still running + Pathname procdir( "/proc"/str::numstring(pid_r) ); + PathInfo status( procdir ); + MIL << "Checking " << status << endl; + + if ( ! status.isDir() ) + { + DBG << "No such process." << endl; + return false; + } + + static char buffer[513]; + buffer[0] = buffer[512] = 0; + // man proc(5): /proc/[pid]/cmdline is empty if zombie. + if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 ) + { + _lockerName = buffer; + DBG << "Is running: " << _lockerName << endl; + return true; + } + + DBG << "In zombie state." << endl; + return false; } - pid_t lockerPid() + pid_t readLockFile() { - pid_t curr_pid = getpid(); - pid_t locker_pid = 0; + clearerr( _zyppLockFile ); + fseek( _zyppLockFile, 0, SEEK_SET ); long readpid = 0; + fscanf( _zyppLockFile, "%ld", &readpid ); + MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl; + return (pid_t)readpid; + } - fscanf(_zypp_lockfile, "%ld", &readpid); - MIL << "read: Lockfile " << ZYPP_LOCK_FILE << " has pid " << readpid << " (our pid: " << curr_pid << ") "<< std::endl; - locker_pid = (pid_t) readpid; - return locker_pid; + void writeLockFile() + { + clearerr( _zyppLockFile ); + fseek( _zyppLockFile, 0, SEEK_SET ); + ftruncate( fileno(_zyppLockFile), 0 ); + fprintf(_zyppLockFile, "%ld\n", (long)getpid() ); + fflush( _zyppLockFile ); + _cleanLock = true; // cleanup on exit + MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " << getpid() << std::endl; } public: + /** Try to aquire a lock. + * \return \c true if zypp is already locked by another process. + */ bool zyppLocked() { - pid_t curr_pid = getpid(); - Pathname lock_file = Pathname(ZYPP_LOCK_FILE); + if ( geteuid() != 0 ) + return false; // no lock as non-root - if ( lockFileExists() ) + // Exception safe access to the lockfile. + ScopedGuard closeOnReturn( accessLockFile() ); { - MIL << "found lockfile " << lock_file << std::endl; - openLockFile("r"); - shLockFile(); - - pid_t locker_pid = lockerPid(); - _locker_pid = locker_pid; - if ( locker_pid == curr_pid ) - { - // alles ok, we are requesting the instance again - //MIL << "Lockfile found, but it is myself. Assuming same process getting zypp instance again." << std::endl; - return false; - } - else - { - if ( isProcessRunning(locker_pid) ) - { - if ( geteuid() == 0 ) - { - // i am root - MIL << locker_pid << " is running and has a ZYpp lock. Sorry" << std::endl; - return true; - } - else - { - MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl; - return false; - } - } - else - { - if ( geteuid() == 0 ) - { - MIL << locker_pid << " has a ZYpp lock, but process is not running. Cleaning lock file." << std::endl; - if ( filesystem::unlink(lock_file) == 0 ) - { - createLockFile(); - // now open it for reading - openLockFile("r"); - shLockFile(); - return false; - } - else - { - ERR << "Can't clean lockfile. Sorry, can't create a new lock. Zypp still locked." << std::endl; - return true; - } - } - else - { - MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl; - return false; - } - } - } - } - else - { - MIL << "no lockfile " << lock_file << " found" << std::endl; - if ( geteuid() == 0 ) - { - MIL << "running as root. Will attempt to create " << lock_file << std::endl; - createLockFile(); - // now open it for reading - openLockFile("r"); - shLockFile(); - } - else - { - MIL << "running as user. Skipping creating " << lock_file << std::endl; - } - return false; + scoped_lock flock( _zyppLockFileLock ); // aquire write lock + + _lockerPid = readLockFile(); + if ( _lockerPid == 0 ) + { + // no or empty lock file + writeLockFile(); + return false; + } + else if ( _lockerPid == getpid() ) + { + // keep my own lock + return false; + } + else + { + // a foreign pid in lock + if ( isProcessRunning( _lockerPid ) ) + { + WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl; + return true; + } + else + { + MIL << _lockerPid << " is dead. Taking the lock file." << std::endl; + writeLockFile(); + return false; + } + } } + INT << "Oops! We should not be here!" << std::endl; return true; } }; - static ZYppGlobalLock globalLock; - - /////////////////////////////////////////////////////////////////// - // - // CLASS NAME : ZYppFactoryException - // /////////////////////////////////////////////////////////////////// + namespace + { + static weak_ptr _theZYppInstance; + static scoped_ptr _theGlobalLock; // on/off in sync with _theZYppInstance - ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t locker_pid ) - : Exception(msg_r), - _locker_pid (locker_pid) - {} + ZYppGlobalLock & globalLock() + { + if ( !_theGlobalLock ) + _theGlobalLock.reset( new ZYppGlobalLock ); + return *_theGlobalLock; + } + } //namespace + /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // - // CLASS NAME : ZYppFactory + // CLASS NAME : ZYpp // /////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////// - // - // METHOD NAME : ZYppFactory::instance - // METHOD TYPE : ZYppFactory - // - ZYppFactory ZYppFactory::instance() + ZYpp::ZYpp( const Impl_Ptr & impl_r ) + : _pimpl( impl_r ) { - return ZYppFactory(); + } + + ZYpp::~ZYpp() + { + _theGlobalLock.reset(); } /////////////////////////////////////////////////////////////////// // - // METHOD NAME : ZYppFactory::ZYppFactory - // METHOD TYPE : Ctor + // CLASS NAME : ZYppFactoryException // - ZYppFactory::ZYppFactory() - { + /////////////////////////////////////////////////////////////////// - } + ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r ) + : Exception( msg_r ) + , _lockerPid( lockerPid_r ) + , _lockerName( lockerName_r ) + {} + + ZYppFactoryException::~ZYppFactoryException() throw () + {} /////////////////////////////////////////////////////////////////// // - // METHOD NAME : ZYppFactory::~ZYppFactory - // METHOD TYPE : Dtor + // CLASS NAME : ZYppFactory // + /////////////////////////////////////////////////////////////////// + + ZYppFactory ZYppFactory::instance() + { return ZYppFactory(); } + + ZYppFactory::ZYppFactory() + {} + ZYppFactory::~ZYppFactory() {} @@ -323,33 +347,74 @@ namespace zypp // ZYpp::Ptr ZYppFactory::getZYpp() const { - static ZYpp::Ptr _instance; - + ZYpp::Ptr _instance = _theZYppInstance.lock(); if ( ! _instance ) { - /*--------------------------------------------------*/ - if ( zypp_readonly_hack::active ) - { - _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) ); - MIL << "ZYPP_READONLY active." << endl; - return _instance; - } - /*--------------------------------------------------*/ - if ( globalLock.zyppLocked() ) + if ( geteuid() != 0 ) { - ZYPP_THROW( ZYppFactoryException(N_("Software management is already running."), - globalLock.locker_pid()) ); + MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl; } - else + else if ( zypp_readonly_hack::active ) { - _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) ); - globalLock._clean_lock = true; + MIL << "ZYPP_READONLY active." << endl; } + else if ( globalLock().zyppLocked() ) + { + bool failed = true; + const long LOCK_TIMEOUT = str::strtonum( getenv( "ZYPP_LOCK_TIMEOUT" ) ); + if ( LOCK_TIMEOUT > 0 ) + { + MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl; + unsigned delay = 1; + Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) ); + for ( long i = 0; i < LOCK_TIMEOUT; i += delay ) + { + if ( PathInfo( procdir ).isDir() ) // wait for /proc/pid to disapear + sleep( delay ); + else + { + MIL << "Retry after " << i << " sec." << endl; + failed = globalLock().zyppLocked(); + if ( failed ) + { + // another proc locked faster. maybe it ends fast as well.... + MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl; + procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) ); + } + else + { + MIL << "Finally got the lock!" << endl; + break; // gotcha + } + } + } + } + if ( failed ) + { + std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n" + "Close this application before trying again."), + globalLock().lockerPid(), + globalLock().lockerName().c_str() + ); + ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() )); + } + } + // Here we go... + static ZYpp::Impl_Ptr _theImplInstance; // for now created once + if ( !_theImplInstance ) + _theImplInstance.reset( new ZYpp::Impl ); + _instance.reset( new ZYpp( _theImplInstance ) ); + _theZYppInstance = _instance; } return _instance; } + /////////////////////////////////////////////////////////////////// + // + bool ZYppFactory::haveZYpp() const + { return !_theZYppInstance.expired(); } + /****************************************************************** ** ** FUNCTION NAME : operator<<