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.

709 lines
17 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #if !defined( _X360 )
  7. #include <windows.h>
  8. #endif
  9. #include "vstdlib/iprocessutils.h"
  10. #include "tier1/utllinkedlist.h"
  11. #include "tier1/utlstring.h"
  12. #include "tier1/utlbuffer.h"
  13. #include "tier1/tier1.h"
  14. // NOTE: This has to be the last file included!
  15. #include "tier0/memdbgon.h"
  16. class CProcess;
  17. class CProcessPipeRead : public IPipeRead
  18. {
  19. // IPipeRead overrides.
  20. public:
  21. virtual int GetNumBytesAvailable();
  22. virtual void ReadAvailable( CUtlString &sStr, int nMaxBytes );
  23. virtual void ReadAvailable( CUtlBuffer *pOutBuffer, int nMaxBytes );
  24. virtual void Read( CUtlString &sStr, int nMaxBytes );
  25. virtual void ReadLine( CUtlString &sStr );
  26. public:
  27. bool IsValid() const;
  28. // This reads in whatever data is available in the pipe and caches it.
  29. void CacheOutput();
  30. // Returns true when there is data in m_hRead, false otherwise.
  31. bool WaitForOutput();
  32. int GetActualProcessOutputSize();
  33. int GetProcessOutput( void *pBuf, int nBytes );
  34. int GetActualProcessOutput( void *pBuf, int nBufLen );
  35. public:
  36. CProcess *m_pProcess;
  37. HANDLE m_hRead; // Comes from ProcessInfo_t::m_hChildStdoutRd or m_hChildStderrRd.
  38. // This is stored if they wait for the process to exit. Even though they haven't read the data yet,
  39. // they can still call ReadAvailable() or GetNumBytesAvailable() on stdio after the process was terminated.
  40. CUtlBuffer m_CachedOutput;
  41. };
  42. struct ProcessInfo_t
  43. {
  44. HANDLE m_hChildStdinRd;
  45. HANDLE m_hChildStdinWr;
  46. HANDLE m_hChildStdoutRd;
  47. HANDLE m_hChildStdoutWr;
  48. HANDLE m_hChildStderrRd;
  49. HANDLE m_hChildStderrWr;
  50. HANDLE m_hProcess;
  51. CUtlString m_CommandLine;
  52. int m_fFlags; // PROCESSSTART_xxx.
  53. };
  54. class CProcessUtils;
  55. class CProcess : public IProcess
  56. {
  57. public:
  58. CProcess( CProcessUtils *pProcessUtils, const ProcessInfo_t &info );
  59. // IProcess overrides.
  60. public:
  61. virtual void Release();
  62. virtual void Abort();
  63. virtual bool IsComplete();
  64. virtual int WaitUntilComplete();
  65. virtual int WriteStdin( char *pBuf, int nBufLen );
  66. virtual IPipeRead* GetStdout();
  67. virtual IPipeRead* GetStderr();
  68. virtual int GetExitCode();
  69. public:
  70. ProcessInfo_t m_Info;
  71. CProcessPipeRead m_StdoutRead;
  72. CProcessPipeRead m_StderrRead;
  73. CProcessUtils *m_pProcessUtils;
  74. intp m_nProcessesIndex; // Index into CProcessUtils::m_Processes.
  75. };
  76. //-----------------------------------------------------------------------------
  77. // At the moment, we can only run one process at a time
  78. //-----------------------------------------------------------------------------
  79. class CProcessUtils : public CTier1AppSystem< IProcessUtils >
  80. {
  81. typedef CTier1AppSystem< IProcessUtils > BaseClass;
  82. public:
  83. CProcessUtils() {}
  84. // Inherited from IAppSystem
  85. virtual InitReturnVal_t Init();
  86. virtual void Shutdown();
  87. // Inherited from IProcessUtils
  88. virtual IProcess* StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir );
  89. virtual IProcess* StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir );
  90. virtual int SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir, CUtlString *pStdout );
  91. public:
  92. void OnProcessDelete( CProcess *pProcess );
  93. private:
  94. // creates the process, adds it to the list and writes the windows HANDLE into info.m_hProcess
  95. CProcess* CreateProcess( ProcessInfo_t &info, int fFlags, const char *pWorkingDir );
  96. CUtlFixedLinkedList< CProcess* > m_Processes;
  97. bool m_bInitialized;
  98. };
  99. //-----------------------------------------------------------------------------
  100. // Purpose: singleton accessor
  101. //-----------------------------------------------------------------------------
  102. static CProcessUtils s_ProcessUtils;
  103. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CProcessUtils, IProcessUtils, PROCESS_UTILS_INTERFACE_VERSION, s_ProcessUtils );
  104. //-----------------------------------------------------------------------------
  105. // Returns the last error that occurred
  106. //-----------------------------------------------------------------------------
  107. char *GetErrorString( char *pBuf, int nBufLen )
  108. {
  109. FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, pBuf, nBufLen, NULL );
  110. char *p = strchr(pBuf, '\r'); // get rid of \r\n
  111. if(p)
  112. {
  113. p[0] = 0;
  114. }
  115. return pBuf;
  116. }
  117. // ------------------------------------------------------------------------------------- //
  118. // CProcessPipeRead implementation.
  119. // ------------------------------------------------------------------------------------- //
  120. bool CProcessPipeRead::IsValid() const
  121. {
  122. return m_hRead != INVALID_HANDLE_VALUE;
  123. }
  124. int CProcessPipeRead::GetNumBytesAvailable()
  125. {
  126. return GetActualProcessOutputSize() + m_CachedOutput.TellPut();
  127. }
  128. void CProcessPipeRead::ReadAvailable( CUtlString &sStr, int32 nMaxBytes )
  129. {
  130. int nBytesAvailable = GetNumBytesAvailable();
  131. nBytesAvailable = MIN( nBytesAvailable, nMaxBytes );
  132. sStr.SetLength( nBytesAvailable ); // If nBytesAvailable != 0, this auto allocates an extra byte for the null terminator.
  133. if ( nBytesAvailable > 0 )
  134. {
  135. char *pOut = sStr.Get();
  136. int nBytesGotten = GetProcessOutput( pOut, nBytesAvailable );
  137. Assert( nBytesGotten == nBytesAvailable );
  138. // Null-terminate it.
  139. pOut[nBytesGotten] = 0;
  140. }
  141. }
  142. void CProcessPipeRead::ReadAvailable( CUtlBuffer *pOutBuffer, int nMaxBytes )
  143. {
  144. int nBytesAvailable = GetNumBytesAvailable();
  145. nBytesAvailable = MIN( nBytesAvailable, nMaxBytes );
  146. if ( nBytesAvailable > 0 )
  147. {
  148. char *pOut = (char*)pOutBuffer->AccessForDirectRead(nBytesAvailable+1);
  149. int nBytesGotten = GetProcessOutput( pOut, nBytesAvailable );
  150. Assert( nBytesGotten == nBytesAvailable );
  151. // Null-terminate it.
  152. pOut[nBytesGotten] = 0;
  153. }
  154. }
  155. void CProcessPipeRead::Read( CUtlString &sStr, int32 nBytes )
  156. {
  157. sStr.SetLength( 0 );
  158. int nBytesLeftToRead = nBytes;
  159. while ( nBytesLeftToRead > 0 )
  160. {
  161. int nAvail = GetNumBytesAvailable();
  162. if ( nAvail == 0 )
  163. {
  164. if ( m_pProcess->IsComplete() )
  165. {
  166. return;
  167. }
  168. else
  169. {
  170. WaitForOutput();
  171. continue;
  172. }
  173. }
  174. int nToRead = MIN( nBytesLeftToRead, nAvail );
  175. // Read as much as we need and add it to the string.
  176. CUtlString sTemp;
  177. ReadAvailable( sTemp, nToRead );
  178. Assert( sTemp.Length() == nToRead );
  179. sStr += sTemp;
  180. nBytesLeftToRead -= nToRead;
  181. }
  182. }
  183. void CProcessPipeRead::ReadLine( CUtlString &sStr )
  184. {
  185. sStr.SetLength( 0 );
  186. while ( 1 )
  187. {
  188. // Wait for output if there's nothing left in our cached output.
  189. if ( m_CachedOutput.GetBytesRemaining() == 0 && !WaitForOutput() )
  190. return;
  191. CacheOutput();
  192. char *pStr = (char*)m_CachedOutput.PeekGet();
  193. int nBytes = m_CachedOutput.GetBytesRemaining();
  194. // No more stuff available and the process is dead?
  195. if ( nBytes == 0 && m_pProcess->IsComplete() )
  196. break;
  197. int i;
  198. for ( i=0; i < nBytes; i++ )
  199. {
  200. if ( pStr[i] == '\n' )
  201. break;
  202. }
  203. if ( i < nBytes )
  204. {
  205. // We hit a line ending.
  206. int nBytesToRead = i;
  207. if ( nBytesToRead > 0 && pStr[nBytesToRead-1] == '\r' )
  208. --nBytesToRead;
  209. CUtlString sTemp;
  210. sTemp.SetDirect( pStr, nBytesToRead );
  211. sStr += sTemp;
  212. m_CachedOutput.SeekGet( CUtlBuffer::SEEK_CURRENT, i+1 ); // Use i here because it'll be at the \n, not the \r\n.
  213. if ( m_CachedOutput.GetBytesRemaining() == 0 )
  214. m_CachedOutput.Purge();
  215. return;
  216. }
  217. }
  218. }
  219. bool CProcessPipeRead::WaitForOutput()
  220. {
  221. if ( m_hRead == INVALID_HANDLE_VALUE )
  222. {
  223. Assert( false );
  224. return false;
  225. }
  226. while ( GetActualProcessOutputSize() == 0 )
  227. {
  228. if ( m_pProcess->IsComplete() )
  229. return false;
  230. else
  231. ThreadSleep( 1 );
  232. }
  233. return true;
  234. }
  235. void CProcessPipeRead::CacheOutput()
  236. {
  237. int nBytes = GetActualProcessOutputSize();
  238. if ( nBytes == 0 )
  239. return;
  240. int nPut = m_CachedOutput.TellPut();
  241. m_CachedOutput.EnsureCapacity( nPut + nBytes );
  242. int nBytesRead = GetActualProcessOutput( (char*)m_CachedOutput.PeekPut(), nBytes );
  243. Assert( nBytesRead == nBytes );
  244. m_CachedOutput.SeekPut( CUtlBuffer::SEEK_HEAD, nPut + nBytesRead );
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Methods used to read output back from a process
  248. //-----------------------------------------------------------------------------
  249. int CProcessPipeRead::GetActualProcessOutputSize()
  250. {
  251. if ( m_hRead == INVALID_HANDLE_VALUE )
  252. {
  253. Assert( false );
  254. return 0;
  255. }
  256. DWORD dwCount = 0;
  257. if ( !PeekNamedPipe( m_hRead, NULL, NULL, NULL, &dwCount, NULL ) )
  258. {
  259. char buf[ 512 ];
  260. Warning( "Could not read from pipe associated with command %s\n"
  261. "Windows gave the error message:\n \"%s\"\n",
  262. m_pProcess->m_Info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
  263. return 0;
  264. }
  265. return (int)dwCount;
  266. }
  267. int CProcessPipeRead::GetProcessOutput( void *pBuf, int nBytes )
  268. {
  269. int nCachedBytes = MIN( nBytes, m_CachedOutput.TellPut() );
  270. int nPipeBytes = nBytes - nCachedBytes;
  271. // Read from the cached buffer.
  272. m_CachedOutput.Get( pBuf, nCachedBytes );
  273. if ( m_CachedOutput.GetBytesRemaining() == 0 )
  274. {
  275. m_CachedOutput.Purge();
  276. }
  277. // Read from the pipe.
  278. int nPipedBytesRead = GetActualProcessOutput( (char*)pBuf + nCachedBytes, nPipeBytes );
  279. return nCachedBytes + nPipedBytesRead;
  280. }
  281. int CProcessPipeRead::GetActualProcessOutput( void *pBuf, int nBufLen )
  282. {
  283. if ( m_hRead == INVALID_HANDLE_VALUE )
  284. {
  285. Assert( false );
  286. return 0;
  287. }
  288. // ReadFile can deadlock in a blaze of awesomeness if you ask for 0 bytes.
  289. if ( nBufLen == 0 )
  290. return 0;
  291. DWORD nBytesRead = 0;
  292. BOOL bSuccess = ReadFile( m_hRead, pBuf, nBufLen, &nBytesRead, NULL );
  293. if ( bSuccess )
  294. {
  295. // ReadFile -should- block until it gets the number of bytes requested OR until
  296. // the process is complete. So if it didn't return the # of bytes requested,
  297. // then make sure the process is complete.
  298. if ( (int)nBytesRead != nBufLen )
  299. {
  300. Assert( m_pProcess->IsComplete() );
  301. }
  302. }
  303. else
  304. {
  305. Assert( false );
  306. }
  307. return (int)nBytesRead;
  308. }
  309. // ------------------------------------------------------------------------------------- //
  310. // CProcess implementation.
  311. // ------------------------------------------------------------------------------------- //
  312. CProcess::CProcess( CProcessUtils *pProcessUtils, const ProcessInfo_t &info )
  313. {
  314. m_pProcessUtils = pProcessUtils;
  315. m_Info = info;
  316. m_StdoutRead.m_pProcess = this;
  317. m_StdoutRead.m_hRead = info.m_hChildStdoutRd;
  318. m_StderrRead.m_pProcess = this;
  319. m_StderrRead.m_hRead = info.m_hChildStderrRd;
  320. }
  321. void CProcess::Release()
  322. {
  323. if ( !( m_Info.m_fFlags & STARTPROCESS_NOAUTOKILL ) )
  324. {
  325. Abort();
  326. }
  327. ProcessInfo_t& info = m_Info;
  328. CloseHandle( info.m_hChildStderrRd );
  329. CloseHandle( info.m_hChildStderrWr );
  330. CloseHandle( info.m_hChildStdinRd );
  331. CloseHandle( info.m_hChildStdinWr );
  332. CloseHandle( info.m_hChildStdoutRd );
  333. CloseHandle( info.m_hChildStdoutWr );
  334. m_pProcessUtils->OnProcessDelete( this );
  335. delete this;
  336. }
  337. void CProcess::Abort()
  338. {
  339. if ( !IsComplete() )
  340. {
  341. TerminateProcess( m_Info.m_hProcess, 1 );
  342. }
  343. }
  344. bool CProcess::IsComplete()
  345. {
  346. HANDLE h = m_Info.m_hProcess;
  347. return ( WaitForSingleObject( h, 0 ) != WAIT_TIMEOUT );
  348. }
  349. int CProcess::WaitUntilComplete()
  350. {
  351. ProcessInfo_t &info = m_Info;
  352. if ( info.m_hChildStdoutRd == INVALID_HANDLE_VALUE )
  353. {
  354. WaitForSingleObject( info.m_hProcess, INFINITE );
  355. }
  356. else
  357. {
  358. // NOTE: The called process can block during writes to stderr + stdout
  359. // if the pipe buffer is empty. Therefore, waiting INFINITE is not
  360. // possible here. We must queue up messages received to allow the
  361. // process to continue
  362. while ( WaitForSingleObject( info.m_hProcess, 50 ) == WAIT_TIMEOUT )
  363. {
  364. m_StdoutRead.CacheOutput();
  365. if ( m_StderrRead.IsValid() )
  366. m_StderrRead.CacheOutput();
  367. }
  368. }
  369. return GetExitCode();
  370. }
  371. int CProcess::WriteStdin( char *pBuf, int nBufLen )
  372. {
  373. ProcessInfo_t& info = m_Info;
  374. if ( info.m_hChildStdinWr == INVALID_HANDLE_VALUE )
  375. {
  376. Assert( false );
  377. return 0;
  378. }
  379. DWORD nBytesWritten = 0;
  380. if ( WriteFile( info.m_hChildStdinWr, pBuf, nBufLen, &nBytesWritten, NULL ) )
  381. return (int)nBytesWritten;
  382. else
  383. return 0;
  384. }
  385. IPipeRead* CProcess::GetStdout()
  386. {
  387. return &m_StdoutRead;
  388. }
  389. IPipeRead* CProcess::GetStderr()
  390. {
  391. return &m_StderrRead;
  392. }
  393. int CProcess::GetExitCode()
  394. {
  395. ProcessInfo_t &info = m_Info;
  396. DWORD nExitCode;
  397. BOOL bOk = GetExitCodeProcess( info.m_hProcess, &nExitCode );
  398. if ( !bOk || nExitCode == STILL_ACTIVE )
  399. return -1;
  400. return nExitCode;
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Initialize, shutdown process system
  404. //-----------------------------------------------------------------------------
  405. InitReturnVal_t CProcessUtils::Init()
  406. {
  407. InitReturnVal_t nRetVal = BaseClass::Init();
  408. if ( nRetVal != INIT_OK )
  409. return nRetVal;
  410. m_bInitialized = true;
  411. return INIT_OK;
  412. }
  413. void CProcessUtils::Shutdown()
  414. {
  415. Assert( m_bInitialized );
  416. Assert( m_Processes.Count() == 0 );
  417. // Delete any lingering processes.
  418. while ( m_Processes.Count() > 0 )
  419. {
  420. m_Processes[ m_Processes.Head() ]->Release();
  421. }
  422. m_bInitialized = false;
  423. return BaseClass::Shutdown();
  424. }
  425. void CProcessUtils::OnProcessDelete( CProcess *pProcess )
  426. {
  427. m_Processes.Remove( pProcess->m_nProcessesIndex );
  428. }
  429. CProcess *CProcessUtils::CreateProcess( ProcessInfo_t &info, int fFlags, const char *pWorkingDir )
  430. {
  431. STARTUPINFO si;
  432. memset(&si, 0, sizeof si);
  433. si.cb = sizeof(si);
  434. if ( fFlags & STARTPROCESS_CONNECTSTDPIPES )
  435. {
  436. si.dwFlags = STARTF_USESTDHANDLES;
  437. si.hStdInput = info.m_hChildStdinRd;
  438. si.hStdError = info.m_hChildStderrWr;
  439. si.hStdOutput = info.m_hChildStdoutWr;
  440. }
  441. DWORD dwCreateProcessFlags = 0;
  442. if ( !( fFlags & STARTPROCESS_SHARE_CONSOLE ) )
  443. {
  444. dwCreateProcessFlags |= DETACHED_PROCESS;
  445. }
  446. PROCESS_INFORMATION pi;
  447. if ( ::CreateProcess( NULL, info.m_CommandLine.Get(), NULL, NULL, TRUE, dwCreateProcessFlags, NULL, pWorkingDir, &si, &pi ) )
  448. {
  449. info.m_hProcess = pi.hProcess;
  450. CProcess *pProcess = new CProcess( this, info );
  451. pProcess->m_nProcessesIndex = m_Processes.AddToTail( pProcess );
  452. return pProcess;
  453. }
  454. char buf[ 512 ];
  455. Warning( "Could not execute the command:\n %s\n"
  456. "Windows gave the error message:\n \"%s\"\n",
  457. info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
  458. return NULL;
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Options for compilation
  462. //-----------------------------------------------------------------------------
  463. IProcess* CProcessUtils::StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir )
  464. {
  465. Assert( m_bInitialized );
  466. ProcessInfo_t info;
  467. info.m_CommandLine = pCommandLine;
  468. if ( !(fFlags & STARTPROCESS_CONNECTSTDPIPES) )
  469. {
  470. info.m_hChildStderrRd = info.m_hChildStderrWr = INVALID_HANDLE_VALUE;
  471. info.m_hChildStdinRd = info.m_hChildStdinWr = INVALID_HANDLE_VALUE;
  472. info.m_hChildStdoutRd = info.m_hChildStdoutWr = INVALID_HANDLE_VALUE;
  473. return CreateProcess( info, fFlags, pWorkingDir );
  474. }
  475. SECURITY_ATTRIBUTES saAttr;
  476. // Set the bInheritHandle flag so pipe handles are inherited.
  477. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  478. saAttr.bInheritHandle = TRUE;
  479. saAttr.lpSecurityDescriptor = NULL;
  480. DWORD nPipeSize = 0; // default size
  481. if ( fFlags & STARTPROCESS_FATPIPES )
  482. {
  483. nPipeSize = 1024*1024;
  484. }
  485. // Create a pipe for the child's STDOUT.
  486. if ( CreatePipe( &info.m_hChildStdoutRd, &info.m_hChildStdoutWr, &saAttr, nPipeSize ) )
  487. {
  488. if ( CreatePipe( &info.m_hChildStdinRd, &info.m_hChildStdinWr, &saAttr, nPipeSize ) )
  489. {
  490. BOOL bSuccess = false;
  491. if ( fFlags & STARTPROCESS_SEPARATE_STDERR )
  492. {
  493. bSuccess = CreatePipe( &info.m_hChildStderrRd, &info.m_hChildStderrWr, &saAttr, nPipeSize );
  494. }
  495. else
  496. {
  497. bSuccess = DuplicateHandle( GetCurrentProcess(), info.m_hChildStdoutWr, GetCurrentProcess(),
  498. &info.m_hChildStderrWr, 0, TRUE, DUPLICATE_SAME_ACCESS );
  499. info.m_hChildStderrRd = INVALID_HANDLE_VALUE;
  500. }
  501. if ( bSuccess )
  502. {
  503. IProcess *pProcess = CreateProcess( info, fFlags, pWorkingDir );
  504. if ( pProcess )
  505. return pProcess;
  506. CloseHandle( info.m_hChildStderrRd );
  507. CloseHandle( info.m_hChildStderrWr );
  508. }
  509. CloseHandle( info.m_hChildStdinRd );
  510. CloseHandle( info.m_hChildStdinWr );
  511. }
  512. CloseHandle( info.m_hChildStdoutRd );
  513. CloseHandle( info.m_hChildStdoutWr );
  514. }
  515. return NULL;
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Start up a process
  519. //-----------------------------------------------------------------------------
  520. IProcess* CProcessUtils::StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir )
  521. {
  522. CUtlString commandLine;
  523. for ( int i = 0; i < argc; ++i )
  524. {
  525. commandLine += argv[i];
  526. if ( i != argc-1 )
  527. {
  528. commandLine += " ";
  529. }
  530. }
  531. return StartProcess( commandLine.Get(), fFlags, pWorkingDir );
  532. }
  533. int CProcessUtils::SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir, CUtlString *pStdout )
  534. {
  535. int nFlags = 0;
  536. if ( pStdout )
  537. nFlags |= STARTPROCESS_CONNECTSTDPIPES;
  538. IProcess *pProcess = StartProcess( pCommandLine, nFlags, pWorkingDir );
  539. if ( !pProcess )
  540. return -1;
  541. int nExitCode = pProcess->WaitUntilComplete();
  542. if ( pStdout )
  543. {
  544. pProcess->GetStdout()->Read( *pStdout );
  545. }
  546. pProcess->Release();
  547. return nExitCode;
  548. }
  549. // Translate '\r\n' to '\n'.
  550. void TranslateLinefeedsToUnix( char *pBuffer )
  551. {
  552. char *pOut = pBuffer;
  553. while ( *pBuffer )
  554. {
  555. if ( pBuffer[0] == '\r' && pBuffer[1] == '\n' )
  556. {
  557. *pOut = '\n';
  558. ++pBuffer;
  559. }
  560. else
  561. {
  562. *pOut = *pBuffer;
  563. }
  564. ++pBuffer;
  565. ++pOut;
  566. }
  567. *pOut = 0;
  568. }