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 // For compilers that support precompilation, includes "wx.h". 
  22 #include "wx/wxprec.h" 
  28 #if wxUSE_SNGLINST_CHECKER 
  31     #include "wx/string.h" 
  37 #include "wx/utils.h"           // wxGetHomeDir() 
  39 #include "wx/snglinst.h" 
  42 #include <sys/types.h> 
  43 #include <sys/stat.h>           // for S_I[RW]USR 
  44 #include <signal.h>             // for kill() 
  49 #elif defined(HAVE_FLOCK) 
  52     // normally, wxUSE_SNGLINST_CHECKER must have been reset by configure 
  53     #error "wxSingleInstanceChecker can't be compiled on this platform" 
  54 #endif // fcntl()/flock() 
  56 // ---------------------------------------------------------------------------- 
  58 // ---------------------------------------------------------------------------- 
  60 // argument of wxLockFile() 
  67 // return value of CreateLockFile() 
  75 // ---------------------------------------------------------------------------- 
  76 // private functions: (exclusively) lock/unlock the file 
  77 // ---------------------------------------------------------------------------- 
  81 static int wxLockFile(int fd
, LockOperation lock
) 
  83     // init the flock parameter struct 
  85     fl
.l_type 
= lock 
== LOCK 
? F_WRLCK 
: F_UNLCK
; 
  87     // lock the entire file 
  95     return fcntl(fd
, F_SETLK
, &fl
); 
 100 static int wxLockFile(int fd
, LockOperation lock
) 
 102     return flock(fd
, lock 
== LOCK 
? LOCK_EX 
| LOCK_NB 
: LOCK_UN
); 
 105 #endif // fcntl()/flock() 
 107 // ---------------------------------------------------------------------------- 
 108 // wxSingleInstanceCheckerImpl: the real implementation class 
 109 // ---------------------------------------------------------------------------- 
 111 class wxSingleInstanceCheckerImpl
 
 114     wxSingleInstanceCheckerImpl() 
 120     bool Create(const wxString
& name
); 
 122     pid_t 
GetLockerPID() const { return m_pidLocker
; } 
 124     ~wxSingleInstanceCheckerImpl() { Unlock(); } 
 127     // try to create and lock the file 
 128     LockResult 
CreateLockFile(); 
 130     // unlock and remove the lock file 
 133     // the descriptor of our lock file, -1 if none 
 136     // pid of the process owning the lock file 
 139     // the name of the lock file 
 143 // ============================================================================ 
 144 // wxSingleInstanceCheckerImpl implementation 
 145 // ============================================================================ 
 147 LockResult 
wxSingleInstanceCheckerImpl::CreateLockFile() 
 149     // try to open the file 
 150     m_fdLock 
