libzypp
10.5.0
|
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