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
.fn_str(), 
 155                     O_WRONLY 
| O_CREAT 
| O_EXCL
, 
 158     if ( m_fdLock 
!= -1 ) 
 161         if ( wxLockFile(m_fdLock
, LOCK
) == 0 ) 
 163             // fine, we have the exclusive lock to the file, write our PID 
 165             m_pidLocker 
= getpid(); 
 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; 
 171             if ( write(m_fdLock
, buf
, len
) != len 
) 
 173                 wxLogSysError(_("Failed to write to lock file '%s'"), 
 185         else // failure: see what exactly happened 
 190             if ( errno 
!= EACCES 
&& errno 
!= EAGAIN 
) 
 192                 wxLogSysError(_("Failed to lock the lock file '%s'"), 
 195                 unlink(m_nameLock
.fn_str()); 
 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 
 206     // we didn't create and lock the file 
 210 bool wxSingleInstanceCheckerImpl::Create(const wxString
& name
) 
 214     switch ( CreateLockFile() ) 
 217             // there is a lock file, check below if it is still valid 
 221             // nothing more to do 
 229     // try to open the file for reading and get the PID of the process 
 231     wxFile 
file(name
, wxFile::read
); 
 232     if ( !file
.IsOpened() ) 
 234         // well, this is really weird - file doesn't exist and we can't 
 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.")); 
 249     off_t count 
= file
.Read(buf
, WXSIZEOF(buf
)); 
 250     if ( count 
== wxInvalidOffset 
) 
 252         wxLogError(_("Failed to read PID from lock file.")); 
 256         if ( sscanf(buf
, "%d", (int *)&m_pidLocker
) == 1 ) 
 258             if ( kill(m_pidLocker
, 0) != 0 ) 
 260                 if ( unlink(name
.fn_str()) != 0 ) 
 262                     wxLogError(_("Failed to remove stale lock file '%s'."), 
 265                     // return TRUE in this case for now... 
 269                     wxLogMessage(_("Deleted stale lock file '%s'."), 
 273                     (void)CreateLockFile(); 
 276             //else: the other process is running 
 280             wxLogWarning(_("Invalid lock file '%s'."), name
.c_str()); 
 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 
 287     return m_pidLocker 
!= 0; 
 290 void wxSingleInstanceCheckerImpl::Unlock() 
 292     if ( m_fdLock 
!= -1 ) 
 294         if ( unlink(m_nameLock
.fn_str()) != 0 ) 
 296             wxLogSysError(_("Failed to remove lock file '%s'"), 
 300         if ( wxLockFile(m_fdLock
, UNLOCK
) != 0 ) 
 302             wxLogSysError(_("Failed to unlock lock file '%s'"), 
 306         if ( close(m_fdLock
) != 0 ) 
 308             wxLogSysError(_("Failed to close lock file '%s'"), 
 316 // ============================================================================ 
 317 // wxSingleInstanceChecker implementation 
 318 // ============================================================================ 
 320 bool wxSingleInstanceChecker::Create(const wxString
& name
, 
 321                                      const wxString
& path
) 
 323     wxASSERT_MSG( !m_impl
, 
 324                   _T("calling wxSingleInstanceChecker::Create() twice?") ); 
 326     // must have the file name to create a lock file 
 327     wxASSERT_MSG( !name
.empty(), _T("lock file name can't be empty") ); 
 329     m_impl 
= new wxSingleInstanceCheckerImpl
; 
 331     wxString fullname 
= path
; 
 332     if ( fullname
.empty() ) 
 334         fullname 
= wxGetHomeDir(); 
 337     if ( fullname
.Last() != _T('/') ) 
 344     return m_impl
->Create(fullname
); 
 347 bool wxSingleInstanceChecker::IsAnotherRunning() const 
 349     wxCHECK_MSG( m_impl
, FALSE
, _T("must call Create() first") ); 
 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(); 
 356 wxSingleInstanceChecker::~wxSingleInstanceChecker() 
 361 #endif // wxUSE_SNGLINST_CHECKER