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 if ((st_buf.st_nlink == 1) && (st_buf2.st_nlink == 1) && (st_buf.st_ino != st_buf2.st_ino))
00177 {
00178 linkCountSupport = testLinkCountSupport(uniqueName);
00179 if (!linkCountSupport)
00180 return KLockFile::LockOK;
00181 }
00182 return KLockFile::LockFail;
00183 }
00184
00185 return KLockFile::LockOK;
00186 }
00187
00188 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, KDE_struct_stat &st_buf, bool &linkCountSupport, const KComponentData &componentData)
00189 {
00190
00191
00192
00193
00194 KTemporaryFile *ktmpFile = new KTemporaryFile(componentData);
00195 ktmpFile->setFileTemplate(lockFile);
00196 if (!ktmpFile->open())
00197 return KLockFile::LockError;
00198
00199 QByteArray lckFile = QFile::encodeName(lockFile);
00200 QByteArray tmpFile = QFile::encodeName(ktmpFile->fileName());
00201 delete ktmpFile;
00202
00203
00204 if (::link(lckFile, tmpFile) != 0)
00205 return KLockFile::LockFail;
00206
00207
00208
00209 KDE_struct_stat st_buf1;
00210 KDE_struct_stat st_buf2;
00211 memcpy(&st_buf1, &st_buf, sizeof(KDE_struct_stat));
00212 st_buf1.st_nlink++;
00213 if ((KDE_lstat(tmpFile, &st_buf2) == 0) && st_buf1 == st_buf2)
00214 {
00215 if ((KDE_lstat(lckFile, &st_buf2) == 0) && st_buf1 == st_buf2)
00216 {
00217
00218 qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00219 ::unlink(lckFile);
00220 ::unlink(tmpFile);
00221 return KLockFile::LockOK;
00222 }
00223 }
00224
00225
00226 if (linkCountSupport)
00227 {
00228 linkCountSupport = testLinkCountSupport(tmpFile);
00229 }
00230
00231 if (!linkCountSupport &&
00232 (KDE_lstat(lckFile, &st_buf2) == 0) && st_buf == st_buf2)
00233 {
00234
00235 qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00236 ::unlink(lckFile);
00237 ::unlink(tmpFile);
00238 return KLockFile::LockOK;
00239 }
00240
00241
00242 qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data());
00243 ::unlink(tmpFile);
00244 return KLockFile::LockFail;
00245 }
00246
00247
00248 KLockFile::LockResult KLockFile::lock(LockFlags options)
00249 {
00250 if (d->isLocked)
00251 return KLockFile::LockOK;
00252
00253 KLockFile::LockResult result;
00254 int hardErrors = 5;
00255 int n = 5;
00256 while(true)
00257 {
00258 KDE_struct_stat st_buf;
00259 result = lockFile(d->file, st_buf, d->linkCountSupport, d->componentData);
00260 if (result == KLockFile::LockOK)
00261 {
00262 d->staleTimer = QTime();
00263 break;
00264 }
00265 else if (result == KLockFile::LockError)
00266 {
00267 d->staleTimer = QTime();
00268 if (--hardErrors == 0)
00269 {
00270 break;
00271 }
00272 }
00273 else
00274 {
00275 if (!d->staleTimer.isNull() && d->statBuf != st_buf)
00276 d->staleTimer = QTime();
00277
00278 if (!d->staleTimer.isNull())
00279 {
00280 bool isStale = false;
00281 if ((d->pid > 0) && !d->hostname.isEmpty())
00282 {
00283
00284 char hostname[256];
00285 hostname[0] = 0;
00286 gethostname(hostname, 255);
00287 hostname[255] = 0;
00288
00289 if (d->hostname == QLatin1String(hostname))
00290 {
00291
00292 int res = ::kill(d->pid, 0);
00293 if ((res == -1) && (errno == ESRCH))
00294 isStale = true;
00295 }
00296 }
00297 if (d->staleTimer.elapsed() > (d->staleTime*1000))
00298 isStale = true;
00299
00300 if (isStale)
00301 {
00302 if ((options & ForceFlag) == 0)
00303 return KLockFile::LockStale;
00304
00305 result = deleteStaleLock(d->file, d->statBuf, d->linkCountSupport, d->componentData);
00306
00307 if (result == KLockFile::LockOK)
00308 {
00309
00310 d->staleTimer = QTime();
00311 continue;
00312 }
00313 else if (result != KLockFile::LockFail)
00314 {
00315 return result;
00316 }
00317 }
00318 }
00319 else
00320 {
00321 memcpy(&(d->statBuf), &st_buf, sizeof(KDE_struct_stat));
00322 d->staleTimer.start();
00323
00324 d->pid = -1;
00325 d->hostname.clear();
00326 d->instance.clear();
00327
00328 QFile file(d->file);
00329 if (file.open(QIODevice::ReadOnly))
00330 {
00331 QTextStream ts(&file);
00332 if (!ts.atEnd())
00333 d->pid = ts.readLine().toInt();
00334 if (!ts.atEnd())
00335 d->instance = ts.readLine();
00336 if (!ts.atEnd())
00337 d->hostname = ts.readLine();
00338 }
00339 }
00340 }
00341
00342 if ((options & NoBlockFlag) != 0)
00343 break;
00344
00345 struct timeval tv;
00346 tv.tv_sec = 0;
00347 tv.tv_usec = n*((KRandom::random() % 200)+100);
00348 if (n < 2000)
00349 n = n * 2;
00350
00351 select(0, 0, 0, 0, &tv);
00352 }
00353 if (result == LockOK)
00354 d->isLocked = true;
00355 return result;
00356 }
00357
00358 bool KLockFile::isLocked() const
00359 {
00360 return d->isLocked;
00361 }
00362
00363 void KLockFile::unlock()
00364 {
00365 if (d->isLocked)
00366 {
00367 ::unlink(QFile::encodeName(d->file));
00368 d->isLocked = false;
00369 }
00370 }
00371
00372 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname)
00373 {
00374 if (d->pid == -1)
00375 return false;
00376 pid = d->pid;
00377 hostname = d->hostname;
00378 appname = d->instance;
00379 return true;
00380 }