kdecore Library: klockfile.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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 }

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:09 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003