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 <kapplication.h>
00040
#include <kcmdlineargs.h>
00041
#include <kglobal.h>
00042
#include <ktempfile.h>
00043
00044
00045
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
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
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
00147
00148
00149
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
00160
if (::link(lckFile, tmpFile) != 0)
00161
return KLockFile::LockFail;
00162
00163
00164
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
00174 qWarning(
"WARNING: deleting stale lockfile %s", lckFile.data());
00175 ::unlink(lckFile);
00176 ::unlink(tmpFile);
00177
return KLockFile::LockOK;
00178 }
00179 }
00180
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
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
00223
char hostname[256];
00224 hostname[0] = 0;
00225 gethostname(hostname, 255);
00226 hostname[255] = 0;
00227
00228
if (d->hostname == hostname)
00229 {
00230
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
00249 d->staleTimer =
QTime();
00250
continue;
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 }