Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

248 lines
7.0 KiB

  1. // ======= Copyright (c) 2009, Valve Corporation, All rights reserved. =========
  2. //
  3. // appinstance.cpp
  4. //
  5. // Purpose: Provide a simple way to enforce that only one instance of an
  6. // application is running on a machine at any one time.
  7. //
  8. // Usage: declare a global object of CSingleAppInstance type, with the unique name
  9. // you want to use wrapped in the TEXT( " " ) macro.
  10. //
  11. // upon entering main you can check the IsUniqueInstance() method to determine if another instance is running
  12. // or you can call the CheckForOtherRunningInstances() method to perform the check AND optinally
  13. // pop up a message box to the user, and/or have the program terminate
  14. //
  15. // Example:
  16. //
  17. // CSingleAppInstance g_ThisAppInstance( TEXT("this_source_app") );
  18. //
  19. // void main(int argc,char **argv)
  20. // {
  21. // if ( g_ThisAppInstance.CheckForOtherRunningInstances() ) return;
  22. //
  23. // .. rest of code ..
  24. //
  25. // Notes: Currently this object only works when IsPlatformWindows() is true
  26. // i.e. no Xbox 360, linux, or Mac yet..
  27. // (feel free to impliment)
  28. //
  29. // ===========================================================================
  30. #include "tier0/platform.h"
  31. #include "tier1/appinstance.h"
  32. #include "tier1/strtools.h"
  33. #include "tier0/dbg.h"
  34. #include "tier0/memdbgon.h"
  35. #ifdef PLATFORM_WINDOWS_PC
  36. #include <windows.h>
  37. #endif
  38. #ifdef PLATFORM_OSX
  39. #include <fcntl.h>
  40. #include <sys/types.h>
  41. #include <sys/stat.h>
  42. #include "tier1/checksum_crc.h"
  43. #endif
  44. // ===========================================================================
  45. // Constructor - create a named mutex on the PC and see if someone else has
  46. // already done it.
  47. // ===========================================================================
  48. CSingleAppInstance::CSingleAppInstance( tchar* InstanceName, bool exitOnNotUnique, bool displayMsgIfNotUnique )
  49. {
  50. // defaults for non-Windows builds
  51. m_hMutex = NULL;
  52. m_isUniqueInstance = true;
  53. if ( InstanceName == NULL || V_strlen( InstanceName ) == 0 || V_strlen( InstanceName ) >= MAX_PATH )
  54. {
  55. Assert( false );
  56. return;
  57. }
  58. #ifndef _PS3
  59. #ifdef WIN32
  60. if ( IsPlatformWindows() )
  61. {
  62. // don't allow more than one instance to run
  63. m_hMutex = ::CreateMutex( NULL, FALSE, InstanceName );
  64. unsigned int waitResult = ::WaitForSingleObject( m_hMutex, 0 );
  65. // Here, we have the mutex
  66. if ( waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED )
  67. {
  68. m_isUniqueInstance = true;
  69. return;
  70. }
  71. // couldn't get the mutex, we must be running another instance
  72. ::CloseHandle( m_hMutex );
  73. m_hMutex = NULL;
  74. // note that we are not unique, i.e. another instance of this app (or one using the same instance name) is running
  75. m_isUniqueInstance = false;
  76. CheckForOtherRunningInstances( exitOnNotUnique, displayMsgIfNotUnique );
  77. }
  78. #elif defined(OSX)
  79. m_hMutex = -1; // 0 in theory is a valid fd, so set the sentinal to -1 for checking in the destructor
  80. // Under OSX use flock in /tmp/source_engine_<game>.lock, create the file if it doesn't exist
  81. CRC32_t gameCRC;
  82. CRC32_Init(&gameCRC);
  83. CRC32_ProcessBuffer( &gameCRC, (void *)InstanceName, Q_strlen( InstanceName ) );
  84. CRC32_Final( &gameCRC );
  85. V_snprintf( m_szLockPath, sizeof(m_szLockPath), "/tmp/source_engine_%lu.lock", gameCRC );
  86. m_hMutex = open( m_szLockPath, O_CREAT | O_WRONLY | O_EXLOCK | O_NONBLOCK | O_TRUNC, 0777 );
  87. if( m_hMutex >= 0 )
  88. {
  89. // make sure we give full perms to the file, we only one instance per machine
  90. fchmod( m_hMutex, 0777 );
  91. m_isUniqueInstance = true;
  92. // we leave the file open, under unix rules when we die we'll automatically close and remove the locks
  93. return;
  94. }
  95. // We were unable to open the file, it should be because we are unable to retain a lock
  96. if ( errno != EWOULDBLOCK)
  97. {
  98. fprintf( stderr, "unexpected error %d trying to exclusively lock %s\n", errno, m_szLockPath );
  99. }
  100. m_isUniqueInstance = false;
  101. #endif
  102. #endif // _PS3
  103. }
  104. CSingleAppInstance::~CSingleAppInstance()
  105. {
  106. #ifndef _PS3
  107. #ifdef WIN32
  108. if ( IsPlatformWindows() && m_hMutex )
  109. {
  110. ::ReleaseMutex( m_hMutex );
  111. ::CloseHandle( m_hMutex );
  112. m_hMutex = NULL;
  113. }
  114. #elif defined(OSX)
  115. if ( m_hMutex != -1 )
  116. {
  117. close( m_hMutex );
  118. m_hMutex = -1;
  119. unlink( m_szLockPath );
  120. }
  121. #endif
  122. #endif // _PS3
  123. }
  124. bool CSingleAppInstance::CheckForOtherRunningInstances( bool exitOnNotUnique, bool displayMsgIfNotUnique )
  125. {
  126. if ( IsPlatformWindows() || IsOSX() )
  127. {
  128. // are we the only running instance of this app? Then we are Unique (aren't we SPECIAL?)
  129. if ( m_isUniqueInstance )
  130. {
  131. return false;
  132. }
  133. // should we display a message to the user?
  134. if ( displayMsgIfNotUnique )
  135. {
  136. Plat_MessageBox( "Alert", "Another copy of this program is already running on this machine. Only one instance at a time is allowed" );
  137. }
  138. // should we attempt normal program termination?
  139. if ( exitOnNotUnique )
  140. {
  141. exit( 0 );
  142. }
  143. return true;
  144. }
  145. // We fell through, so act like there are no other instances
  146. return false;
  147. }
  148. // ===========================================================================
  149. // Static Function - this is used by *other* programs to query the system for
  150. // a running program that grabs the named mutex/has a CSingleAppInstance
  151. // object of the specified name.
  152. // It's a way to check if a tool is running or not, (and then presumably
  153. // lauch it if it is not there).
  154. // ===========================================================================
  155. bool CSingleAppInstance::CheckForRunningInstance( tchar* InstanceName )
  156. {
  157. #ifndef _PS3
  158. // validate input
  159. Assert( InstanceName != NULL && V_strlen( InstanceName ) > 0 && V_strlen( InstanceName ) < MAX_PATH );
  160. #ifdef WIN32
  161. if ( IsPlatformWindows() )
  162. {
  163. // don't allow more than one instance to run
  164. HANDLE hMutex = ::CreateMutex( NULL, FALSE, InstanceName );
  165. unsigned int waitResult = ::WaitForSingleObject( hMutex, 0 );
  166. ::CloseHandle( hMutex );
  167. // Did we grab the mutex successfully? nope...
  168. if ( waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED )
  169. {
  170. return false;
  171. }
  172. // couldn't get the mutex, must be another instance running somewhere that has it
  173. return true;
  174. }
  175. #elif defined(OSX)
  176. // Under OSX use flock in /tmp/source_engine_<game>.lock, create the file if it doesn't exist
  177. CRC32_t gameCRC;
  178. CRC32_Init(&gameCRC);
  179. CRC32_ProcessBuffer( &gameCRC, (void *)InstanceName, Q_strlen( InstanceName ) );
  180. CRC32_Final( &gameCRC );
  181. char szLockPath[ MAX_PATH ];
  182. V_snprintf( szLockPath, sizeof(szLockPath), "/tmp/source_engine_%lu.lock", gameCRC );
  183. int lockFD = open( szLockPath, O_CREAT | O_WRONLY | O_EXLOCK | O_NONBLOCK | O_TRUNC, 0777 );
  184. if( lockFD >= 0 )
  185. {
  186. close( lockFD );
  187. unlink( szLockPath );
  188. return false;
  189. }
  190. // We were unable to open the file, it should be because we are unable to retain a lock
  191. if ( errno != EWOULDBLOCK)
  192. {
  193. fprintf( stderr, "unexpected error %d trying to exclusively lock %s\n", errno, szLockPath );
  194. }
  195. // couldn't get the mutex, must be another instance running somewhere that has it
  196. return true;
  197. #endif
  198. #endif // _PS3
  199. return false;
  200. }