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 licence 
  11 /////////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  21 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  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'"), 
 183             // change file's permission so that only this user can access it: 
 184             if ( chmod(m_nameLock
.fn_str(), S_IRUSR 
| S_IWUSR
) != 0 ) 
 186                 wxLogSysError(_("Failed to set permissions on lock file '%s'"), 
 196         else // failure: see what exactly happened 
 201             if ( errno 
!= EACCES 
&& errno 
!= EAGAIN 
) 
 203                 wxLogSysError(_("Failed to lock the lock file '%s'"), 
 206                 unlink(m_nameLock
.fn_str()); 
 210             //else: couldn't lock because the lock is held by another process: 
 211             //      this might have happened because of a race condition: 
 212             //      maybe another instance opened and locked the file between 
 213             //      our calls to open() and flock(), so don't give an error 
 217     // we didn't create and lock the file 
 221 bool wxSingleInstanceCheckerImpl::Create(const wxString
& name
) 
 225     switch ( CreateLockFile() ) 
 228             // there is a lock file, check below if it is still valid 
 232             // nothing more to do 
 240     // Check if the file is owned by current user and has 0600 permissions. 
 241     // If it doesn't, it's a fake file, possibly meant as a DoS attack, and 
 242     // so we refuse to touch it: 
 244     if ( wxStat(name
, &stats
) != 0 ) 
 246         wxLogSysError(_("Failed to inspect the lock file '%s'"), name
.c_str()); 
 249     if ( stats
.st_uid 
!= getuid() ) 
 251         wxLogError(_("Lock file '%s' has incorrect owner."), name
.c_str()); 
 254     if ( stats
.st_mode 
!= (S_IFREG 
| S_IRUSR 
| S_IWUSR
) ) 
 256         wxLogError(_("Lock file '%s' has incorrect permissions."), name
.c_str()); 
 260     // try to open the file for reading and get the PID of the process 
 262     wxFile 
file(name
, wxFile::read
); 
 263     if ( !file
.IsOpened() ) 
 265         // well, this is really weird - file doesn't exist and we can't 
 268         // normally, this just means that we don't have write access to 
 269         // the directory where we try to create it, so return failure, 
 270         // even it might also be a rare case of a race condition when 
 271         // another process managed to open and lock the file and terminate 
 272         // (erasing it) before we got here, but this should happen so 
 273         // rarely in practice that we don't care 
 274         wxLogError(_("Failed to access lock file.")); 
 280     ssize_t count 
= file
.Read(buf
, WXSIZEOF(buf
)); 
 281     if ( count 
== wxInvalidOffset 
) 
 283         wxLogError(_("Failed to read PID from lock file.")); 
 287         if ( sscanf(buf
, "%d", (int *)&m_pidLocker
) == 1 ) 
 289             if ( kill(m_pidLocker
, 0) != 0 ) 
 291                 if ( unlink(name
.fn_str()) != 0 ) 
 293                     wxLogError(_("Failed to remove stale lock file '%s'."), 
 296                     // return TRUE in this case for now... 
 300                     wxLogMessage(_("Deleted stale lock file '%s'."), 
 304                     (void)CreateLockFile(); 
 307             //else: the other process is running 
 311             wxLogWarning(_("Invalid lock file '%s'."), name
.c_str()); 
 315     // return TRUE if we could get the PID of the process owning the lock file 
 316     // (whether it is still running or not), FALSE otherwise as it is 
 318     return m_pidLocker 
!= 0; 
 321 void wxSingleInstanceCheckerImpl::Unlock() 
 323     if ( m_fdLock 
!= -1 ) 
 325         if ( unlink(m_nameLock
.fn_str()) != 0 ) 
 327             wxLogSysError(_("Failed to remove lock file '%s'"), 
 331         if ( wxLockFile(m_fdLock
, UNLOCK
) != 0 ) 
 333             wxLogSysError(_("Failed to unlock lock file '%s'"), 
 337         if ( close(m_fdLock
) != 0 ) 
 339             wxLogSysError(_("Failed to close lock file '%s'"), 
 347 // ============================================================================ 
 348 // wxSingleInstanceChecker implementation 
 349 // ============================================================================ 
 351 bool wxSingleInstanceChecker::Create(const wxString
& name
, 
 352                                      const wxString
& path
) 
 354     wxASSERT_MSG( !m_impl
, 
 355                   _T("calling wxSingleInstanceChecker::Create() twice?") ); 
 357     // must have the file name to create a lock file 
 358     wxASSERT_MSG( !name
.empty(), _T("lock file name can't be empty") ); 
 360     m_impl 
= new wxSingleInstanceCheckerImpl
; 
 362     wxString fullname 
= path
; 
 363     if ( fullname
.empty() ) 
 365         fullname 
= wxGetHomeDir(); 
 368     if ( fullname
.Last() != _T('/') ) 
 375     return m_impl
->Create(fullname
); 
 378 bool wxSingleInstanceChecker::IsAnotherRunning() const 
 380     wxCHECK_MSG( m_impl
, FALSE
, _T("must call Create() first") ); 
 382     // if another instance is running, it must own the lock file - otherwise 
 383     // we have it and the locker PID is ours one 
 384     return m_impl
->GetLockerPID() != getpid(); 
 387 wxSingleInstanceChecker::~wxSingleInstanceChecker() 
 392 #endif // wxUSE_SNGLINST_CHECKER