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 <kde_file.h>
00040 #include <kapplication.h>
00041 #include <kcmdlineargs.h>
00042 #include <kglobal.h>
00043 #include <ktempfile.h>
00044 
00045 // TODO: http://www.spinnaker.de/linux/nfs-locking.html
00046 // TODO: Make regression test
00047 
00048 class KLockFile::KLockFilePrivate {
00049 public:
00050    QString file;
00051    int staleTime;
00052    bool isLocked;
00053    bool recoverLock;
00054    QTime staleTimer;
00055    KDE_struct_stat statBuf;
00056    int pid;
00057    QString hostname;
00058    QString instance;
00059    QString lockRecoverFile;
00060 };
00061 
00062 
00063 // 30 seconds
00064 KLockFile::KLockFile(const QString &file)
00065 {
00066   d = new KLockFilePrivate();
00067   d->file = file;
00068   d->staleTime = 30;
00069   d->isLocked = false;
00070   d->recoverLock = false;
00071 }
00072 
00073 KLockFile::~KLockFile()
00074 {
00075   unlock();
00076   delete d;
00077 }
00078 
00079 int 
00080 KLockFile::staleTime() const
00081 {
00082   return d->staleTime;
00083 }
00084 
00085 
00086 void
00087 KLockFile::setStaleTime(int _staleTime)
00088 {
00089   d->staleTime = _staleTime;
00090 }
00091 
00092 static bool statResultIsEqual(KDE_struct_stat &st_buf1, KDE_struct_stat &st_buf2)
00093 {
00094 #define FIELD_EQ(what)       (st_buf1.what == st_buf2.what)
00095   return FIELD_EQ(st_dev) && FIELD_EQ(st_ino) && 
00096          FIELD_EQ(st_uid) && FIELD_EQ(st_gid) && FIELD_EQ(st_nlink);
00097 #undef FIELD_EQ
00098 }
00099 
00100 static KLockFile::LockResult lockFile(const QString &lockFile, KDE_struct_stat &st_buf)
00101 {
00102   QCString lockFileName = QFile::encodeName( lockFile );
00103   int result = KDE_lstat( lockFileName, &st_buf );
00104   if (result == 0)
00105      return KLockFile::LockFail;
00106   
00107   KTempFile uniqueFile(lockFile, QString::null, 0644);
00108   uniqueFile.setAutoDelete(true);
00109   if (uniqueFile.status() != 0)
00110      return KLockFile::LockError;
00111 
00112   char hostname[256];
00113   hostname[0] = 0;
00114   gethostname(hostname, 255);
00115   hostname[255] = 0;
00116   QCString instanceName = KCmdLineArgs::appName();
00117 
00118   (*(uniqueFile.textStream())) << QString::number(getpid()) << endl
00119       << instanceName << endl
00120       << hostname << endl;
00121   uniqueFile.close();
00122   
00123   QCString uniqueName = QFile::encodeName( uniqueFile.name() );
00124       
00125 #ifdef Q_OS_UNIX
00126   // Create lock file
00127   result = ::link( uniqueName, lockFileName );
00128   if (result != 0)
00129      return KLockFile::LockError;
00130 
00131 #else
00132   //TODO for win32
00133   return KLockFile::LockOK;
00134 #endif
00135 
00136   KDE_struct_stat st_buf2;
00137   result = KDE_lstat( uniqueName, &st_buf2 );
00138   if (result != 0)
00139      return KLockFile::LockError;
00140 
00141   result = KDE_lstat( lockFileName, &st_buf );
00142   if (result != 0)
00143      return KLockFile::LockError;
00144 
00145   if (!statResultIsEqual(st_buf, st_buf2) || S_ISLNK(st_buf.st_mode) || S_ISLNK(st_buf2.st_mode))
00146      return KLockFile::LockFail;
00147 
00148   return KLockFile::LockOK;
00149 }
00150 
00151 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, KDE_struct_stat &st_buf)
00152 {
00153    // This is dangerous, we could be deleting a new lock instead of
00154    // the old stale one, let's be very careful
00155               
00156    // Create temp file
00157    KTempFile ktmpFile(lockFile);
00158    if (ktmpFile.status() != 0)
00159       return KLockFile::LockError;
00160               
00161    QCString lckFile = QFile::encodeName( lockFile );
00162    QCString tmpFile = QFile::encodeName(ktmpFile.name());
00163    ktmpFile.close();
00164    ktmpFile.unlink();
00165               
00166 #ifdef Q_OS_UNIX
00167    // link to lock file
00168    if (::link(lckFile, tmpFile) != 0)
00169       return KLockFile::LockFail; // Try again later
00170 #else
00171    //TODO for win32
00172    return KLockFile::LockOK;
00173 #endif
00174 
00175    // check if link count increased with exactly one
00176    // and if the lock file still matches
00177    KDE_struct_stat st_buf1;
00178    KDE_struct_stat st_buf2;
00179    memcpy(&st_buf1, &st_buf, sizeof(KDE_struct_stat));
00180    st_buf1.st_nlink++;
00181    if ((KDE_lstat(tmpFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2))
00182    {
00183       if ((KDE_lstat(lckFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2))
00184       {
00185          // - - if yes, delete lock file, delete temp file, retry lock
00186          qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00187          ::unlink(lckFile);
00188          ::unlink(tmpFile);
00189          return KLockFile::LockOK;
00190       }
00191    }
00192    // Failed to delete stale lock file
00193    qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data());
00194    ::unlink(tmpFile);
00195    return KLockFile::LockFail;
00196 }
00197 
00198 
00199 KLockFile::LockResult KLockFile::lock(int options)
00200 {
00201   if (d->isLocked)
00202      return KLockFile::LockOK;
00203 
00204   KLockFile::LockResult result;     
00205   int hardErrors = 5;
00206   int n = 5;
00207   while(true)
00208   {
00209      KDE_struct_stat st_buf;
00210      result = lockFile(d->file, st_buf);
00211      if (result == KLockFile::LockOK)
00212      {
00213         d->staleTimer = QTime();
00214         break;
00215      }
00216      else if (result == KLockFile::LockError)
00217      {
00218         d->staleTimer = QTime();
00219         if (--hardErrors == 0)
00220         {
00221            break;
00222         }
00223      }
00224      else // KLockFile::Fail
00225      {
00226         if (!d->staleTimer.isNull() && !statResultIsEqual(d->statBuf, st_buf))
00227            d->staleTimer = QTime();
00228            
00229         if (!d->staleTimer.isNull())
00230         {
00231            bool isStale = false;
00232            if ((d->pid > 0) && !d->hostname.isEmpty())
00233            {
00234               // Check if hostname is us
00235               char hostname[256];
00236               hostname[0] = 0;
00237               gethostname(hostname, 255);
00238               hostname[255] = 0;
00239               
00240               if (d->hostname == hostname)
00241               {
00242                  // Check if pid still exists
00243                  int res = ::kill(d->pid, 0);
00244                  if ((res == -1) && (errno == ESRCH))
00245                     isStale = true;
00246               }
00247            }
00248            if (d->staleTimer.elapsed() > (d->staleTime*1000))
00249               isStale = true;
00250            
00251            if (isStale)
00252            {
00253               if ((options & LockForce) == 0)
00254                  return KLockFile::LockStale;
00255                  
00256               result = deleteStaleLock(d->file, d->statBuf);
00257 
00258               if (result == KLockFile::LockOK)
00259               {
00260                  // Lock deletion successful
00261                  d->staleTimer = QTime();
00262                  continue; // Now try to get the new lock
00263               }
00264               else if (result != KLockFile::LockFail)
00265               {
00266                  return result;
00267               }
00268            }
00269         }
00270         else
00271         {
00272            memcpy(&(d->statBuf), &st_buf, sizeof(KDE_struct_stat));
00273            d->staleTimer.start();
00274            
00275            d->pid = -1;
00276            d->hostname = QString::null;
00277            d->instance = QString::null;
00278         
00279            QFile file(d->file);
00280            if (file.open(IO_ReadOnly))
00281            {
00282               QTextStream ts(&file);
00283               if (!ts.atEnd())
00284                  d->pid = ts.readLine().toInt();
00285               if (!ts.atEnd())
00286                  d->instance = ts.readLine();
00287               if (!ts.atEnd())
00288                  d->hostname = ts.readLine();
00289            }
00290         }
00291      }
00292         
00293      if ((options & LockNoBlock) != 0)
00294         break;
00295      
00296      struct timeval tv;
00297      tv.tv_sec = 0;
00298      tv.tv_usec = n*((KApplication::random() % 200)+100);
00299      if (n < 2000)
00300         n = n * 2;
00301      
00302 #ifdef Q_OS_UNIX
00303      select(0, 0, 0, 0, &tv);
00304 #else
00305      //TODO for win32
00306 #endif
00307   }
00308   if (result == LockOK)
00309      d->isLocked = true;
00310   return result;
00311 }
00312    
00313 bool KLockFile::isLocked() const
00314 {
00315   return d->isLocked;
00316 }
00317    
00318 void KLockFile::unlock()
00319 {
00320   if (d->isLocked)
00321   {
00322      ::unlink(QFile::encodeName(d->file));
00323      d->isLocked = false;
00324   }
00325 }
00326 
00327 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname)
00328 {
00329   if (d->pid == -1)
00330      return false;
00331   pid = d->pid;
00332   hostname = d->hostname;
00333   appname = d->instance;
00334   return true;
00335 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 4 06:54:19 2005 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003