libzypp  10.5.0
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/PathInfo.h"
00023 
00024 #include "zypp/ZYppFactory.h"
00025 #include "zypp/zypp_detail/ZYppImpl.h"
00026 #include "zypp/zypp_detail/ZYppReadOnlyHack.h"
00027 
00028 using std::endl;
00029 
00031 namespace zypp
00032 { 
00033 
00034   namespace env
00035   {
00037     inline Pathname ZYPP_LOCKFILE_ROOT()
00038     { return getenv("ZYPP_LOCKFILE_ROOT") ? getenv("ZYPP_LOCKFILE_ROOT") : "/"; }
00039   }
00040 
00042   namespace zypp_readonly_hack
00043   { 
00044 
00045     static bool active = false;
00046 
00047     void IWantIt()
00048     {
00049       active = true;
00050       MIL << "ZYPP_READONLY promised." <<  endl;
00051     }
00052 
00054   } // namespace zypp_readonly_hack
00056 
00058   //
00059   //    CLASS NAME : ZYppGlobalLock
00060   //
00062 
00063   class ZYppGlobalLock
00064   {
00065     public:
00066 
00067       ZYppGlobalLock()
00068       : _clean_lock(false)
00069       , _zyppLockFilePath( env::ZYPP_LOCKFILE_ROOT() / "/var/run/zypp.pid" )
00070       , _zypp_lockfile(0)
00071       , _locker_pid(0)
00072     {
00073       filesystem::assert_dir(_zyppLockFilePath.dirname());
00074     }
00075 
00076     ~ZYppGlobalLock()
00077     {
00078       try
00079         {
00080           pid_t curr_pid = getpid();
00081           if ( _zypp_lockfile )
00082             {
00083               unLockFile();
00084               closeLockFile();
00085 
00086               if ( _clean_lock )
00087               {
00088                 MIL << "Cleaning lock file. (" << curr_pid << ")" << std::endl;
00089                 if ( filesystem::unlink(_zyppLockFilePath) == 0 )
00090                   MIL << "Lockfile cleaned. (" << curr_pid << ")" << std::endl;
00091                 else
00092                   ERR << "Cant clean lockfile. (" << curr_pid << ")" << std::endl;
00093               }
00094             }
00095         }
00096       catch(...) {} // let no exception escape.
00097     }
00098 
00099     pid_t locker_pid() const
00100     { return _locker_pid; }
00101 
00102     const std::string & locker_name() const
00103     { return _locker_name; }
00104 
00105 
00106     bool _clean_lock;
00107 
00108     private:
00109     Pathname _zyppLockFilePath;
00110     FILE *_zypp_lockfile;
00111     pid_t _locker_pid;
00112     std::string _locker_name;
00113 
00114     void openLockFile(const char *mode)
00115     {
00116 
00117       _zypp_lockfile = fopen(_zyppLockFilePath.asString().c_str(), mode);
00118       if (_zypp_lockfile == 0)
00119         ZYPP_THROW (Exception( "Cant open " + _zyppLockFilePath.asString() + " in mode " + std::string(mode) ) );
00120     }
00121 
00122     void closeLockFile()
00123     {
00124       fclose(_zypp_lockfile);
00125     }
00126 
00127     void shLockFile()
00128     {
00129       int fd = fileno(_zypp_lockfile);
00130       int lock_error = flock(fd, LOCK_SH);
00131       if (lock_error != 0)
00132         ZYPP_THROW (Exception( "Cant get shared lock"));
00133       else
00134         MIL << "locked (shared)" << std::endl;
00135     }
00136 
00137     void exLockFile()
00138     {
00139       int fd = fileno(_zypp_lockfile);
00140     // lock access to the file
00141       int lock_error = flock(fd, LOCK_EX);
00142       if (lock_error != 0)
00143         ZYPP_THROW (Exception( "Cant get exclusive lock" ));
00144       else
00145         MIL << "locked (exclusive)" << std::endl;
00146     }
00147 
00148     void unLockFile()
00149     {
00150       int fd = fileno(_zypp_lockfile);
00151     // lock access to the file
00152       int lock_error = flock(fd, LOCK_UN);
00153       if (lock_error != 0)
00154         ZYPP_THROW (Exception( "Cant release lock" ));
00155       else
00156         MIL << "unlocked" << std::endl;
00157     }
00158 
00159     bool lockFileExists()
00160     {
00161       // check if the file already existed.
00162       PathInfo pi(_zyppLockFilePath);
00163       DBG << pi << endl;
00164       return pi.isExist();
00165     }
00166 
00167     void createLockFile()
00168     {
00169       pid_t curr_pid = getpid();
00170       openLockFile("w");
00171       exLockFile();
00172       fprintf(_zypp_lockfile, "%ld\n", (long) curr_pid);
00173       fflush(_zypp_lockfile);
00174       unLockFile();
00175       MIL << "written lockfile with pid " << curr_pid << std::endl;
00176       closeLockFile();
00177     }
00178 
00179     bool isProcessRunning(pid_t pid_r)
00180     {
00181       // it is another program, not me, see if it is still running
00182       Pathname procdir( "/proc"/str::numstring(pid_r) );
00183       PathInfo status( procdir );
00184       MIL << "Checking " <<  status << endl;
00185 
00186       if ( ! status.isDir() )
00187       {
00188         DBG << "No such process." << endl;
00189         return false;
00190       }
00191 
00192       static char buffer[513];
00193       buffer[0] = buffer[512] = 0;
00194       // man proc(5): /proc/[pid]/cmdline is empty if zombie.
00195       if ( std::ifstream( (procdir/"cmdline").c_str() ).read( buffer, 512 ).gcount() > 0 )
00196       {
00197         _locker_name = buffer;
00198         DBG << "Is running: " <<  _locker_name << endl;
00199         return true;
00200       }
00201 
00202       DBG << "In zombie state." << endl;
00203       return false;
00204     }
00205 
00206     pid_t lockerPid()
00207     {
00208       pid_t curr_pid = getpid();
00209       pid_t locker_pid = 0;
00210       long readpid = 0;
00211 
00212       fscanf(_zypp_lockfile, "%ld", &readpid);
00213       MIL << "read: Lockfile " << _zyppLockFilePath << " has pid " << readpid << " (our pid: " << curr_pid << ") "<< std::endl;
00214       locker_pid = (pid_t) readpid;
00215       return locker_pid;
00216     }
00217 
00218   public:
00219 
00220     bool zyppLocked()
00221     {
00222       pid_t curr_pid = getpid();
00223 
00224       if ( lockFileExists() )
00225       {
00226         MIL << "found lockfile " << _zyppLockFilePath << std::endl;
00227         openLockFile("r");
00228         shLockFile();
00229 
00230         pid_t locker_pid = lockerPid();
00231         _locker_pid = locker_pid;
00232         if ( locker_pid == curr_pid )
00233         {
00234         // alles ok, we are requesting the instance again
00235           //MIL << "Lockfile found, but it is myself. Assuming same process getting zypp instance again." << std::endl;
00236           return false;
00237         }
00238         else
00239         {
00240           if ( isProcessRunning(locker_pid) )
00241           {
00242             if ( geteuid() == 0 )
00243             {
00244               // i am root
00245               MIL << locker_pid << " is running and has a ZYpp lock. Sorry" << std::endl;
00246               return true;
00247             }
00248             else
00249             {
00250               MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
00251               return false;
00252             }
00253           }
00254           else
00255           {
00256             if ( geteuid() == 0 )
00257             {
00258               MIL << locker_pid << " has a ZYpp lock, but process is not running. Cleaning lock file." << std::endl;
00259               if ( filesystem::unlink(_zyppLockFilePath) == 0 )
00260               {
00261                 createLockFile();
00262               // now open it for reading
00263                 openLockFile("r");
00264                 shLockFile();
00265                 return false;
00266               }
00267               else
00268               {
00269                 ERR << "Can't clean lockfile. Sorry, can't create a new lock. Zypp still locked." << std::endl;
00270                 return true;
00271               }
00272             }
00273             else
00274             {
00275               MIL << locker_pid << " is running and has a ZYpp lock. Access as normal user allowed." << std::endl;
00276               return false;
00277             }
00278           }
00279         }
00280       }
00281       else
00282       {
00283         MIL << "no lockfile " << _zyppLockFilePath << " found" << std::endl;
00284         if ( geteuid() == 0 )
00285         {
00286           MIL << "running as root. Will attempt to create " << _zyppLockFilePath << std::endl;
00287           createLockFile();
00288         // now open it for reading
00289           openLockFile("r");
00290           shLockFile();
00291         }
00292         else
00293         {
00294           MIL << "running as user. Skipping creating " << _zyppLockFilePath << std::endl;
00295         }
00296         return false;
00297       }
00298       return true;
00299     }
00300 
00301   };
00302 
00303   namespace
00304   {
00305     ZYppGlobalLock globalLock;
00306     bool           _haveZYpp = false;
00307   }
00308 
00310   //
00311   //    CLASS NAME : ZYppFactoryException
00312   //
00314 
00315   ZYppFactoryException::ZYppFactoryException( const std::string & msg_r, pid_t lockerPid_r, const std::string & lockerName_r )
00316     : Exception( msg_r )
00317     , _lockerPid( lockerPid_r )
00318     , _lockerName( lockerName_r )
00319   {}
00320 
00321   ZYppFactoryException::~ZYppFactoryException() throw ()
00322   {}
00323 
00325   //
00326   //    CLASS NAME : ZYppFactory
00327   //
00329 
00331   //
00332   //    METHOD NAME : ZYppFactory::instance
00333   //    METHOD TYPE : ZYppFactory
00334   //
00335   ZYppFactory ZYppFactory::instance()
00336   {
00337     return ZYppFactory();
00338   }
00339 
00341   //
00342   //    METHOD NAME : ZYppFactory::ZYppFactory
00343   //    METHOD TYPE : Ctor
00344   //
00345   ZYppFactory::ZYppFactory()
00346   {
00347 
00348   }
00349 
00351   //
00352   //    METHOD NAME : ZYppFactory::~ZYppFactory
00353   //    METHOD TYPE : Dtor
00354   //
00355   ZYppFactory::~ZYppFactory()
00356   {}
00357 
00359   //
00360   ZYpp::Ptr ZYppFactory::getZYpp() const
00361   {
00362     static ZYpp::Ptr _instance;
00363 
00364     if ( ! _instance )
00365     {
00366       /*--------------------------------------------------*/
00367       if ( zypp_readonly_hack::active )
00368       {
00369           _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
00370           MIL << "ZYPP_READONLY active." << endl;
00371       }
00372       /*--------------------------------------------------*/
00373       else if ( globalLock.zyppLocked() )
00374       {
00375         std::string t = str::form(_("System management is locked by the application with pid %d (%s).\n"
00376                                      "Close this application before trying again."),
00377                                   globalLock.locker_pid(),
00378                                   globalLock.locker_name().c_str()
00379                                  );
00380         ZYPP_THROW(ZYppFactoryException(t, globalLock.locker_pid(),globalLock.locker_name() ));
00381       }
00382       else
00383       {
00384         _instance = new ZYpp( ZYpp::Impl_Ptr(new ZYpp::Impl) );
00385         globalLock._clean_lock = true;
00386       }
00387 
00388       if ( _instance )
00389         _haveZYpp = true;
00390     }
00391 
00392     return _instance;
00393   }
00394 
00396   //
00397   bool ZYppFactory::haveZYpp() const
00398   { return _haveZYpp; }
00399 
00400   /******************************************************************
00401   **
00402   **    FUNCTION NAME : operator<<
00403   **    FUNCTION TYPE : std::ostream &
00404   */
00405   std::ostream & operator<<( std::ostream & str, const ZYppFactory & obj )
00406   {
00407     return str << "ZYppFactory";
00408   }
00409 
00411 } // namespace zypp