home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / unix / snglinst.cpp < prev    next >
C/C++ Source or Header  |  2002-08-05  |  10KB  |  363 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name:        unix/snglinst.cpp
  3. // Purpose:     implements wxSingleInstanceChecker class for Unix using
  4. //              lock files with fcntl(2) or flock(2)
  5. // Author:      Vadim Zeitlin
  6. // Modified by:
  7. // Created:     09.06.01
  8. // RCS-ID:      $Id: snglinst.cpp,v 1.6 2002/08/05 18:04:08 RR Exp $
  9. // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
  10. // License:     wxWindows license
  11. ///////////////////////////////////////////////////////////////////////////////
  12.  
  13. // ============================================================================
  14. // declarations
  15. // ============================================================================
  16.  
  17. // ----------------------------------------------------------------------------
  18. // headers
  19. // ----------------------------------------------------------------------------
  20.  
  21. #ifdef __GNUG__
  22.     #pragma implementation "snglinst.h"
  23. #endif
  24.  
  25. // For compilers that support precompilation, includes "wx.h".
  26. #include "wx/wxprec.h"
  27.  
  28. #ifdef __BORLANDC__
  29.     #pragma hdrstop
  30. #endif
  31.  
  32. #if wxUSE_SNGLINST_CHECKER
  33.  
  34. #ifndef WX_PRECOMP
  35.     #include "wx/string.h"
  36.     #include "wx/log.h"
  37.     #include "wx/intl.h"
  38.     #include "wx/file.h"
  39. #endif //WX_PRECOMP
  40.  
  41. #include "wx/utils.h"           // wxGetHomeDir()
  42.  
  43. #include "wx/snglinst.h"
  44.  
  45. #include <unistd.h>
  46. #include <sys/types.h>
  47. #include <sys/stat.h>           // for S_I[RW]USR
  48. #include <signal.h>             // for kill()
  49. #include <errno.h>
  50.  
  51. #ifdef HAVE_FCNTL
  52.     #include <fcntl.h>
  53. #elif defined(HAVE_FLOCK)
  54.     #include <sys/file.h>
  55. #else
  56.     // normally, wxUSE_SNGLINST_CHECKER must have been reset by configure
  57.     #error "wxSingleInstanceChecker can't be compiled on this platform"
  58. #endif // fcntl()/flock()
  59.  
  60. // ----------------------------------------------------------------------------
  61. // constants
  62. // ----------------------------------------------------------------------------
  63.  
  64. // argument of wxLockFile()
  65. enum LockOperation
  66. {
  67.     LOCK,
  68.     UNLOCK
  69. };
  70.  
  71. // return value of CreateLockFile()
  72. enum LockResult
  73. {
  74.     LOCK_ERROR = -1,
  75.     LOCK_EXISTS,
  76.     LOCK_CREATED
  77. };
  78.  
  79. // ----------------------------------------------------------------------------
  80. // private functions: (exclusively) lock/unlock the file
  81. // ----------------------------------------------------------------------------
  82.  
  83. #ifdef HAVE_FCNTL
  84.  
  85. static int wxLockFile(int fd, LockOperation lock)
  86. {
  87.     // init the flock parameter struct
  88.     struct flock fl;
  89.     fl.l_type = lock == LOCK ? F_WRLCK : F_UNLCK;
  90.  
  91.     // lock the entire file
  92.     fl.l_whence =
  93.     fl.l_start =
  94.     fl.l_len = 0;
  95.  
  96.     // is this needed?
  97.     fl.l_pid = getpid();
  98.  
  99.     return fcntl(fd, F_SETLK, &fl);
  100. }
  101.  
  102. #else // HAVE_FLOCK
  103.  
  104. static int wxLockFile(int fd, LockOperation lock)
  105. {
  106.     return flock(fd, lock == LOCK ? LOCK_EX | LOCK_NB : LOCK_UN);
  107. }
  108.  
  109. #endif // fcntl()/flock()
  110.  
  111. // ----------------------------------------------------------------------------
  112. // wxSingleInstanceCheckerImpl: the real implementation class
  113. // ----------------------------------------------------------------------------
  114.  
  115. class wxSingleInstanceCheckerImpl
  116. {
  117. public:
  118.     wxSingleInstanceCheckerImpl()
  119.     {
  120.         m_fdLock = -1;
  121.         m_pidLocker = 0;
  122.     }
  123.  
  124.     bool Create(const wxString& name);
  125.  
  126.     pid_t GetLockerPID() const { return m_pidLocker; }
  127.  
  128.     ~wxSingleInstanceCheckerImpl() { Unlock(); }
  129.  
  130. private:
  131.     // try to create and lock the file
  132.     LockResult CreateLockFile();
  133.  
  134.     // unlock and remove the lock file
  135.     void Unlock();
  136.  
  137.     // the descriptor of our lock file, -1 if none
  138.     int m_fdLock;
  139.  
  140.     // pid of the process owning the lock file
  141.     pid_t m_pidLocker;
  142.  
  143.     // the name of the lock file
  144.     wxString m_nameLock;
  145. };
  146.  
  147. // ============================================================================
  148. // wxSingleInstanceCheckerImpl implementation
  149. // ============================================================================
  150.  
  151. LockResult wxSingleInstanceCheckerImpl::CreateLockFile()
  152. {
  153.     // try to open the file
  154.     m_fdLock = open(m_nameLock.fn_str(),
  155.                     O_WRONLY | O_CREAT | O_EXCL,
  156.                     S_IRUSR | S_IWUSR);
  157.  
  158.     if ( m_fdLock != -1 )
  159.     {
  160.         // try to lock it
  161.         if ( wxLockFile(m_fdLock, LOCK) == 0 )
  162.         {
  163.             // fine, we have the exclusive lock to the file, write our PID
  164.             // into it
  165.             m_pidLocker = getpid();
  166.  
  167.             // use char here, not wxChar!
  168.             char buf[256]; // enough for any PID size
  169.             int len = sprintf(buf, "%d", (int)m_pidLocker) + 1;
  170.  
  171.             if ( write(m_fdLock, buf, len) != len )
  172.             {
  173.                 wxLogSysError(_("Failed to write to lock file '%s'"),
  174.                               m_nameLock.c_str());
  175.  
  176.                 Unlock();
  177.  
  178.                 return LOCK_ERROR;
  179.             }
  180.  
  181.             fsync(m_fdLock);
  182.  
  183.             return LOCK_CREATED;
  184.         }
  185.         else // failure: see what exactly happened
  186.         {
  187.             close(m_fdLock);
  188.             m_fdLock = -1;
  189.  
  190.             if ( errno != EACCES && errno != EAGAIN )
  191.             {
  192.                 wxLogSysError(_("Failed to lock the lock file '%s'"),
  193.                               m_nameLock.c_str());
  194.  
  195.                 unlink(m_nameLock.fn_str());
  196.  
  197.                 return LOCK_ERROR;
  198.             }
  199.             //else: couldn't lock because the lock is held by another process:
  200.             //      this might have happened because of a race condition:
  201.             //      maybe another instance opened and locked the file between
  202.             //      our calls to open() and flock(), so don't give an error
  203.         }
  204.     }
  205.  
  206.     // we didn't create and lock the file
  207.     return LOCK_EXISTS;
  208. }
  209.  
  210. bool wxSingleInstanceCheckerImpl::Create(const wxString& name)
  211. {
  212.     m_nameLock = name;
  213.  
  214.     switch ( CreateLockFile() )
  215.     {
  216.         case LOCK_EXISTS:
  217.             // there is a lock file, check below if it is still valid
  218.             break;
  219.  
  220.         case LOCK_CREATED:
  221.             // nothing more to do
  222.             return TRUE;
  223.  
  224.         case LOCK_ERROR:
  225.             // oops...
  226.             return FALSE;
  227.     }
  228.  
  229.     // try to open the file for reading and get the PID of the process
  230.     // which has it
  231.     wxFile file(name, wxFile::read);
  232.     if ( !file.IsOpened() )
  233.     {
  234.         // well, this is really weird - file doesn't exist and we can't
  235.         // create it
  236.         //
  237.         // normally, this just means that we don't have write access to
  238.         // the directory where we try to create it, so return failure,
  239.         // even it might also be a rare case of a race condition when
  240.         // another process managed to open and lock the file and terminate
  241.         // (erasing it) before we got here, but this should happen so
  242.         // rarely in practice that we don't care
  243.         wxLogError(_("Failed to access lock file."));
  244.  
  245.         return FALSE;
  246.     }
  247.  
  248.     char buf[256];
  249.     off_t count = file.Read(buf, WXSIZEOF(buf));
  250.     if ( count == wxInvalidOffset )
  251.     {
  252.         wxLogError(_("Failed to read PID from lock file."));
  253.     }
  254.     else
  255.     {
  256.         if ( sscanf(buf, "%d", (int *)&m_pidLocker) == 1 )
  257.         {
  258.             if ( kill(m_pidLocker, 0) != 0 )
  259.             {
  260.                 if ( unlink(name.fn_str()) != 0 )
  261.                 {
  262.                     wxLogError(_("Failed to remove stale lock file '%s'."),
  263.                                name.c_str());
  264.  
  265.                     // return TRUE in this case for now...
  266.                 }
  267.                 else
  268.                 {
  269.                     wxLogMessage(_("Deleted stale lock file '%s'."),
  270.                                  name.c_str());
  271.  
  272.                     // retry now
  273.                     (void)CreateLockFile();
  274.                 }
  275.             }
  276.             //else: the other process is running
  277.         }
  278.         else
  279.         {
  280.             wxLogWarning(_("Invalid lock file '%s'."), name.c_str());
  281.         }
  282.     }
  283.  
  284.     // return TRUE if we could get the PID of the process owning the lock file
  285.     // (whether it is still running or not), FALSE otherwise as it is
  286.     // unexpected
  287.     return m_pidLocker != 0;
  288. }
  289.  
  290. void wxSingleInstanceCheckerImpl::Unlock()
  291. {
  292.     if ( m_fdLock != -1 )
  293.     {
  294.         if ( unlink(m_nameLock.fn_str()) != 0 )
  295.         {
  296.             wxLogSysError(_("Failed to remove lock file '%s'"),
  297.                           m_nameLock.c_str());
  298.         }
  299.  
  300.         if ( wxLockFile(m_fdLock, UNLOCK) != 0 )
  301.         {
  302.             wxLogSysError(_("Failed to unlock lock file '%s'"),
  303.                           m_nameLock.c_str());
  304.         }
  305.  
  306.         if ( close(m_fdLock) != 0 )
  307.         {
  308.             wxLogSysError(_("Failed to close lock file '%s'"),
  309.                           m_nameLock.c_str());
  310.         }
  311.     }
  312.  
  313.     m_pidLocker = 0;
  314. }
  315.  
  316. // ============================================================================
  317. // wxSingleInstanceChecker implementation
  318. // ============================================================================
  319.  
  320. bool wxSingleInstanceChecker::Create(const wxString& name,
  321.                                      const wxString& path)
  322. {
  323.     wxASSERT_MSG( !m_impl,
  324.                   _T("calling wxSingleInstanceChecker::Create() twice?") );
  325.  
  326.     // must have the file name to create a lock file
  327.     wxASSERT_MSG( !name.empty(), _T("lock file name can't be empty") );
  328.  
  329.     m_impl = new wxSingleInstanceCheckerImpl;
  330.  
  331.     wxString fullname = path;
  332.     if ( fullname.empty() )
  333.     {
  334.         fullname = wxGetHomeDir();
  335.     }
  336.  
  337.     if ( fullname.Last() != _T('/') )
  338.     {
  339.         fullname += _T('/');
  340.     }
  341.  
  342.     fullname << name;
  343.  
  344.     return m_impl->Create(fullname);
  345. }
  346.  
  347. bool wxSingleInstanceChecker::IsAnotherRunning() const
  348. {
  349.     wxCHECK_MSG( m_impl, FALSE, _T("must call Create() first") );
  350.  
  351.     // if another instance is running, it must own the lock file - otherwise
  352.     // we have it and the locker PID is ours one
  353.     return m_impl->GetLockerPID() != getpid();
  354. }
  355.  
  356. wxSingleInstanceChecker::~wxSingleInstanceChecker()
  357. {
  358.     delete m_impl;
  359. }
  360.  
  361. #endif // wxUSE_SNGLINST_CHECKER
  362.  
  363.