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
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
00069
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
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
00090
00091
00092 lock.l_type = ( ( _options.type == Writer ) ? F_WRLCK : F_RDLCK );
00093
00094
00095
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
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132 if ( lock.l_type != F_UNLCK )
00133 {
00134
00135 LMIL << "pid " << lock.l_pid << " is running and has a lock that conflicts with us." << std::endl;
00136
00137
00138
00139
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
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
00156
00157
00158
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
00171
00172
00173
00174
00175
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
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
00202 break;
00203 }
00204 else
00205 {
00206
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
00216
00217
00218
00219
00220
00221
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
00237 LMIL << "undefined case!" << endl;
00238
00239 break;
00240 }
00241
00242 }
00243 else
00244 {
00245
00246
00247
00248
00249
00250
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
00266
00267
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 }
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
00301 filesystem::unlink(lock_file.c_str());
00302 break;
00303
00304 }
00305
00306
00307 }
00308 catch(...) {}
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
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