= open(m_nameLock
.fn_str(), 
 151                     O_WRONLY 
| O_CREAT 
| O_EXCL
, 
 154     if ( m_fdLock 
!= -1 ) 
 157         if ( wxLockFile(m_fdLock
, LOCK
) == 0 ) 
 159             // fine, we have the exclusive lock to the file, write our PID 
 161             m_pidLocker 
= getpid(); 
 163             // use char here, not wxChar! 
 164             char buf
[256]; // enough for any PID size 
 165             int len 
= sprintf(buf
, "%d", (int)m_pidLocker
) + 1; 
 167             if ( write(m_fdLock
, buf
, len
) != len 
) 
 169                 wxLogSysError(_("Failed to write to lock file '%s'"), 
 179             // change file's permission so that only this user can access it: 
 180             if ( chmod(m_nameLock
.fn_str(), S_IRUSR 
| S_IWUSR
) != 0 ) 
 182                 wxLogSysError(_("Failed to set permissions on lock file '%s'"), 
 192         else // failure: see what exactly happened 
 197             if ( errno 
!= EACCES 
&& errno 
!= EAGAIN 
) 
 199                 wxLogSysError(_("Failed to lock the lock file '%s'"), 
 202                 unlink(m_nameLock
.fn_str()); 
 206             //else: couldn't lock because the lock is held by another process: 
 207             //      this might have happened because of a race condition: 
 208             //      maybe another instance opened and locked the file between 
 209             //      our calls to open() and flock(), so don't give an error 
 213     // we didn't create and lock the file 
 217 bool wxSingleInstanceCheckerImpl::Create(const wxString
& name
) 
 221     switch ( CreateLockFile() ) 
 224             // there is a lock file, check below if it is still valid 
 228             // nothing more to do 
 236     // Check if the file is owned by current user and has 0600 permissions. 
 237     // If it doesn't, it's a fake file, possibly meant as a DoS attack, and 
 238     // so we refuse to touch it: 
 240     if ( wxStat(name
, &stats
) != 0 ) 
 242         wxLogSysError(_("Failed to inspect the lock file '%s'"), name
.c_str()); 
 245     if ( stats
.st_uid 
!= getuid() ) 
 247         wxLogError(_("Lock file '%s' has incorrect owner."), name
.c_str()); 
 250     if ( stats
.st_mode 
!= (S_IFREG 
| S_IRUSR 
| S_IWUSR
) ) 
 252         wxLogError(_("Lock file '%s' has incorrect permissions."), name
.c_str()); 
 256     // try to open the file for reading and get the PID of the process 
 258     wxFile 
file(name
, wxFile::read
); 
 259     if ( !file
.IsOpened() ) 
 261         // well, this is really weird - file doesn't exist and we can't 
 264         // normally, this just means that we don't have write access to 
 265         // the directory where we try to create it, so return failure, 
 266         // even it might also be a rare case of a race condition when 
 267         // another process managed to open and lock the file and terminate 
 268         // (erasing it) before we got here, but this should happen so 
 269         // rarely in practice that we don't care 
 270         wxLogError(_("Failed to access lock file.")); 
 276     ssize_t count 
= file
.Read(buf
, WXSIZEOF(buf
)); 
 277     if ( count 
== wxInvalidOffset 
) 
 279         wxLogError(_("Failed to read PID from lock file.")); 
 283         if ( sscanf(buf
, "%d", (int *)&m_pidLocker
) == 1 ) 
 285             if ( kill(m_pidLocker
, 0) != 0 ) 
 287                 if ( unlink(name
.fn_str()) != 0 ) 
 289                     wxLogError(_("Failed to remove stale lock file '%s'."), 
 292                     // return TRUE in this case for now... 
 296                     wxLogMessage(_("Deleted stale lock file '%s'."), 
 300                     (void)CreateLockFile(); 
 303             //else: the other process is running 
 307             wxLogWarning(_("Invalid lock file '%s'."), name
.c_str()); 
 311     // return TRUE if we could get the PID of the process owning the lock file 
 312     // (whether it is still running or not), FALSE otherwise as it is 
 314     return m_pidLocker 
!= 0; 
 317 void wxSingleInstanceCheckerImpl::Unlock() 
 319     if ( m_fdLock 
!= -1 ) 
 321         if ( unlink(m_nameLock
.fn_str()) != 0 ) 
 323             wxLogSysError(_("Failed to remove lock file '%s'"), 
 327         if ( wxLockFile(m_fdLock
, UNLOCK
) != 0 ) 
 329             wxLogSysError(_("Failed to unlock lock file '%s'"), 
 333         if ( close(m_fdLock
) != 0 ) 
 335             wxLogSysError(_("Failed to close lock file '%s'"), 
 343 // ============================================================================ 
 344 // wxSingleInstanceChecker implementation 
 345 // ============================================================================ 
 347 bool wxSingleInstanceChecker::Create(const wxString
& name
, 
 348                                      const wxString
& path
) 
 350     wxASSERT_MSG( !m_impl
, 
 351                   _T("calling wxSingleInstanceChecker::Create() twice?") ); 
 353     // must have the file name to create a lock file 
 354     wxASSERT_MSG( !name
.empty(), _T("lock file name can't be empty") ); 
 356     m_impl 
= new wxSingleInstanceCheckerImpl
; 
 358     wxString fullname 
= path
; 
 359     if ( fullname
.empty() ) 
 361         fullname 
= wxGetHomeDir(); 
 364     if ( fullname
.Last() != _T('/') ) 
 371     return m_impl
->Create(fullname
); 
 374 bool wxSingleInstanceChecker::IsAnotherRunning() const 
 376     wxCHECK_MSG( m_impl
, FALSE
, _T("must call Create() first") ); 
 378     // if another instance is running, it must own the lock file - otherwise 
 379     // we have it and the locker PID is ours one 
 380     return m_impl
->GetLockerPID() != getpid(); 
 383 wxSingleInstanceChecker::~wxSingleInstanceChecker() 
 388 #endif // wxUSE_SNGLINST_CHECKER