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 <kde_file.h> 00040 #include <kapplication.h> 00041 #include <kcmdlineargs.h> 00042 #include <kglobal.h> 00043 #include <ktempfile.h> 00044 00045 // TODO: http://www.spinnaker.de/linux/nfs-locking.html 00046 // TODO: Make regression test 00047 00048 class KLockFile::KLockFilePrivate { 00049 public: 00050 QString file; 00051 int staleTime; 00052 bool isLocked; 00053 bool recoverLock; 00054 QTime staleTimer; 00055 KDE_struct_stat statBuf; 00056 int pid; 00057 QString hostname; 00058 QString instance; 00059 QString lockRecoverFile; 00060 }; 00061 00062 00063 // 30 seconds 00064 KLockFile::KLockFile(const QString &file) 00065 { 00066 d = new KLockFilePrivate(); 00067 d->file = file; 00068 d->staleTime = 30; 00069 d->isLocked = false; 00070 d->recoverLock = false; 00071 } 00072 00073 KLockFile::~KLockFile() 00074 { 00075 unlock(); 00076 delete d; 00077 } 00078 00079 int 00080 KLockFile::staleTime() const 00081 { 00082 return d->staleTime; 00083 } 00084 00085 00086 void 00087 KLockFile::setStaleTime(int _staleTime) 00088 { 00089 d->staleTime = _staleTime; 00090 } 00091 00092 static bool statResultIsEqual(KDE_struct_stat &st_buf1, KDE_struct_stat &st_buf2) 00093 { 00094 #define FIELD_EQ(what) (st_buf1.what == st_buf2.what) 00095 return FIELD_EQ(st_dev) && FIELD_EQ(st_ino) && 00096 FIELD_EQ(st_uid) && FIELD_EQ(st_gid) && FIELD_EQ(st_nlink); 00097 #undef FIELD_EQ 00098 } 00099 00100 static KLockFile::LockResult lockFile(const QString &lockFile, KDE_struct_stat &st_buf) 00101 { 00102 QCString lockFileName = QFile::encodeName( lockFile ); 00103 int result = KDE_lstat( lockFileName, &st_buf ); 00104 if (result == 0) 00105 return KLockFile::LockFail; 00106 00107 KTempFile uniqueFile(lockFile, QString::null, 0644); 00108 uniqueFile.setAutoDelete(true); 00109 if (uniqueFile.status() != 0) 00110 return KLockFile::LockError; 00111 00112 char hostname[256]; 00113 hostname[0] = 0; 00114 gethostname(hostname, 255); 00115 hostname[255] = 0; 00116 QCString instanceName = KCmdLineArgs::appName(); 00117 00118 (*(uniqueFile.textStream())) << QString::number(getpid()) << endl 00119 << instanceName << endl 00120 << hostname << endl; 00121 uniqueFile.close(); 00122 00123 QCString uniqueName = QFile::encodeName( uniqueFile.name() ); 00124 00125 #ifdef Q_OS_UNIX 00126 // Create lock file 00127 result = ::link( uniqueName, lockFileName ); 00128 if (result != 0) 00129 return KLockFile::LockError; 00130 00131 #else 00132 //TODO for win32 00133 return KLockFile::LockOK; 00134 #endif 00135 00136 KDE_struct_stat st_buf2; 00137 result = KDE_lstat( uniqueName, &st_buf2 ); 00138 if (result != 0) 00139 return KLockFile::LockError; 00140 00141 result = KDE_lstat( lockFileName, &st_buf ); 00142 if (result != 0) 00143 return KLockFile::LockError; 00144 00145 if (!statResultIsEqual(st_buf, st_buf2) || S_ISLNK(st_buf.st_mode) || S_ISLNK(st_buf2.st_mode)) 00146 return KLockFile::LockFail; 00147 00148 return KLockFile::LockOK; 00149 } 00150 00151 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, KDE_struct_stat &st_buf) 00152 { 00153 // This is dangerous, we could be deleting a new lock instead of 00154 // the old stale one, let's be very careful 00155 00156 // Create temp file 00157 KTempFile ktmpFile(lockFile); 00158 if (ktmpFile.status() != 0) 00159 return KLockFile::LockError; 00160 00161 QCString lckFile = QFile::encodeName( lockFile ); 00162 QCString tmpFile = QFile::encodeName(ktmpFile.name()); 00163 ktmpFile.close(); 00164 ktmpFile.unlink(); 00165 00166 #ifdef Q_OS_UNIX 00167 // link to lock file 00168 if (::link(lckFile, tmpFile) != 0) 00169 return KLockFile::LockFail; // Try again later 00170 #else 00171 //TODO for win32 00172 return KLockFile::LockOK; 00173 #endif 00174 00175 // check if link count increased with exactly one 00176 // and if the lock file still matches 00177 KDE_struct_stat st_buf1; 00178 KDE_struct_stat st_buf2; 00179 memcpy(&st_buf1, &st_buf, sizeof(KDE_struct_stat)); 00180 st_buf1.st_nlink++; 00181 if ((KDE_lstat(tmpFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2)) 00182 { 00183 if ((KDE_lstat(lckFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2)) 00184 { 00185 // - - if yes, delete lock file, delete temp file, retry lock 00186 qWarning("WARNING: deleting stale lockfile %s", lckFile.data()); 00187 ::unlink(lckFile); 00188 ::unlink(tmpFile); 00189 return KLockFile::LockOK; 00190 } 00191 } 00192 // Failed to delete stale lock file 00193 qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data()); 00194 ::unlink(tmpFile); 00195 return KLockFile::LockFail; 00196 } 00197 00198 00199 KLockFile::LockResult KLockFile::lock(int options) 00200 { 00201 if (d->isLocked) 00202 return KLockFile::LockOK; 00203 00204 KLockFile::LockResult result; 00205 int hardErrors = 5; 00206 int n = 5; 00207 while(true) 00208 { 00209 KDE_struct_stat st_buf; 00210 result = lockFile(d->file, st_buf); 00211 if (result == KLockFile::LockOK) 00212 { 00213 d->staleTimer = QTime(); 00214 break; 00215 } 00216 else if (result == KLockFile::LockError) 00217 { 00218 d->staleTimer = QTime(); 00219 if (--hardErrors == 0) 00220 { 00221 break; 00222 } 00223 } 00224 else // KLockFile::Fail 00225 { 00226 if (!d->staleTimer.isNull() && !statResultIsEqual(d->statBuf, st_buf)) 00227 d->staleTimer = QTime(); 00228 00229 if (!d->staleTimer.isNull()) 00230 { 00231 bool isStale = false; 00232 if ((d->pid > 0) && !d->hostname.isEmpty()) 00233 { 00234 // Check if hostname is us 00235 char hostname[256]; 00236 hostname[0] = 0; 00237 gethostname(hostname, 255); 00238 hostname[255] = 0; 00239 00240 if (d->hostname == hostname) 00241 { 00242 // Check if pid still exists 00243 int res = ::kill(d->pid, 0); 00244 if ((res == -1) && (errno == ESRCH)) 00245 isStale = true; 00246 } 00247 } 00248 if (d->staleTimer.elapsed() > (d->staleTime*1000)) 00249 isStale = true; 00250 00251 if (isStale) 00252 { 00253 if ((options & LockForce) == 0) 00254 return KLockFile::LockStale; 00255 00256 result = deleteStaleLock(d->file, d->statBuf); 00257 00258 if (result == KLockFile::LockOK) 00259 { 00260 // Lock deletion successful 00261 d->staleTimer = QTime(); 00262 continue; // Now try to get the new lock 00263 } 00264 else if (result != KLockFile::LockFail) 00265 { 00266 return result; 00267 } 00268 } 00269 } 00270 else 00271 { 00272 memcpy(&(d->statBuf), &st_buf, sizeof(KDE_struct_stat)); 00273 d->staleTimer.start(); 00274 00275 d->pid = -1; 00276 d->hostname = QString::null; 00277 d->instance = QString::null; 00278 00279 QFile file(d->file); 00280 if (file.open(IO_ReadOnly)) 00281 { 00282 QTextStream ts(&file); 00283 if (!ts.atEnd()) 00284 d->pid = ts.readLine().toInt(); 00285 if (!ts.atEnd()) 00286 d->instance = ts.readLine(); 00287 if (!ts.atEnd()) 00288 d->hostname = ts.readLine(); 00289 } 00290 } 00291 } 00292 00293 if ((options & LockNoBlock) != 0) 00294 break; 00295 00296 struct timeval tv; 00297 tv.tv_sec = 0; 00298 tv.tv_usec = n*((KApplication::random() % 200)+100); 00299 if (n < 2000) 00300 n = n * 2; 00301 00302 #ifdef Q_OS_UNIX 00303 select(0, 0, 0, 0, &tv); 00304 #else 00305 //TODO for win32 00306 #endif 00307 } 00308 if (result == LockOK) 00309 d->isLocked = true; 00310 return result; 00311 } 00312 00313 bool KLockFile::isLocked() const 00314 { 00315 return d->isLocked; 00316 } 00317 00318 void KLockFile::unlock() 00319 { 00320 if (d->isLocked) 00321 { 00322 ::unlink(QFile::encodeName(d->file)); 00323 d->isLocked = false; 00324 } 00325 } 00326 00327 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname) 00328 { 00329 if (d->pid == -1) 00330 return false; 00331 pid = d->pid; 00332 hostname = d->hostname; 00333 appname = d->instance; 00334 return true; 00335 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:03:30 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003