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 
   9 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
  10 // License:     wxWindows license 
  11 /////////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  22     #pragma implementation "snglinst.h" 
  25 // For compilers that support precompilation, includes "wx.h". 
  26 #include "wx/wxprec.h" 
  32 #if wxUSE_SNGLINST_CHECKER 
  35     #include "wx/string.h" 
  41 #include "wx/utils.h"           // wxGetHomeDir() 
  43 #include "wx/snglinst.h" 
  46 #include <sys/types.h> 
  47 #include <sys/stat.h>           // for S_I[RW]USR 
  48 #include <signal.h>             // for kill() 
  53 #elif defined(HAVE_FLOCK) 
  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() 
  60 // ---------------------------------------------------------------------------- 
  62 // ---------------------------------------------------------------------------- 
  64 // argument of wxLockFile() 
  71 // return value of CreateLockFile() 
  79 // ---------------------------------------------------------------------------- 
  80 // private functions: (exclusively) lock/unlock the file 
  81 // ---------------------------------------------------------------------------- 
  85 static int wxLockFile(int fd
, LockOperation lock
) 
  87     // init the flock parameter struct 
  89     fl
.l_type 
= lock 
== LOCK 
? F_WRLCK 
: F_UNLCK
; 
  91     // lock the entire file 
  99     return fcntl(fd
, F_SETLK
, &fl
); 
 104 static int wxLockFile(int fd
, LockOperation lock
) 
 106     return flock(fd
, lock 
== LOCK 
? LOCK_EX 
| LOCK_NB 
: LOCK_UN
); 
 109 #endif // fcntl()/flock() 
 111 // ---------------------------------------------------------------------------- 
 112 // wxSingleInstanceCheckerImpl: the real implementation class 
 113 // ---------------------------------------------------------------------------- 
 115 class wxSingleInstanceCheckerImpl
 
 118     wxSingleInstanceCheckerImpl() 
 124     bool Create(const wxString
& name
); 
 126     pid_t 
GetLockerPID() const { return m_pidLocker
; } 
 128     ~wxSingleInstanceCheckerImpl() { Unlock(); } 
 131     // try to create and lock the file 
 132     LockResult 
CreateLockFile(); 
 134     // unlock and remove the lock file 
 137     // the descriptor of our lock file, -1 if none 
 140     // pid of the process owning the lock file 
 143     // the name of the lock file 
 147 // ============================================================================ 
 148 // wxSingleInstanceCheckerImpl implementation 
 149 // ============================================================================ 
 151 LockResult 
wxSingleInstanceCheckerImpl::CreateLockFile() 
 153     // try to open the file 
 154     m_fdLock 
= open(m_nameLock
, 
 155                     O_WRONLY 
| O_CREAT 
| O_EXCL
, 
 158     if ( m_fdLock 
!= -1 ) 
 161         int rc 
= wxLockFile(m_fdLock
, LOCK
); 
 164             // fine, we have the exclusive lock to the file, write our PID 
 166             m_pidLocker 
= getpid(); 
 168             // use char here, not wxChar! 
 169             char buf
[256]; // enough for any PID size 
 170             int len 
= sprintf(buf
, "%d", (int)m_pidLocker
) + 1; 
 172             if ( write(m_fdLock
, buf
, len
) != len 
) 
 174                 wxLogSysError(_("Failed to write to lock file '%s'"), 
 186         else // failure: see what exactly happened 
 191             if ( rc 
!= EACCES 
&& rc 
!= EAGAIN 
) 
 193                 wxLogSysError(_("Failed to lock the lock file '%s'"), 
 200             //else: couldn't lock because the lock is held by another process: 
 201             //      this might have happened because of a race condition: 
 202             //      maybe another instance opened and locked the file between 
 203             //      our calls to open() and flock(), so don't give an error 
 207     // we didn't create and lock the file 
 211 bool wxSingleInstanceCheckerImpl::Create(const wxString
& name
) 
 215     switch ( CreateLockFile() ) 
 218             // there is a lock file, check below if it is still valid 
 222             // nothing more to do 
 230     // try to open the file for reading and get the PID of the process 
 232     wxFile 
file(name
, wxFile::read
); 
 233     if ( !file
.IsOpened() ) 
 235         // well, this is really weird - file doesn't exist and we can't 
 238         // normally, this just means that we don't have write access to 
 239         // the directory where we try to create it, so return failure, 
 240         // even it might also be a rare case of a race condition when 
 241         // another process managed to open and lock the file and terminate 
 242         // (erasing it) before we got here, but this should happen so 
 243         // rarely in practice that we don't care 
 244         wxLogError(_("Failed to access lock file.")); 
 250     off_t count 
= file
.Read(buf
, WXSIZEOF(buf
)); 
 251     if ( count 
== wxInvalidOffset 
) 
 253         wxLogError(_("Failed to read PID from lock file.")); 
 257         if ( sscanf(buf
, "%d", (int *)&m_pidLocker
) == 1 ) 
 259             if ( kill(m_pidLocker
, 0) != 0 ) 
 261                 if ( unlink(name
) != 0 ) 
 263                     wxLogError(_("Failed to remove stale lock file '%s'."), 
 266                     // return TRUE in this case for now... 
 270                     wxLogMessage(_("Deleted stale lock file '%s'."), 
 274                     (void)CreateLockFile(); 
 277             //else: the other process is running 
 281             wxLogWarning(_("Invalid lock file '%s'."), name
.c_str()); 
 285     // return TRUE if we could get the PID of the process owning the lock file 
 286     // (whether it is still running or not), FALSE otherwise as it is 
 288     return m_pidLocker 
!= 0; 
 291 void wxSingleInstanceCheckerImpl::Unlock() 
 293     if ( m_fdLock 
!= -1 ) 
 295         if ( unlink(m_nameLock
) != 0 ) 
 297             wxLogSysError(_("Failed to remove lock file '%s'"), 
 301         if ( wxLockFile(m_fdLock
, UNLOCK
) != 0 ) 
 303             wxLogSysError(_("Failed to unlock lock file '%s'"), 
 307         if ( close(m_fdLock
) != 0 ) 
 309             wxLogSysError(_("Failed to close lock file '%s'"), 
 317 // ============================================================================ 
 318 // wxSingleInstanceChecker implementation 
 319 // ============================================================================ 
 321 bool wxSingleInstanceChecker::Create(const wxString
& name
, 
 322                                      const wxString
& path
) 
 324     wxASSERT_MSG( !m_impl
, 
 325                   _T("calling wxSingleInstanceChecker::Create() twice?") ); 
 327     // must have the file name to create a lock file 
 328     wxASSERT_MSG( !name
.empty(), _T("lock file name can't be empty") ); 
 330     m_impl 
= new wxSingleInstanceCheckerImpl
; 
 332     wxString fullname 
= path
; 
 333     if ( fullname
.empty() ) 
 335         fullname 
<< wxGetHomeDir() << _T('/'); 
 340     return m_impl
->Create(fullname
); 
 343 bool wxSingleInstanceChecker::IsAnotherRunning() const 
 345     wxCHECK_MSG( m_impl
, FALSE
, _T("must call Create() first") ); 
 347     // if another instance is running, it must own the lock file - otherwise 
 348     // we have it and the locker PID is ours one 
 349     return m_impl
->GetLockerPID() != getpid(); 
 352 wxSingleInstanceChecker::~wxSingleInstanceChecker() 
 357 #endif // wxUSE_SNGLINST_CHECKER