• Skip to content
  • Skip to link menu
KDE 4.3 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      // cifs increases link count artifically but the inodes are still different
00177      if ((st_buf2.st_nlink > 1 ||
00178          ((st_buf.st_nlink == 1) && (st_buf2.st_nlink == 1))) && (st_buf.st_ino != st_buf2.st_ino))
00179      {
00180         linkCountSupport = testLinkCountSupport(uniqueName);
00181         if (!linkCountSupport)
00182            return KLockFile::LockOK; // Link count support is missing... assume everything is OK.
00183      }
00184      return KLockFile::LockFail;
00185   }
00186 
00187   return KLockFile::LockOK;
00188 }
00189 
00190 static KLockFile::LockResult deleteStaleLock(const QString &lockFile, KDE_struct_stat &st_buf, bool &linkCountSupport, const KComponentData &componentData)
00191 {
00192    // This is dangerous, we could be deleting a new lock instead of
00193    // the old stale one, let's be very careful
00194 
00195    // Create temp file
00196    KTemporaryFile *ktmpFile = new KTemporaryFile(componentData);
00197    ktmpFile->setFileTemplate(lockFile);
00198    if (!ktmpFile->open())
00199       return KLockFile::LockError;
00200 
00201    QByteArray lckFile = QFile::encodeName(lockFile);
00202    QByteArray tmpFile = QFile::encodeName(ktmpFile->fileName());
00203    delete ktmpFile;
00204 
00205    // link to lock file
00206    if (::link(lckFile, tmpFile) != 0)
00207       return KLockFile::LockFail; // Try again later
00208 
00209    // check if link count increased with exactly one
00210    // and if the lock file still matches
00211    KDE_struct_stat st_buf1;
00212    KDE_struct_stat st_buf2;
00213    memcpy(&st_buf1, &st_buf, sizeof(KDE_struct_stat));
00214    st_buf1.st_nlink++;
00215    if ((KDE_lstat(tmpFile, &st_buf2) == 0) && st_buf1 == st_buf2)
00216    {
00217       if ((KDE_lstat(lckFile, &st_buf2) == 0) && st_buf1 == st_buf2)
00218       {
00219          // - - if yes, delete lock file, delete temp file, retry lock
00220          qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00221          ::unlink(lckFile);
00222          ::unlink(tmpFile);
00223          return KLockFile::LockOK;
00224       }
00225    }
00226 
00227    // SMBFS supports hardlinks by copying the file, as a result the above test will always fail
00228    if (linkCountSupport)
00229    {
00230       linkCountSupport = testLinkCountSupport(tmpFile);
00231    }
00232 
00233    if (!linkCountSupport)
00234    {
00235       // Without support for link counts we will have a little race condition
00236       qWarning("WARNING: deleting stale lockfile %s", lckFile.data());
00237       ::unlink(tmpFile);
00238       if (::unlink(lckFile) < 0) {
00239           qWarning("WARNING: Problem deleting stale lockfile %s: %s", lckFile.data(),
00240                   strerror(errno));
00241           return KLockFile::LockFail;
00242       }
00243       return KLockFile::LockOK;
00244    }
00245 
00246    // Failed to delete stale lock file
00247    qWarning("WARNING: Problem deleting stale lockfile %s", lckFile.data());
00248    ::unlink(tmpFile);
00249    return KLockFile::LockFail;
00250 }
00251 
00252 
00253 KLockFile::LockResult KLockFile::lock(LockFlags options)
00254 {
00255   if (d->isLocked)
00256      return KLockFile::LockOK;
00257 
00258   KLockFile::LockResult result;
00259   int hardErrors = 5;
00260   int n = 5;
00261   while(true)
00262   {
00263      KDE_struct_stat st_buf;
00264      result = lockFile(d->file, st_buf, d->linkCountSupport, d->componentData);
00265      if (result == KLockFile::LockOK)
00266      {
00267         d->staleTimer = QTime();
00268         break;
00269      }
00270      else if (result == KLockFile::LockError)
00271      {
00272         d->staleTimer = QTime();
00273         if (--hardErrors == 0)
00274         {
00275            break;
00276         }
00277      }
00278      else // KLockFile::Fail -- there is already such a file present (e.g. left by a crashed app)
00279      {
00280         if (!d->staleTimer.isNull() && d->statBuf != st_buf)
00281            d->staleTimer = QTime();
00282 
00283         if (d->staleTimer.isNull())
00284         {
00285            memcpy(&(d->statBuf), &st_buf, sizeof(KDE_struct_stat));
00286            d->staleTimer.start();
00287 
00288            d->pid = -1;
00289            d->hostname.clear();
00290            d->instance.clear();
00291 
00292            QFile file(d->file);
00293            if (file.open(QIODevice::ReadOnly))
00294            {
00295               QTextStream ts(&file);
00296               if (!ts.atEnd())
00297                  d->pid = ts.readLine().toInt();
00298               if (!ts.atEnd())
00299                  d->instance = ts.readLine();
00300               if (!ts.atEnd())
00301                  d->hostname = ts.readLine();
00302            }
00303         }
00304 
00305         bool isStale = false;
00306         if ((d->pid > 0) && !d->hostname.isEmpty())
00307         {
00308            // Check if hostname is us
00309            char hostname[256];
00310            hostname[0] = 0;
00311            gethostname(hostname, 255);
00312            hostname[255] = 0;
00313 
00314            if (d->hostname == QLatin1String(hostname))
00315            {
00316               // Check if pid still exists
00317               int res = ::kill(d->pid, 0);
00318               if ((res == -1) && (errno == ESRCH))
00319                  isStale = true;
00320            }
00321         }
00322         if (d->staleTimer.elapsed() > (d->staleTime*1000))
00323            isStale = true;
00324 
00325         if (isStale)
00326         {
00327            if ((options & ForceFlag) == 0)
00328               return KLockFile::LockStale;
00329 
00330            result = deleteStaleLock(d->file, d->statBuf, d->linkCountSupport, d->componentData);
00331 
00332            if (result == KLockFile::LockOK)
00333            {
00334               // Lock deletion successful
00335               d->staleTimer = QTime();
00336               continue; // Now try to get the new lock
00337            }
00338            else if (result != KLockFile::LockFail)
00339            {
00340               return result;
00341            }
00342         }
00343      }
00344 
00345      if (options & NoBlockFlag)
00346         break;
00347 
00348      struct timeval tv;
00349      tv.tv_sec = 0;
00350      tv.tv_usec = n*((KRandom::random() % 200)+100);
00351      if (n < 2000)
00352         n = n * 2;
00353 
00354      select(0, 0, 0, 0, &tv);
00355   }
00356   if (result == LockOK)
00357      d->isLocked = true;
00358   return result;
00359 }
00360 
00361 bool KLockFile::isLocked() const
00362 {
00363   return d->isLocked;
00364 }
00365 
00366 void KLockFile::unlock()
00367 {
00368   if (d->isLocked)
00369   {
00370      ::unlink(QFile::encodeName(d->file));
00371      d->isLocked = false;
00372   }
00373 }
00374 
00375 bool KLockFile::getLockInfo(int &pid, QString &hostname, QString &appname)
00376 {
00377   if (d->pid == -1)
00378      return false;
00379   pid = d->pid;
00380   hostname = d->hostname;
00381   appname = d->instance;
00382   return true;
00383 }

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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