KDECore
klockfile.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00046
00047
00048 class KLockFile::KLockFilePrivate {
00049 public:
00050 QString file;
00051 int staleTime;
00052 bool isLocked;
00053 bool recoverLock;
00054 bool linkCountSupport;
00055 QTime staleTimer;
00056 KDE_struct_stat statBuf;
00057 int pid;
00058 QString hostname;
00059 QString instance;
00060 QString lockRecoverFile;
00061 };
00062
00063
00064
00065 KLockFile::KLockFile(const QString &file)
00066 {
00067 d = new KLockFilePrivate();
00068 d->file = file;
00069 d->staleTime = 30;
00070 d->isLocked = false;
00071 d->recoverLock = false;
00072 d->linkCountSupport = true;
00073 }
00074
00075 KLockFile::~KLockFile()
00076 {
00077 unlock();
00078 delete d;
00079 }
00080
00081 int
00082 KLockFile::staleTime() const
00083 {
00084 return d->staleTime;
00085 }
00086
00087
00088 void
00089 KLockFile::setStaleTime(int _staleTime)
00090 {
00091 d->staleTime = _staleTime;
00092 }
00093
00094 static bool statResultIsEqual(KDE_struct_stat &st_buf1, KDE_struct_stat &st_buf2)
00095 {
00096 #define FIELD_EQ(what) (st_buf1.what == st_buf2.what)
00097 return FIELD_EQ(st_dev) && FIELD_EQ(st_ino) &&
00098 FIELD_EQ(st_uid) && FIELD_EQ(st_gid) && FIELD_EQ(st_nlink);
00099 #undef FIELD_EQ
00100 }
00101
00102 static bool testLinkCountSupport(const QCString &fileName)
00103 {
00104 KDE_struct_stat st_buf;
00105
00106 ::link( fileName, fileName+".test" );
00107 int result = KDE_lstat( fileName, &st_buf );
00108 ::unlink( fileName+".test" );
00109 return ((result == 0) && (st_buf.st_nlink == 2));
00110 }
00111
00112 static KLockFile::LockResult lockFile(const QString &lockFile, KDE_struct_stat &st_buf, bool &linkCountSupport)
00113 {
00114 QCString lockFileName = QFile::encodeName( lockFile );
00115 int result = KDE_lstat( lockFileName, &st_buf );
00116 if (result == 0)
00117 return KLockFile::LockFail;
00118
00119 KTempFile uniqueFile(lockFile, QString::null, 0644);
00120 uniqueFile.setAutoDelete(true);
00121 if (uniqueFile.status() != 0)
00122 return KLockFile::LockError;
00123
00124 char hostname[256];
00125 hostname[0] = 0;
00126 gethostname(hostname, 255);
00127 hostname[255] = 0;
00128 QCString instanceName = KCmdLineArgs::appName();
00129
00130 (*(uniqueFile.textStream())) << QString::number(getpid()) << endl
00131 << instanceName << endl
00132 << hostname << endl;
00133 uniqueFile.close();
00134
00135 QCString uniqueName = QFile::encodeName( uniqueFile.name() );
00136
00137 #ifdef Q_OS_UNIX
00138
00139 result = ::link( uniqueName, lockFileName );
00140 if (result != 0)
00141 return KLockFile::LockError;
00142
00143 if (!linkCountSupport)
00144 return KLockFile::LockOK;
00145 #else
00146
00147 return KLockFile::LockOK;
00148 #endif
00149
00150 KDE_struct_stat st_buf2;
00151 result = KDE_lstat( uniqueName, &st_buf2 );
00152 if (result != 0)
00153 return KLockFile::LockError;
00154
00155 result = KDE_lstat( lockFileName, &st_buf );
00156 if (result != 0)
00157 return KLockFile::LockError;
00158
00159 if (!statResultIsEqual(st_buf, st_buf2) || S_ISLNK(st_buf.st_mode) || S_ISLNK(st_buf2.st_mode))
00160 {
00161
00162 if ((st_buf.st_nlink == 1) && (st_buf2.st_nlink == 1) && (st_buf.st_ino != st_buf2.st_ino))
00163 {
00164 linkCountSupport = testLinkCountSupport(uniqueName);
00165 if (!linkCountSupport)
00166 return KLockFile::LockOK;
00167 }
00168 return KLockFile::LockFail;
00169 }
00170
00171 return KLockFile::LockOK;
00172 }
00173
00174 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, KDE_struct_stat &st_buf, bool &linkCountSupport)
00175 {
00176
00177
00178
00179
00180 KTempFile ktmpFile(lockFile);
00181 if (ktmpFile.status() != 0)
00182 return KLockFile::LockError;
00183
00184 QCString lckFile = QFile::encodeName(lockFile);
00185 QCString tmpFile = QFile::encodeName(ktmpFile.name());
00186 ktmpFile.close();
00187 ktmpFile.unlink();
00188
00189 #ifdef Q_OS_UNIX
00190
00191 if (::link(lckFile, tmpFile) != 0)
00192 return KLockFile::LockFail;
00193 #else
00194
00195 return KLockFile::LockOK;
00196 #endif
00197
00198
00199
00200 KDE_struct_stat st_buf1;
00201 KDE_struct_stat st_buf2;
00202 memcpy(&st_buf1, &st_buf, sizeof(KDE_struct_stat));
00203 st_buf1.st_nlink++;
00204 if ((KDE_lstat(tmpFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2))
00205 {
00206 if ((KDE_lstat(lckFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2))
00207 {
00208
00209 qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00210 ::unlink(lckFile);
00211 ::unlink(tmpFile);
00212 return KLockFile::LockOK;
00213 }
00214 }
00215
00216
00217 if (linkCountSupport)
00218 {
00219 linkCountSupport = testLinkCountSupport(tmpFile);
00220 }
00221
00222 if (!linkCountSupport &&
00223 (KDE_lstat(lckFile, &st_buf2) == 0) &&
00224 statResultIsEqual(st_buf, st_buf2))
00225 {
00226
00227 qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00228 ::unlink(lckFile);
00229 ::unlink(tmpFile);
00230 return KLockFile::LockOK;
00231 }
00232
00233
00234 qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data());
00235 ::unlink(tmpFile);
00236 return KLockFile::LockFail;
00237 }
00238
00239
00240 KLockFile::LockResult KLockFile::lock(int options)
00241 {
00242 if (d->isLocked)
00243 return KLockFile::LockOK;
00244
00245 KLockFile::LockResult result;
00246 int hardErrors = 5;
00247 int n = 5;
00248 while(true)
00249 {
00250 KDE_struct_stat st_buf;
00251 result = lockFile(d->file, st_buf, d->linkCountSupport);
00252 if (result == KLockFile::LockOK)
00253 {
00254 d->staleTimer = QTime();
00255 break;
00256 }
00257 else if (result == KLockFile::LockError)
00258 {
00259 d->staleTimer = QTime();
00260 if (--hardErrors == 0)
00261 {
00262 break;
00263 }
00264 }
00265 else
00266 {
00267 if (!d->staleTimer.isNull() && !statResultIsEqual(d->statBuf, st_buf))
00268 d->staleTimer = QTime();
00269
00270 if (!d->staleTimer.isNull())
00271 {
00272 bool isStale = false;
00273 if ((d->pid > 0) && !d->hostname.isEmpty())
00274 {
00275
00276 char hostname[256];
00277 hostname[0] = 0;
00278 gethostname(hostname, 255);
00279 hostname[255] = 0;
00280
00281 if (d->hostname == hostname)
00282 {
00283
00284 int res = ::kill(d->pid, 0);
00285 if ((res == -1) && (errno == ESRCH))
00286 isStale = true;
00287 }
00288 }
00289 if (d->staleTimer.elapsed() > (d->staleTime*1000))
00290 isStale = true;
00291
00292 if (isStale)
00293 {
00294 if ((options & LockForce) == 0)
00295 return KLockFile::LockStale;
00296
00297 result = deleteStaleLock(d->file, d->statBuf, d->linkCountSupport);
00298
00299 if (result == KLockFile::LockOK)
00300 {
00301
00302 d->staleTimer = QTime();
00303 continue;
00304 }
00305 else if (result != KLockFile::LockFail)
00306 {
00307 return result;
00308 }
00309 }
00310 }
00311 else
00312 {
00313 memcpy(&(d->statBuf), &st_buf, sizeof(KDE_struct_stat));
00314 d->staleTimer.start();
00315
00316 d->pid = -1;
00317 d->hostname = QString::null;
00318 d->instance = QString::null;
00319
00320 QFile file(d->file);
00321 if (file.open(IO_ReadOnly))
00322 {
00323 QTextStream ts(&file);
00324 if (!ts.atEnd())
00325 d->pid = ts.readLine().toInt();
00326 if (!ts.atEnd())
00327 d->instance = ts.readLine();
00328 if (!ts.atEnd())
00329 d->hostname = ts.readLine();
00330 }
00331 }
00332 }
00333
00334 if ((options & LockNoBlock) != 0)
00335 break;
00336
00337 struct timeval tv;
00338 tv.tv_sec = 0;
00339 tv.tv_usec = n*((KApplication::random() % 200)+100);
00340 if (n < 2000)
00341 n = n * 2;
00342
00343 #ifdef Q_OS_UNIX
00344 select(0, 0, 0, 0, &tv);
00345 #else
00346
00347 #endif
00348 }
00349 if (result == LockOK)
00350 d->isLocked = true;
00351 return result;
00352 }
00353
00354 bool KLockFile::isLocked() const
00355 {
00356 return d->isLocked;
00357 }
00358
00359 void KLockFile::unlock()
00360 {
00361 if (d->isLocked)
00362 {
00363 ::unlink(QFile::encodeName(d->file));
00364 d->isLocked = false;
00365 }
00366 }
00367
00368 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname)
00369 {
00370 if (d->pid == -1)
00371 return false;
00372 pid = d->pid;
00373 hostname = d->hostname;
00374 appname = d->instance;
00375 return true;
00376 }