kdecore Library API Documentation

klockfile.cpp

00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2004 Waldo Bastian <bastian@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 #include <klockfile.h> 00021 00022 #include <config.h> 00023 00024 #include <sys/types.h> 00025 #ifdef HAVE_SYS_STAT_H 00026 #include <sys/stat.h> 00027 #endif 00028 #ifdef HAVE_SYS_TIME_H 00029 #include <sys/time.h> 00030 #endif 00031 #include <signal.h> 00032 #include <errno.h> 00033 #include <stdlib.h> 00034 #include <unistd.h> 00035 00036 #include <qfile.h> 00037 #include <qtextstream.h> 00038 00039 #include <kapplication.h> 00040 #include <kcmdlineargs.h> 00041 #include <kglobal.h> 00042 #include <ktempfile.h> 00043 00044 // TODO: http://www.spinnaker.de/linux/nfs-locking.html 00045 // TODO: Make regression test 00046 00047 class KLockFile::KLockFilePrivate { 00048 public: 00049 QString file; 00050 int staleTime; 00051 bool isLocked; 00052 bool recoverLock; 00053 QTime staleTimer; 00054 struct stat statBuf; 00055 int pid; 00056 QString hostname; 00057 QString instance; 00058 QString lockRecoverFile; 00059 }; 00060 00061 00062 // 30 seconds 00063 KLockFile::KLockFile(const QString &file) 00064 { 00065 d = new KLockFilePrivate(); 00066 d->file = file; 00067 d->staleTime = 30; 00068 d->isLocked = false; 00069 d->recoverLock = false; 00070 } 00071 00072 KLockFile::~KLockFile() 00073 { 00074 unlock(); 00075 delete d; 00076 } 00077 00078 int 00079 KLockFile::staleTime() const 00080 { 00081 return d->staleTime; 00082 } 00083 00084 00085 void 00086 KLockFile::setStaleTime(int _staleTime) 00087 { 00088 d->staleTime = _staleTime; 00089 } 00090 00091 static bool statResultIsEqual(struct stat &st_buf1, struct stat &st_buf2) 00092 { 00093 #define FIELD_EQ(what) (st_buf1.what == st_buf2.what) 00094 return FIELD_EQ(st_dev) && FIELD_EQ(st_ino) && 00095 FIELD_EQ(st_uid) && FIELD_EQ(st_gid) && FIELD_EQ(st_nlink); 00096 #undef FIELD_EQ 00097 } 00098 00099 static KLockFile::LockResult lockFile(const QString &lockFile, struct stat &st_buf) 00100 { 00101 QCString lockFileName = QFile::encodeName( lockFile ); 00102 int result = ::lstat( lockFileName, &st_buf ); 00103 if (result == 0) 00104 return KLockFile::LockFail; 00105 00106 KTempFile uniqueFile(lockFile, QString::null, 0644); 00107 uniqueFile.setAutoDelete(true); 00108 if (uniqueFile.status() != 0) 00109 return KLockFile::LockError; 00110 00111 char hostname[256]; 00112 hostname[0] = 0; 00113 gethostname(hostname, 255); 00114 hostname[255] = 0; 00115 QCString instanceName = KCmdLineArgs::appName(); 00116 00117 (*(uniqueFile.textStream())) << QString::number(getpid()) << endl 00118 << instanceName << endl 00119 << hostname << endl; 00120 uniqueFile.close(); 00121 00122 QCString uniqueName = QFile::encodeName( uniqueFile.name() ); 00123 00124 // Create lock file 00125 result = ::link( uniqueName, lockFileName ); 00126 if (result != 0) 00127 return KLockFile::LockError; 00128 00129 struct stat st_buf2; 00130 result = ::lstat( uniqueName, &st_buf2 ); 00131 if (result != 0) 00132 return KLockFile::LockError; 00133 00134 result = ::lstat( lockFileName, &st_buf ); 00135 if (result != 0) 00136 return KLockFile::LockError; 00137 00138 if (!statResultIsEqual(st_buf, st_buf2) || S_ISLNK(st_buf.st_mode) || S_ISLNK(st_buf2.st_mode)) 00139 return KLockFile::LockFail; 00140 00141 return KLockFile::LockOK; 00142 } 00143 00144 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, struct stat &st_buf) 00145 { 00146 // This is dangerous, we could be deleting a new lock instead of 00147 // the old stale one, let's be very careful 00148 00149 // Create temp file 00150 KTempFile ktmpFile(lockFile); 00151 if (ktmpFile.status() != 0) 00152 return KLockFile::LockError; 00153 00154 QCString lckFile = QFile::encodeName( lockFile ); 00155 QCString tmpFile = QFile::encodeName(ktmpFile.name()); 00156 ktmpFile.close(); 00157 ktmpFile.unlink(); 00158 00159 // link to lock file 00160 if (::link(lckFile, tmpFile) != 0) 00161 return KLockFile::LockFail; // Try again later 00162 00163 // check if link count increased with exactly one 00164 // and if the lock file still matches 00165 struct stat st_buf1; 00166 struct stat st_buf2; 00167 memcpy(&st_buf1, &st_buf, sizeof(struct stat)); 00168 st_buf1.st_nlink++; 00169 if ((lstat(tmpFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2)) 00170 { 00171 if ((lstat(lckFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2)) 00172 { 00173 // - - if yes, delete lock file, delete temp file, retry lock 00174 qWarning("WARNING: deleting stale lockfile %s", lckFile.data()); 00175 ::unlink(lckFile); 00176 ::unlink(tmpFile); 00177 return KLockFile::LockOK; 00178 } 00179 } 00180 // Failed to delete stale lock file 00181 qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data()); 00182 ::unlink(tmpFile); 00183 return KLockFile::LockFail; 00184 } 00185 00186 00187 KLockFile::LockResult KLockFile::lock(int options) 00188 { 00189 if (d->isLocked) 00190 return KLockFile::LockOK; 00191 00192 KLockFile::LockResult result; 00193 int hardErrors = 5; 00194 int n = 5; 00195 while(true) 00196 { 00197 struct stat st_buf; 00198 result = lockFile(d->file, st_buf); 00199 if (result == KLockFile::LockOK) 00200 { 00201 d->staleTimer = QTime(); 00202 break; 00203 } 00204 else if (result == KLockFile::LockError) 00205 { 00206 d->staleTimer = QTime(); 00207 if (--hardErrors == 0) 00208 { 00209 break; 00210 } 00211 } 00212 else // KLockFile::Fail 00213 { 00214 if (!d->staleTimer.isNull() && !statResultIsEqual(d->statBuf, st_buf)) 00215 d->staleTimer = QTime(); 00216 00217 if (!d->staleTimer.isNull()) 00218 { 00219 bool isStale = false; 00220 if ((d->pid > 0) && !d->hostname.isEmpty()) 00221 { 00222 // Check if hostname is us 00223 char hostname[256]; 00224 hostname[0] = 0; 00225 gethostname(hostname, 255); 00226 hostname[255] = 0; 00227 00228 if (d->hostname == hostname) 00229 { 00230 // Check if pid still exists 00231 int res = ::kill(d->pid, 0); 00232 if ((res == -1) && (errno == ESRCH)) 00233 isStale = true; 00234 } 00235 } 00236 if (d->staleTimer.elapsed() > (d->staleTime*1000)) 00237 isStale = true; 00238 00239 if (isStale) 00240 { 00241 if ((options & LockForce) == 0) 00242 return KLockFile::LockStale; 00243 00244 result = deleteStaleLock(d->file, d->statBuf); 00245 00246 if (result == KLockFile::LockOK) 00247 { 00248 // Lock deletion successful 00249 d->staleTimer = QTime(); 00250 continue; // Now try to get the new lock 00251 } 00252 else if (result != KLockFile::LockFail) 00253 { 00254 return result; 00255 } 00256 } 00257 } 00258 else 00259 { 00260 memcpy(&(d->statBuf), &st_buf, sizeof(struct stat)); 00261 d->staleTimer.start(); 00262 00263 d->pid = -1; 00264 d->hostname = QString::null; 00265 d->instance = QString::null; 00266 00267 QFile file(d->file); 00268 if (file.open(IO_ReadOnly)) 00269 { 00270 QTextStream ts(&file); 00271 if (!ts.atEnd()) 00272 d->pid = ts.readLine().toInt(); 00273 if (!ts.atEnd()) 00274 d->instance = ts.readLine(); 00275 if (!ts.atEnd()) 00276 d->hostname = ts.readLine(); 00277 } 00278 } 00279 } 00280 00281 if ((options & LockNoBlock) != 0) 00282 break; 00283 00284 struct timeval tv; 00285 tv.tv_sec = 0; 00286 tv.tv_usec = n*((KApplication::random() % 200)+100); 00287 if (n < 2000) 00288 n = n * 2; 00289 00290 select(0, 0, 0, 0, &tv); 00291 } 00292 if (result == LockOK) 00293 d->isLocked = true; 00294 return result; 00295 } 00296 00297 bool KLockFile::isLocked() const 00298 { 00299 return d->isLocked; 00300 } 00301 00302 void KLockFile::unlock() 00303 { 00304 if (d->isLocked) 00305 { 00306 ::unlink(QFile::encodeName(d->file)); 00307 d->isLocked = false; 00308 } 00309 } 00310 00311 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname) 00312 { 00313 if (d->pid == -1) 00314 return false; 00315 pid = d->pid; 00316 hostname = d->hostname; 00317 appname = d->instance; 00318 return true; 00319 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:40:08 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003