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 <kapplication.h>
00040 #include <kcmdlineargs.h>
00041 #include <kglobal.h>
00042 #include <ktempfile.h>
00043 
00044 // TODO: http://www.spinnaker.de/linux/nfs-locking.html
00045 // TODO: Make regression test
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 // 30 seconds
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   // Create lock file
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    // This is dangerous, we could be deleting a new lock instead of
00147    // the old stale one, let's be very careful
00148               
00149    // Create temp file
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    // link to lock file
00160    if (::link(lckFile, tmpFile) != 0)
00161       return KLockFile::LockFail; // Try again later
00162 
00163    // check if link count increased with exactly one
00164    // and if the lock file still matches
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          // - - if yes, delete lock file, delete temp file, retry lock
00174          qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00175          ::unlink(lckFile);
00176          ::unlink(tmpFile);
00177          return KLockFile::LockOK;
00178       }
00179    }
00180    // Failed to delete stale lock file
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 // KLockFile::Fail
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               // Check if hostname is us
00223               char hostname[256];
00224               hostname[0] = 0;
00225               gethostname(hostname, 255);
00226               hostname[255] = 0;
00227               
00228               if (d->hostname == hostname)
00229               {
00230                  // Check if pid still exists
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                  // Lock deletion successful
00249                  d->staleTimer = QTime();
00250                  continue; // Now try to get the new lock
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 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Nov 27 13:41:16 2004 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003