libzypp  10.5.0
InterProcessMutex.cc
Go to the documentation of this file.
00001 
00002 extern "C"
00003 {
00004 #include <sys/file.h>
00005 }
00006 #include <iostream>
00007 #include <fstream>
00008 
00009 #include "zypp/base/Logger.h"
00010 #include "zypp/base/Gettext.h"
00011 #include "zypp/base/IOStream.h"
00012 #include "zypp/base/InterProcessMutex.h"
00013 #include "zypp/base/String.h"
00014 
00015 #include "zypp/TmpPath.h"
00016 #include "zypp/Pathname.h"
00017 #include "zypp/PathInfo.h"
00018 
00019 #define LMIL MIL << "LOCK [" << _options.name << "] "
00020 
00021 using namespace std;
00022 
00023 namespace zypp
00024 {
00025 namespace base
00026 {
00027 
00028 ZYppLockedException::ZYppLockedException( const std::string & msg_r,
00029                                           const std::string &name,
00030                                           pid_t locker_pid )
00031     : Exception(msg_r)
00032     , _locker_pid (locker_pid)
00033     , _name(name)
00034 {}
00035 
00036 ZYppLockedException::~ZYppLockedException() throw()
00037 {}
00038  
00039 
00040 InterProcessMutex::Options::Options( ConsumerType ptype,
00041                                      const std::string &pname,
00042                                      int ptimeout )
00043     : name(pname) 
00044     , timeout(ptimeout)
00045     , type(ptype)
00046 {
00047     if ( geteuid() == 0 )
00048         base = "/var/run";
00049     else
00050         base = filesystem::TmpPath::defaultLocation() + ( string("zypp-") + getenv("USER") );
00051 }
00052     
00053 
00054    
00055 InterProcessMutex::InterProcessMutex( const Options &poptions )
00056     : _options(poptions)
00057 {
00058     // get the current pid
00059     pid_t curr_pid = getpid();
00060     Pathname lock_file = lockFilePath();
00061     int totalslept = 0;
00062     int k = 0;
00063     
00064     while (1)
00065     {
00066         k++;
00067         
00068         // try to create the lock file atomically, this will fail if
00069         // the lock exists
00070         try {
00071           _fd.reset( new Fd( lock_file, O_RDWR | O_CREAT | O_EXCL, 0666) );
00072         } catch (...) {
00073           _fd.reset();
00074         }
00075         if ( !_fd || !_fd->isOpen() )
00076         {
00077             struct flock lock;
00078             
00079             // the file exists, lets see if someone has it locked exclusively
00080             _fd.reset( new Fd( lock_file, O_RDWR ) );
00081             if ( !_fd->isOpen() )
00082             {
00083                 ZYPP_THROW(Exception(str::form(_("Can't open lock file: %s"), strerror(errno))));
00084             }
00085             
00086             memset(&lock, 0, sizeof(struct flock));
00087             lock.l_whence = SEEK_SET;
00088 
00089             // GETLK tells you the conflicting lock as if the lock you pass
00090             // would have been set. So set the lock type depending on whether
00091             // we are a writer or a reader.
00092             lock.l_type = ( ( _options.type == Writer ) ? F_WRLCK : F_RDLCK );
00093             
00094 
00095             // get lock information
00096             if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
00097             {
00098                 ZYPP_THROW(Exception(string("Error getting lock info: ") +  strerror(errno)));
00099             }
00100 
00101             
00102             MIL << lock_file << " : ";
00103             switch ( lock.l_type )
00104             {
00105                 case F_WRLCK:
00106                     MIL << " Write-Lock conflicts" << endl;
00107                     break;
00108                 case F_RDLCK:
00109                     MIL << " Read-Lock conflicts" << endl;                    
00110                     break;
00111                 case F_UNLCK:
00112                     MIL << " No lock conflicts" << endl;
00113                     break;
00114                 default:
00115                     break;
00116                     
00117             }
00118             
00119             // F_GETLK is confusing
00120             // http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2005-09/123fae2c774bceed?rnum=61&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2005-09%3F
00121             // new table of access
00122             // F_WRLCK   Reader  Wait or abort
00123             // F_WRLCK   Writer  Wait or abort
00124             // F_RDLCK   Writer  Wait or abort
00125             // F_RDLCK   Reader  Can't happen, anyway, wait or abort            
00126             // F_UNLCK   Reader  Take reader lock
00127             // F_UNLCK   Writer  Take writer lock
00128             
00129             
00130 
00131             // wait or abort
00132             if (  lock.l_type != F_UNLCK )
00133             {
00134                 // some lock conflicts with us.
00135                 LMIL << "pid " << lock.l_pid << " is running and has a lock that conflicts with us." << std::endl;
00136                 
00137                 // abort if we have slept more or equal than the timeout, but
00138                 // not for the case where timeout is negative which means no
00139                 // timeout and therefore we never abort.
00140                 if ( (totalslept >= _options.timeout) && (_options.timeout >= 0 ) )
00141                 {
00142                     ZYPP_THROW(ZYppLockedException(                                       
00143                                    _("This action is being run by another program already."),
00144                                    _options.name, lock.l_pid));
00145                 }
00146                         
00147                 // if not, let sleep one second and count it
00148                 LMIL << "waiting 1 second..." << endl;
00149                 sleep(1);
00150                 ++totalslept;
00151                 continue;
00152             }
00153             else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Reader ) )
00154             {
00155                 // either there is no lock or a reader has it so we just
00156                 // acquire a reader lock.
00157 
00158                 // try to get more lock info
00159                 lock.l_type = F_WRLCK;
00160  
00161                 if (fcntl(_fd->fd(), F_GETLK, &lock) < 0)
00162                 {
00163                     ZYPP_THROW(Exception(string("Error getting lock info: ") +  strerror(errno)));
00164                 }
00165                 
00166                 if ( lock.l_type == F_UNLCK )
00167                 {
00168                     LMIL << "no previous readers, unlinking lock file and retrying." << endl;
00169                     
00170                     // actually there are no readers
00171                     // lets delete it and break, so the next loop will
00172                     // probably succeed in creating it. The worst thing that can
00173                     // happen is that another process will take it first, but
00174                     // we are not aiming at such level of correctness. Otherwise
00175                     // the code path will complicate too much.
00176                     memset(&lock, 0, sizeof(struct flock));
00177                     lock.l_type = F_WRLCK;
00178                     lock.l_whence = SEEK_SET;
00179                     lock.l_pid = getpid();
00180 
00181                     if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
00182                     {
00183                         ZYPP_THROW (Exception( "Can't lock file to unlink it."));
00184                     }
00185                     filesystem::unlink(lock_file.c_str());
00186                     continue;
00187                 }
00188                 else if ( lock.l_type == F_RDLCK )
00189                 {
00190                     // there is another reader.
00191                     LMIL << "previous readers on lock file. taking lock as a reader." << std::endl;
00192                     memset(&lock, 0, sizeof(struct flock));
00193                     lock.l_type = F_RDLCK;
00194                     lock.l_whence = SEEK_SET;
00195                     lock.l_pid = getpid();
00196 
00197                     if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
00198                     {
00199                         ZYPP_THROW (Exception( "Can't lock file for reader"));
00200                     }
00201                     // and keep the lock open.
00202                     break;
00203                 }
00204                 else
00205                 {
00206                     // cant happen!
00207                     ERR << "impossible condition" << endl;
00208                     
00209                     break;
00210                 }
00211             }
00212             else if ( ( lock.l_type == F_UNLCK ) && ( _options.type == Writer ) )
00213             {
00214                 LMIL << "stale lock found" << endl;
00215                 // Nobody conflicts with a writer lock so nobody is actually
00216                 // locking.
00217                 // lets delete it and break, so the next loop will
00218                 // probably succeed in creating it. The worst thing that can
00219                 // happen is that another process will take it first, but
00220                 // we are not aiming at such level of correctness. Otherwise
00221                 // the code path will complicate too much.
00222                 memset(&lock, 0, sizeof(struct flock));
00223                 lock.l_type = F_WRLCK;
00224                 lock.l_whence = SEEK_SET;
00225                 lock.l_pid = getpid();
00226 
00227                 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
00228                 {
00229                     ZYPP_THROW (Exception( "Can't lock file to unlink it."));
00230                 }
00231                 filesystem::unlink(lock_file.c_str());
00232                 continue;
00233             } 
00234             else 
00235             {
00236                 // undefined case, just get out of the loop
00237                 LMIL << "undefined case!" << endl;
00238                 
00239                 break;
00240             }
00241             
00242         }
00243         else 
00244         {
00245             // exclusive file creation succeeded. So may be we are the
00246             // first writer or first reader
00247             
00248             // try to lock it exclusively
00249             // if it fails, someone won us, so we just go for another try
00250             // or just abort
00251             LMIL << "no lock found, taking ownership of it as a " << ( (_options.type == Reader ) ? "reader" : "writer" ) << endl;
00252             struct flock lock;
00253             memset(&lock, 0, sizeof(struct flock));
00254             lock.l_whence = SEEK_SET;
00255             lock.l_type = F_WRLCK;
00256             lock.l_pid = getpid();
00257 
00258             if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
00259                 ZYPP_THROW (Exception( "Can't lock file to write pid."));
00260             
00261             char buffer[100];
00262             sprintf( buffer, "%d\n", curr_pid);
00263             write( _fd->fd(), buffer, strlen(buffer));
00264             
00265             // by now the pid is written and the file locked.
00266             // If we are a reader, just downgrade the lock to
00267             // read shared lock.
00268             if ( _options.type == Reader )
00269             {
00270                 lock.l_type = F_RDLCK;
00271                
00272                 if (fcntl(_fd->fd(), F_SETLK, &lock) < 0)
00273                     ZYPP_THROW (Exception( "Can't set lock file to shared"));
00274             }
00275             
00276             break;
00277         }           
00278     } // end loop       
00279 
00280     LMIL << "Lock intialized" << endl;
00281     
00282 }
00283 
00284 InterProcessMutex::~InterProcessMutex()
00285 {
00286     try
00287     {
00288         Pathname lock_file = lockFilePath();
00289         LMIL << "dropping " 
00290              << ( (_options.type == Reader ) ? "reader" : "writer" ) 
00291              << " lock on " << lock_file << endl;
00292         
00293         switch ( _options.type )
00294         {
00295             case Reader:
00296                 
00297                 break;
00298                 
00299             case Writer:
00300                 // we are the only writer, so unlink the file
00301                 filesystem::unlink(lock_file.c_str());
00302                 break;
00303                 
00304         }
00305         // and finally close the file and release the lock
00306         // (happens automatically)
00307     }
00308     catch(...) {} // let no exception escape.
00309 }
00310 
00311 
00312 Pathname InterProcessMutex::lockFilePath() const
00313 {
00314     filesystem::assert_dir(_options.base);
00315     return _options.base + ("zypp-" + _options.name + ".lock");
00316 }    
00317 
00318 bool InterProcessMutex::isProcessRunning(pid_t pid_r)
00319 {
00320     // it is another program, not me, see if it is still running
00321     Pathname procdir( Pathname("/proc") / str::numstring(pid_r) );
00322 
00323     PathInfo status( procdir/"status" );
00324     XXX << "Checking " <<  status << endl;
00325     bool still_running = status.isExist();
00326 
00327     if ( still_running )
00328     {
00329         Pathname p( procdir/"exe" );
00330         XXX << p << " -> " << filesystem::readlink( p ) << endl;
00331 
00332         p = procdir/"cmdline";
00333         XXX << p << ": ";
00334         std::ifstream infile( p.c_str() );
00335         for( iostr::EachLine in( infile ); in; in.next() )
00336         {
00337           XXX << *in << endl;
00338         }
00339      }
00340 
00341      return still_running;
00342 }
00343 
00344 
00345 }
00346 }
00347 
00348