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

doxygen