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