ZYppFactory.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 extern "C"
00013 {
00014 #include <sys/file.h>
00015 }
00016 #include <iostream>
00017 #include <fstream>
00018 
00019 #include "zypp/base/Logger.h"
00020 #include "zypp/base/Gettext.h"
00021 #include "zypp/base/IOStream.h"
00022 #include "zypp/base/Functional.h"
00023 #include "zypp/PathInfo.h"
00024 
00025 #include "zypp/ZYppFactory.h"
00026 #include "zypp/zypp_detail/ZYppImpl.h"
00027 #include "zypp/zypp_detail/ZYppReadOnlyHack.h"
00028 
00029 #include <boost/interprocess/sync/file_lock.hpp>
00030 #include <boost/interprocess/sync/scoped_lock.hpp>
00031 #include <boost/interprocess/sync/sharable_lock.hpp>
00032 
00033 using boost::interprocess::file_lock;
00034 using boost::interprocess::scoped_lock;
00035 using boost::interprocess::sharable_lock;
00036 
00037 using std::endl;
00038 
00040 namespace zypp
00041 { 
00042 
00043   namespace env
00044   {
00046     inline Pathname ZYPP_LOCKFILE_ROOT()
00047     { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
00048   }
00049 
00051   namespace zypp_readonly_hack
00052   { 
00053 
00054     static bool active = false;
00055 
00056     void IWantIt()
00057     {
00058       active = true;
00059       MIL << "ZYPP_READONLY promised." <<  endl;
00060     }
00061 
00062     bool IGotIt()
00063     {
00064       return active;
00065     }
00066 
00068   } // namespace zypp_readonly_hack
00070 
00076   class ZYppGlobalLock
00077   {
00078   public:
00079     ZYppGlobalLock()
00080     : _cleanLock( false )
00081     , _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
00082     , _zyppLockFile( NULL )
00083     , _lockerPid( 0 )
00084     {
00085       filesystem::assert_dir(_zyppLockFilePath.dirname() );
00086     }
00087 
00088     ~ZYppGlobalLock()
00089     {
00090         if ( _cleanLock )
00091         try {
00092           // Exception safe access to the lockfile.
00093           ScopedGuard closeOnReturn( accessLockFile() );
00094           {
00095             scoped_lock<file_lock> flock( *_zyppLockFileLockPtr );      // aquire write lock
00096             // Truncate the file rather than deleting it. Other processes may
00097             // still use it to synchronsize.
00098             ftruncate( fileno(_zyppLockFile), 0 );
00099           }
00100           MIL << "Cleanned lock file. (" << getpid() << ")" << std::endl;
00101         }
00102         catch(...) {} // let no exception escape.
00103     }
00104 
00105     pid_t lockerPid() const
00106     { return _lockerPid; }
00107 
00108     const std::string & lockerName() const
00109     { return _lockerName; }
00110 
00111     const Pathname & zyppLockFilePath() const
00112     { return _zyppLockFilePath; }
00113 
00114 
00115   private:
00116     Pathname    _zyppLockFilePath;
00117     scoped_ptr<file_lock>       _zyppLockFileLockPtr;
00118     FILE *      _zyppLockFile;
00119 
00120     pid_t       _lockerPid;
00121     std::string _lockerName;
00122     bool        _cleanLock;
00123 
00124   private:
00125     typedef shared_ptr<void> ScopedGuard;
00126 
00133     ScopedGuard accessLockFile()
00134     {
00135       _openLockFile();
00136       return ScopedGuard( static_cast<void*>(0),
00137                           bind( mem_fun_ref( &ZYppGlobalLock::_closeLockFile ), ref(*this) ) );
00138     }
00139 
00141     void _openLockFile()
00142     {
00143       if ( _zyppLockFile != NULL )
00144         return; // is open
00145 
00146       // open pid file rw so we are sure it exist when creating the flock
00147       _zyppLockFile = fopen( _zyppLockFilePath.c_str(), "a+" );
00148       if ( _zyppLockFile == NULL )
00149         ZYPP_THROW( Exception( "Cant open " + _zyppLockFilePath.asString() ) );
00150       _zyppLockFileLockPtr.reset( new file_lock( _zyppLockFilePath.c_str() ) );
00151       MIL << "Open lockfile " << _zyppLockFilePath << endl;
00152     }
00153 
00155     void _closeLockFile()
00156     {
00157       if ( _zyppLockFile == NULL )
00158         return; // is closed
00159 
00160       clearerr( _zyppLockFile );
00161       fflush( _zyppLockFile );
00162       // http://www.boost.org/doc/libs/1_50_0/doc/html/interprocess/synchronization_mechanisms.html
00163       // If you are using a std::fstream/native file handle to write to the file
00164       // while using file locks on that file, don't close the file before releasing
00165       // all the locks of the file.
00166       _zyppLockFileLockPtr.reset();
00167       fclose( _zyppLockFile );
00168       _zyppLockFile = NULL;
00169       MIL << "Close lockfile " << _zyppLockFilePath << endl;
00170     }
00171 
00172 
00173     bool isProcessRunning( pid_t pid_r )
00174     {
00175       // it is another program, not me, see if it is still running
00176       Pathname procdir( "/proc"/str::numstring(pid_r) );
00177       PathInfo status( procdir );
00178       MIL << "Checking " <<  status << endl;
00179 
00180       if ( ! status.isDir() )
00181       {
00182         DBG << "No such process." << endl;
00183         return false;
00184       }
00185 
00186       static char buffer[513];
00187       buffer[0] = buffer[512] = 0;
00188       // man proc(5): /proc/[pid]/cmdline is empty if zombie.
00189       if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
00190       {
00191         _lockerName = buffer;
00192         DBG << "Is running: " <<  _lockerName << endl;
00193         return true;
00194       }
00195 
00196       DBG << "In zombie state." << endl;
00197       return false;
00198     }
00199 
00200     pid_t readLockFile()
00201     {
00202       clearerr( _zyppLockFile );
00203       fseek( _zyppLockFile, 0, SEEK_SET );
00204       long readpid = 0;
00205       fscanf( _zyppLockFile, "%ld", &readpid );
00206       MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << getpid() << ") "<< std::endl;
00207       return (pid_t)readpid;
00208     }
00209 
00210     void writeLockFile()
00211     {
00212       clearerr( _zyppLockFile );
00213       fseek( _zyppLockFile, 0, SEEK_SET );
00214       ftruncate( fileno(_zyppLockFile), 0 );
00215       fprintf(_zyppLockFile, "%ld\n", (long)getpid() );
00216       fflush( _zyppLockFile );
00217       _cleanLock = true; // cleanup on exit
00218       MIL << "write: Lockfile " << _zyppLockFilePath << " got pid " <<  getpid() << std::endl;
00219     }
00220 
00221   public:
00222 
00226     bool zyppLocked()
00227     {
00228       if ( geteuid() != 0 )
00229         return false;   // no lock as non-root
00230 
00231       // Exception safe access to the lockfile.
00232       ScopedGuard closeOnReturn( accessLockFile() );
00233       {
00234         scoped_lock<file_lock> flock( *_zyppLockFileLockPtr );  // aquire write lock
00235 
00236         _lockerPid = readLockFile();
00237         if ( _lockerPid == 0 )
00238         {
00239           // no or empty lock file
00240           writeLockFile();
00241           return false;
00242         }
00243         else if ( _lockerPid == getpid() )
00244         {
00245           // keep my own lock
00246           return false;
00247         }
00248         else
00249         {
00250           // a foreign pid in lock
00251           if ( isProcessRunning( _lockerPid ) )
00252           {
00253             WAR << _lockerPid << " is running and has a ZYpp lock. Sorry." << std::endl;
00254             return true;
00255           }
00256           else
00257           {
00258             MIL << _lockerPid << " is dead. Taking the lock file." << std::endl;
00259             writeLockFile();
00260             return false;
00261           }
00262         }
00263       }
00264       INT << "Oops! We should not be here!" << std::endl;
00265       return true;
00266     }
00267 
00268   };
00269 
00270   namespace
00271   {
00272     static ZYppGlobalLock & globalLock()
00273     {
00274       static ZYppGlobalLock lock;
00275       return lock;
00276     }
00277     bool           _haveZYpp = false;
00278   }
00279 
00281   //
00282   //    CLASS NAME : ZYppFactoryException
00283   //
00285 
00286   ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
00287     : Exception( msg_r )
00288     , _lockerPid( lockerPid_r )
00289     , _lockerName( lockerName_r )
00290   {}
00291 
00292   ZYppFactoryException::~ZYppFactoryException() throw ()
00293   {}
00294 
00296   //
00297   //    CLASS NAME : ZYppFactory
00298   //
00300 
00302   //
00303   //    METHOD NAME : ZYppFactory::instance
00304   //    METHOD TYPE : ZYppFactory
00305   //
00306   ZYppFactory ZYppFactory::instance()
00307   {
00308     return ZYppFactory();
00309   }
00310 
00312   //
00313   //    METHOD NAME : ZYppFactory::ZYppFactory
00314   //    METHOD TYPE : Ctor
00315   //
00316   ZYppFactory::ZYppFactory()
00317   {
00318 
00319   }
00320 
00322   //
00323   //    METHOD NAME : ZYppFactory::~ZYppFactory
00324   //    METHOD TYPE : Dtor
00325   //
00326   ZYppFactory::~ZYppFactory()
00327   {}
00328 
00330   //
00331   ZYpp::Ptr ZYppFactory::getZYpp() const
00332   {
00333     static ZYpp::Ptr _instance;
00334 
00335     if ( ! _instance )
00336     {
00337       if ( geteuid() != 0 )
00338       {
00339         MIL << "Running as user. Skip creating " << globalLock().zyppLockFilePath() << std::endl;
00340       }
00341       else if ( zypp_readonly_hack::active )
00342       {
00343         MIL << "ZYPP_READONLY active." << endl;
00344       }
00345       else if ( globalLock().zyppLocked() )
00346       {
00347         bool failed = true;
00348         const long LOCK_TIMEOUT = str::strtonum<long>( getenv( "ZYPP_LOCK_TIMEOUT" ) );
00349         if ( LOCK_TIMEOUT > 0 )
00350         {
00351           MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within $LOCK_TIMEOUT=" << LOCK_TIMEOUT << " sec." << endl;
00352           unsigned delay = 1;
00353           Pathname procdir( "/proc"/str::numstring(globalLock().lockerPid()) );
00354           for ( long i = 0; i < LOCK_TIMEOUT; i += delay )
00355           {
00356             if ( PathInfo( procdir ).isDir() )  // wait for /proc/pid to disapear
00357               sleep( delay );
00358             else
00359             {
00360               MIL << "Retry after " << i << " sec." << endl;
00361               failed = globalLock().zyppLocked();
00362               if ( failed )
00363               {
00364                 // another proc locked faster. maybe it ends fast as well....
00365                 MIL << "Waiting whether pid " << globalLock().lockerPid() << " ends within " << (LOCK_TIMEOUT-i) << " sec." << endl;
00366                 procdir = Pathname( "/proc"/str::numstring(globalLock().lockerPid()) );
00367               }
00368               else
00369               {
00370                 MIL << "Finally got the lock!" << endl;
00371                 break;  // gotcha
00372               }
00373             }
00374           }
00375 
00376         }
00377         if ( failed )
00378         {
00379           std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
00380                                       "Close this application before trying again."),
00381                                       globalLock().lockerPid(),
00382                                       globalLock().lockerName().c_str()
00383                                     );
00384           ZYPP_THROW(ZYppFactoryException(t, globalLock().lockerPid(), globalLock().lockerName() ));
00385         }
00386       }
00387       // Here we go...
00388       _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
00389       if ( _instance )
00390         _haveZYpp = true;
00391     }
00392 
00393     return _instance;
00394   }
00395 
00397   //
00398   bool ZYppFactory::haveZYpp() const
00399   { return _haveZYpp; }
00400 
00401   /******************************************************************
00402   **
00403   **    FUNCTION NAME : operator<<
00404   **    FUNCTION TYPE : std::ostream &
00405   */
00406   std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
00407   {
00408     return str << "ZYppFactory";
00409   }
00410 
00412 } // namespace zypp

Generated on Tue May 5 14:43:20 2015 for libzypp by  doxygen 1.5.6