• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDECore

klockfile.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, 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    bool linkCountSupport;
00055    QTime staleTimer;
00056    KDE_struct_stat statBuf;
00057    int pid;
00058    QString hostname;
00059    QString instance;
00060    QString lockRecoverFile;
00061 };
00062 
00063 
00064 // 30 seconds
00065 KLockFile::KLockFile(const QString &file)
00066 {
00067   d = new KLockFilePrivate();
00068   d->file = file;
00069   d->staleTime = 30;
00070   d->isLocked = false;
00071   d->recoverLock = false;
00072   d->linkCountSupport = true;
00073 }
00074 
00075 KLockFile::~KLockFile()
00076 {
00077   unlock();
00078   delete d;
00079 }
00080 
00081 int 
00082 KLockFile::staleTime() const
00083 {
00084   return d->staleTime;
00085 }
00086 
00087 
00088 void
00089 KLockFile::setStaleTime(int _staleTime)
00090 {
00091   d->staleTime = _staleTime;
00092 }
00093 
00094 static bool statResultIsEqual(KDE_struct_stat &st_buf1, KDE_struct_stat &st_buf2)
00095 {
00096 #define FIELD_EQ(what)       (st_buf1.what == st_buf2.what)
00097   return FIELD_EQ(st_dev) && FIELD_EQ(st_ino) && 
00098          FIELD_EQ(st_uid) && FIELD_EQ(st_gid) && FIELD_EQ(st_nlink);
00099 #undef FIELD_EQ
00100 }
00101 
00102 static bool testLinkCountSupport(const QCString &fileName)
00103 {
00104    KDE_struct_stat st_buf;
00105    // Check if hardlinks raise the link count at all?
00106    ::link( fileName, fileName+".test" );
00107    int result = KDE_lstat( fileName, &st_buf );
00108    ::unlink( fileName+".test" );
00109    return ((result == 0) && (st_buf.st_nlink == 2));
00110 }
00111 
00112 static KLockFile::LockResult lockFile(const QString &lockFile, KDE_struct_stat &st_buf, bool &linkCountSupport)
00113 {
00114   QCString lockFileName = QFile::encodeName( lockFile );
00115   int result = KDE_lstat( lockFileName, &st_buf );
00116   if (result == 0)
00117      return KLockFile::LockFail;
00118   
00119   KTempFile uniqueFile(lockFile, QString::null, 0644);
00120   uniqueFile.setAutoDelete(true);
00121   if (uniqueFile.status() != 0)
00122      return KLockFile::LockError;
00123 
00124   char hostname[256];
00125   hostname[0] = 0;
00126   gethostname(hostname, 255);
00127   hostname[255] = 0;
00128   QCString instanceName = KCmdLineArgs::appName();
00129 
00130   (*(uniqueFile.textStream())) << QString::number(getpid()) << endl
00131       << instanceName << endl
00132       << hostname << endl;
00133   uniqueFile.close();
00134   
00135   QCString uniqueName = QFile::encodeName( uniqueFile.name() );
00136       
00137 #ifdef Q_OS_UNIX
00138   // Create lock file
00139   result = ::link( uniqueName, lockFileName );
00140   if (result != 0)
00141      return KLockFile::LockError;
00142      
00143   if (!linkCountSupport)
00144      return KLockFile::LockOK;
00145 #else
00146   //TODO for win32
00147   return KLockFile::LockOK;
00148 #endif
00149 
00150   KDE_struct_stat st_buf2;
00151   result = KDE_lstat( uniqueName, &st_buf2 );
00152   if (result != 0)
00153      return KLockFile::LockError;
00154 
00155   result = KDE_lstat( lockFileName, &st_buf );
00156   if (result != 0)
00157      return KLockFile::LockError;
00158 
00159   if (!statResultIsEqual(st_buf, st_buf2) || S_ISLNK(st_buf.st_mode) || S_ISLNK(st_buf2.st_mode))
00160   {
00161      // SMBFS supports hardlinks by copying the file, as a result the above test will always fail
00162      if ((st_buf.st_nlink == 1) && (st_buf2.st_nlink == 1) && (st_buf.st_ino != st_buf2.st_ino))
00163      {
00164         linkCountSupport = testLinkCountSupport(uniqueName);
00165         if (!linkCountSupport)
00166            return KLockFile::LockOK; // Link count support is missing... assume everything is OK.
00167      }
00168      return KLockFile::LockFail;
00169   }
00170 
00171   return KLockFile::LockOK;
00172 }
00173 
00174 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, KDE_struct_stat &st_buf, bool &linkCountSupport)
00175 {
00176    // This is dangerous, we could be deleting a new lock instead of
00177    // the old stale one, let's be very careful
00178               
00179    // Create temp file
00180    KTempFile ktmpFile(lockFile);
00181    if (ktmpFile.status() != 0)
00182       return KLockFile::LockError;
00183               
00184    QCString lckFile = QFile::encodeName(lockFile);
00185    QCString tmpFile = QFile::encodeName(ktmpFile.name());
00186    ktmpFile.close();
00187    ktmpFile.unlink();
00188               
00189 #ifdef Q_OS_UNIX
00190    // link to lock file
00191    if (::link(lckFile, tmpFile) != 0)
00192       return KLockFile::LockFail; // Try again later
00193 #else
00194    //TODO for win32
00195    return KLockFile::LockOK;
00196 #endif
00197 
00198    // check if link count increased with exactly one
00199    // and if the lock file still matches
00200    KDE_struct_stat st_buf1;
00201    KDE_struct_stat st_buf2;
00202    memcpy(&st_buf1, &st_buf, sizeof(KDE_struct_stat));
00203    st_buf1.st_nlink++;
00204    if ((KDE_lstat(tmpFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2))
00205    {
00206       if ((KDE_lstat(lckFile, &st_buf2) == 0) && statResultIsEqual(st_buf1, st_buf2))
00207       {
00208          // - - if yes, delete lock file, delete temp file, retry lock
00209          qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00210          ::unlink(lckFile);
00211          ::unlink(tmpFile);
00212          return KLockFile::LockOK;
00213       }
00214    }
00215    
00216    // SMBFS supports hardlinks by copying the file, as a result the above test will always fail
00217    if (linkCountSupport)
00218    {
00219       linkCountSupport = testLinkCountSupport(tmpFile);
00220    }
00221 
00222    if (!linkCountSupport && 
00223        (KDE_lstat(lckFile, &st_buf2) == 0) && 
00224        statResultIsEqual(st_buf, st_buf2))
00225    {
00226       // Without support for link counts we will have a little race condition
00227       qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00228       ::unlink(lckFile);
00229       ::unlink(tmpFile);
00230       return KLockFile::LockOK;
00231    }
00232    
00233    // Failed to delete stale lock file
00234    qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data());
00235    ::unlink(tmpFile);
00236    return KLockFile::LockFail;
00237 }
00238 
00239 
00240 KLockFile::LockResult KLockFile::lock(int options)
00241 {
00242   if (d->isLocked)
00243      return KLockFile::LockOK;
00244 
00245   KLockFile::LockResult result;     
00246   int hardErrors = 5;
00247   int n = 5;
00248   while(true)
00249   {
00250      KDE_struct_stat st_buf;
00251      result = lockFile(d->file, st_buf, d->linkCountSupport);
00252      if (result == KLockFile::LockOK)
00253      {
00254         d->staleTimer = QTime();
00255         break;
00256      }
00257      else if (result == KLockFile::LockError)
00258      {
00259         d->staleTimer = QTime();
00260         if (--hardErrors == 0)
00261         {
00262            break;
00263         }
00264      }
00265      else // KLockFile::Fail
00266      {
00267         if (!d->staleTimer.isNull() && !statResultIsEqual(d->statBuf, st_buf))
00268            d->staleTimer = QTime();
00269            
00270         if (!d->staleTimer.isNull())
00271         {
00272            bool isStale = false;
00273            if ((d->pid > 0) && !d->hostname.isEmpty())
00274            {
00275               // Check if hostname is us
00276               char hostname[256];
00277               hostname[0] = 0;
00278               gethostname(hostname, 255);
00279               hostname[255] = 0;
00280               
00281               if (d->hostname == hostname)
00282               {
00283                  // Check if pid still exists
00284                  int res = ::kill(d->pid, 0);
00285                  if ((res == -1) && (errno == ESRCH))
00286                     isStale = true;
00287               }
00288            }
00289            if (d->staleTimer.elapsed() > (d->staleTime*1000))
00290               isStale = true;
00291            
00292            if (isStale)
00293            {
00294               if ((options & LockForce) == 0)
00295                  return KLockFile::LockStale;
00296                  
00297               result = deleteStaleLock(d->file, d->statBuf, d->linkCountSupport);
00298 
00299               if (result == KLockFile::LockOK)
00300               {
00301                  // Lock deletion successful
00302                  d->staleTimer = QTime();
00303                  continue; // Now try to get the new lock
00304               }
00305               else if (result != KLockFile::LockFail)
00306               {
00307                  return result;
00308               }
00309            }
00310         }
00311         else
00312         {
00313            memcpy(&(d->statBuf), &st_buf, sizeof(KDE_struct_stat));
00314            d->staleTimer.start();
00315            
00316            d->pid = -1;
00317            d->hostname = QString::null;
00318            d->instance = QString::null;
00319         
00320            QFile file(d->file);
00321            if (file.open(IO_ReadOnly))
00322            {
00323               QTextStream ts(&file);
00324               if (!ts.atEnd())
00325                  d->pid = ts.readLine().toInt();
00326               if (!ts.atEnd())
00327                  d->instance = ts.readLine();
00328               if (!ts.atEnd())
00329                  d->hostname = ts.readLine();
00330            }
00331         }
00332      }
00333         
00334      if ((options & LockNoBlock) != 0)
00335         break;
00336      
00337      struct timeval tv;
00338      tv.tv_sec = 0;
00339      tv.tv_usec = n*((KApplication::random() % 200)+100);
00340      if (n < 2000)
00341         n = n * 2;
00342      
00343 #ifdef Q_OS_UNIX
00344      select(0, 0, 0, 0, &tv);
00345 #else
00346      //TODO for win32
00347 #endif
00348   }
00349   if (result == LockOK)
00350      d->isLocked = true;
00351   return result;
00352 }
00353    
00354 bool KLockFile::isLocked() const
00355 {
00356   return d->isLocked;
00357 }
00358    
00359 void KLockFile::unlock()
00360 {
00361   if (d->isLocked)
00362   {
00363      ::unlink(QFile::encodeName(d->file));
00364      d->isLocked = false;
00365   }
00366 }
00367 
00368 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname)
00369 {
00370   if (d->pid == -1)
00371      return false;
00372   pid = d->pid;
00373   hostname = d->hostname;
00374   appname = d->instance;
00375   return true;
00376 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal