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

KDECore

klockfile_unix.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 <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 // TODO: http://www.spinnaker.de/linux/nfs-locking.html
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 // 30 seconds
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    // Check if hardlinks raise the link count at all?
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   // Create lock file
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      // SMBFS supports hardlinks by copying the file, as a result the above test will always fail
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; // Link count support is missing... assume everything is OK.
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    // This is dangerous, we could be deleting a new lock instead of
00191    // the old stale one, let's be very careful
00192 
00193    // Create temp file
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    // link to lock file
00204    if (::link(lckFile, tmpFile) != 0)
00205       return KLockFile::LockFail; // Try again later
00206 
00207    // check if link count increased with exactly one
00208    // and if the lock file still matches
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          // - - if yes, delete lock file, delete temp file, retry lock
00218          qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00219          ::unlink(lckFile);
00220          ::unlink(tmpFile);
00221          return KLockFile::LockOK;
00222       }
00223    }
00224 
00225    // SMBFS supports hardlinks by copying the file, as a result the above test will always fail
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       // Without support for link counts we will have a little race condition
00235       qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00236       ::unlink(lckFile);
00237       ::unlink(tmpFile);
00238       return KLockFile::LockOK;
00239    }
00240 
00241    // Failed to delete stale lock file
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 // KLockFile::Fail
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               // Check if hostname is us
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                  // Check if pid still exists
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                  // Lock deletion successful
00310                  d->staleTimer = QTime();
00311                  continue; // Now try to get the new lock
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 }

KDECore

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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