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.

845 lines
20 KiB

  1. //===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #ifdef _LINUX
  8. // linux has a multi-processing forked server mode.
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <sys/poll.h>
  12. #include <sys/socket.h>
  13. #include <sys/wait.h>
  14. #include <syscall.h>
  15. #include <unistd.h>
  16. #include <arpa/inet.h>
  17. //#include <linux/tcp.h>
  18. #include <netdb.h>
  19. //#include <sys/param.h>
  20. #include <sys/uio.h>
  21. #include <errno.h>
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #include "isys.h"
  25. #include "dedicated.h"
  26. #include "engine_hlds_api.h"
  27. #include "filesystem.h"
  28. #include "tier0/dbg.h"
  29. #include "tier1/strtools.h"
  30. #include "tier0/icommandline.h"
  31. #include "tier2/socketcreator.h"
  32. #include "idedicatedexports.h"
  33. #include <sys/types.h>
  34. #include <sys/socket.h>
  35. #include <netinet/in.h>
  36. #include <sys/ioctl.h>
  37. #include "mathlib/expressioncalculator.h"
  38. #define closesocket close
  39. #define WSAGetLastError() errno
  40. #define ioctlsocket ioctl
  41. // memdbgon must be the last include file in a .cpp file!!!
  42. #include "tier0/memdbgon.h"
  43. static netadr_t net_local_adr;
  44. unsigned short NET_HostToNetShort( unsigned short us_in )
  45. {
  46. return htons( us_in );
  47. }
  48. unsigned short NET_NetToHostShort( unsigned short us_in )
  49. {
  50. return ntohs( us_in );
  51. }
  52. //-----------------------------------------------------------------------------
  53. // Purpose:
  54. // Input : *s -
  55. // *sadr -
  56. // Output : bool NET_StringToSockaddr
  57. //-----------------------------------------------------------------------------
  58. bool NET_StringToSockaddr( const char *s, struct sockaddr *sadr )
  59. {
  60. char *colon;
  61. char copy[128];
  62. Q_memset (sadr, 0, sizeof(*sadr));
  63. ((struct sockaddr_in *)sadr)->sin_family = AF_INET;
  64. ((struct sockaddr_in *)sadr)->sin_port = 0;
  65. Q_strncpy (copy, s, sizeof( copy ) );
  66. // strip off a trailing :port if present
  67. for (colon = copy ; *colon ; colon++)
  68. {
  69. if (*colon == ':')
  70. {
  71. *colon = 0;
  72. ((struct sockaddr_in *)sadr)->sin_port = NET_HostToNetShort((short)atoi(colon+1));
  73. }
  74. }
  75. if (copy[0] >= '0' && copy[0] <= '9' && Q_strstr( copy, "." ) )
  76. {
  77. *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
  78. }
  79. else
  80. {
  81. struct hostent *h;
  82. if ( (h = gethostbyname(copy)) == NULL )
  83. return false;
  84. *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
  85. }
  86. return true;
  87. }
  88. /*
  89. =============
  90. NET_StringToAdr
  91. localhost
  92. idnewt
  93. idnewt:28000
  94. 192.246.40.70
  95. 192.246.40.70:28000
  96. =============
  97. */
  98. bool NET_StringToAdr ( const char *s, netadr_t *a)
  99. {
  100. struct sockaddr saddr;
  101. char address[128];
  102. Q_strncpy( address, s, sizeof(address) );
  103. if ( !Q_strncmp( address, "localhost", 10 ) || !Q_strncmp( address, "localhost:", 10 ) )
  104. {
  105. // subsitute 'localhost' with '127.0.0.1", both have 9 chars
  106. // this way we can resolve 'localhost' without DNS and still keep the port
  107. Q_memcpy( address, "127.0.0.1", 9 );
  108. }
  109. if ( !NET_StringToSockaddr (address, &saddr) )
  110. return false;
  111. a->SetFromSockadr( &saddr );
  112. return true;
  113. }
  114. void NET_GetLocalAddress (void)
  115. {
  116. net_local_adr.Clear();
  117. char buff[512];
  118. gethostname( buff, sizeof(buff) ); // get own IP address
  119. buff[sizeof(buff)-1] = 0; // Ensure that it doesn't overrun the buffer
  120. NET_StringToAdr (buff, &net_local_adr);
  121. }
  122. #define MAX_STATUS_STRING_LENGTH 1024
  123. #define MAX_INPUT_FROM_CHILD 2048
  124. class CConnectedNetConsoleData
  125. {
  126. public:
  127. int m_nCharsInCommandBuffer;
  128. char m_pszInputCommandBuffer[MAX_INPUT_FROM_CHILD];
  129. bool m_bAuthorized; // for password protection
  130. CConnectedNetConsoleData( void )
  131. {
  132. m_nCharsInCommandBuffer = 0;
  133. m_bAuthorized = false;
  134. }
  135. };
  136. class CParentProcessNetConsoleMgr : public ISocketCreatorListener
  137. {
  138. public:
  139. CSocketCreator m_Socket;
  140. netadr_t m_Address;
  141. char m_pPassword[256]; // if set
  142. bool m_bPasswordProtected;
  143. bool m_bActive;
  144. bool ShouldAcceptSocket( SocketHandle_t hSocket, const netadr_t &netAdr )
  145. {
  146. return true;
  147. }
  148. void OnSocketAccepted( SocketHandle_t hSocket, const netadr_t &netAdr, void** ppData )
  149. {
  150. CConnectedNetConsoleData *pData = new CConnectedNetConsoleData;
  151. if ( ! m_bPasswordProtected )
  152. pData->m_bAuthorized = true; // no password, auto-auth
  153. *ppData = pData;
  154. }
  155. void OnSocketClosed( SocketHandle_t hSocket, const netadr_t &netAdr, void* pData )
  156. {
  157. if ( pData )
  158. free( pData );
  159. }
  160. void RunFrame( void );
  161. CParentProcessNetConsoleMgr( void );
  162. void HandleInputChars( char const *pIn, int recvLen, CConnectedNetConsoleData *pData, int idx );
  163. void SendString( char const *pString, int idx = -1 ); // send a string to all sockets or just one
  164. void Execute( CConnectedNetConsoleData *pData, int idx );
  165. };
  166. void CParentProcessNetConsoleMgr::SendString( char const *pString, int nidx )
  167. {
  168. m_Socket.RunFrame();
  169. int nCount = m_Socket.GetAcceptedSocketCount();
  170. if ( nCount )
  171. {
  172. // lets add the lf to any cr's
  173. char *pTmp = (char * ) stackalloc( strlen( pString ) * 2 + 1 );
  174. char *oString = pTmp;
  175. char const *pIn = pString;
  176. while ( *pIn )
  177. {
  178. if ( *pIn == '\n' )
  179. *( oString++ ) = '\r';
  180. *( oString++ ) = *( pIn++ );
  181. }
  182. *( oString++ ) = 0;
  183. for ( int i = 0; i < nCount; i++ )
  184. {
  185. if ( ( nidx == -1 ) || ( i == nidx ) )
  186. {
  187. SocketHandle_t hSocket = m_Socket.GetAcceptedSocketHandle( i );
  188. //const netadr_t& socketAdr = m_Socket.GetAcceptedSocketAddress( i );
  189. CConnectedNetConsoleData *pData = ( CConnectedNetConsoleData * ) m_Socket.GetAcceptedSocketData( i );
  190. if ( pData->m_bAuthorized ) // no output to un-authed net consoles
  191. {
  192. send( hSocket, pTmp, oString - pTmp - 1, MSG_NOSIGNAL );
  193. }
  194. }
  195. }
  196. }
  197. }
  198. CParentProcessNetConsoleMgr::CParentProcessNetConsoleMgr( void ) : m_Socket( this )
  199. {
  200. m_bActive = false;
  201. m_bPasswordProtected = false;
  202. int nPassword = CommandLine()->FindParm( "-netconpassword" );
  203. if ( nPassword )
  204. {
  205. char const *pPassword = CommandLine()->GetParm( nPassword + 1 );
  206. V_strncpy( m_pPassword, pPassword, sizeof( m_pPassword ) );
  207. m_bPasswordProtected = true;
  208. }
  209. int nPort = CommandLine()->FindParm( "-netconport" );
  210. if ( nPort )
  211. {
  212. NET_GetLocalAddress();
  213. char const *pPortNum = CommandLine()->GetParm( nPort + 1 );
  214. char newBuf[256];
  215. V_strncpy( newBuf, pPortNum, sizeof( newBuf ) );
  216. char *pReplace = V_strstr( newBuf, "##" );
  217. if ( pReplace )
  218. {
  219. pReplace[0] = '0';
  220. pReplace[1] = '0';
  221. }
  222. m_Address = net_local_adr;
  223. int nPortNumber = EvaluateExpression( newBuf, -1 );
  224. if ( nPortNumber > 0 )
  225. {
  226. m_Address.SetPort( nPortNumber );
  227. m_bActive = true;
  228. m_Socket.CreateListenSocket( m_Address, true );
  229. }
  230. }
  231. }
  232. void CParentProcessNetConsoleMgr::RunFrame( void )
  233. {
  234. // check for incoming data
  235. if (! m_bActive )
  236. return;
  237. m_Socket.RunFrame();
  238. int nCount = m_Socket.GetAcceptedSocketCount();
  239. for ( int i = nCount - 1; i >= 0; i-- )
  240. {
  241. SocketHandle_t hSocket = m_Socket.GetAcceptedSocketHandle( i );
  242. // const netadr_t& socketAdr = m_Socket.GetAcceptedSocketAddress( i );
  243. CConnectedNetConsoleData *pData = ( CConnectedNetConsoleData * ) m_Socket.GetAcceptedSocketData( i );
  244. char ch;
  245. int pendingLen = recv( hSocket, &ch, sizeof(ch), MSG_PEEK );
  246. if ( pendingLen == -1 && SocketWouldBlock() )
  247. continue;
  248. if ( pendingLen <= 0 ) // eof or error
  249. {
  250. m_Socket.CloseAcceptedSocket( i );
  251. continue;
  252. }
  253. // find out how much we have to read
  254. unsigned long readLen;
  255. ioctlsocket( hSocket, FIONREAD, &readLen );
  256. while( readLen > 0 )
  257. {
  258. char recvBuf[256];
  259. int recvLen = recv( hSocket, recvBuf , MIN( sizeof( recvBuf ) , readLen ), 0 );
  260. if ( recvLen == 0 ) // socket was closed
  261. {
  262. m_Socket.CloseAcceptedSocket( i );
  263. break;
  264. }
  265. if ( recvLen < 0 && !SocketWouldBlock() )
  266. {
  267. break;
  268. }
  269. readLen -= recvLen;
  270. // now, lets write what we've got into the command buffer
  271. HandleInputChars( recvBuf, recvLen, pData, i );
  272. }
  273. }
  274. }
  275. void CParentProcessNetConsoleMgr::HandleInputChars( char const *pIn, int recvLen, CConnectedNetConsoleData *pData, int idx )
  276. {
  277. while( recvLen )
  278. {
  279. switch( *pIn )
  280. {
  281. case '\r':
  282. case '\n':
  283. {
  284. if ( pData->m_nCharsInCommandBuffer )
  285. {
  286. pData->m_pszInputCommandBuffer[pData->m_nCharsInCommandBuffer] = 0;
  287. Execute( pData, idx );
  288. }
  289. pData->m_nCharsInCommandBuffer = 0;
  290. break;
  291. }
  292. default:
  293. {
  294. if ( pData->m_nCharsInCommandBuffer < MAX_INPUT_FROM_CHILD - 1 )
  295. pData->m_pszInputCommandBuffer[pData->m_nCharsInCommandBuffer++] = *pIn;
  296. break;
  297. }
  298. }
  299. pIn++;
  300. recvLen--;
  301. }
  302. }
  303. struct CServerInstance
  304. {
  305. pid_t m_nPid;
  306. int m_nSocketToChild; // "our" side of the socket connection
  307. int m_nNumCharsInInputBuffer;
  308. int m_nNumPlayers;
  309. char m_pszStatus[MAX_STATUS_STRING_LENGTH];
  310. char m_pszMapName[MAX_PATH];
  311. char m_pszInputBuffer[MAX_INPUT_FROM_CHILD];
  312. bool m_bRunning;
  313. void ClearInputBuffer( void )
  314. {
  315. m_nNumCharsInInputBuffer = 0;
  316. }
  317. void ResetStatus( void )
  318. {
  319. m_pszMapName[0] = 0;
  320. m_nNumPlayers = 0;
  321. }
  322. CServerInstance( void )
  323. {
  324. m_pszStatus[0] = 0; // clear status string
  325. m_bRunning = false;
  326. m_nSocketToChild = -1;
  327. ClearInputBuffer();
  328. ResetStatus();
  329. }
  330. void HandleSocketInput( void );
  331. void ProcessInputFromChild( void );
  332. };
  333. #define MAX_CHILD_PROCESSSES 100
  334. CParentProcessNetConsoleMgr *g_pParentProcessNetConsole;
  335. int g_nNumChildInstances;
  336. CServerInstance *g_pChildProcesses;
  337. static bool s_bQuit = false;
  338. static bool s_bDelayedQuit = false;
  339. typedef void (*CMDFN)( char const *pArgs, int nIdx );
  340. struct CommandDescriptor
  341. {
  342. char const *m_pCmdName;
  343. CMDFN m_pCmdFn;
  344. char const *m_pCmdHelp;
  345. };
  346. static char *va( char *format, ... )
  347. {
  348. va_list argptr;
  349. static char string[8][512];
  350. static int curstring = 0;
  351. curstring = ( curstring + 1 ) % 8;
  352. va_start (argptr, format);
  353. Q_vsnprintf( string[curstring], sizeof( string[curstring] ), format, argptr );
  354. va_end (argptr);
  355. return string[curstring];
  356. }
  357. static void s_DoStatusCmd( char const *pArgs, int nConsoleIdx )
  358. {
  359. // print status
  360. g_pParentProcessNetConsole->SendString( "#status\n", nConsoleIdx );
  361. for( int i = 0; i < g_nNumChildInstances; i++ )
  362. {
  363. CServerInstance *pChild = g_pChildProcesses + i;
  364. g_pParentProcessNetConsole->SendString( va( "child %d\n", i ), nConsoleIdx );
  365. if ( pChild && ( pChild->m_nSocketToChild != -1 ) )
  366. {
  367. g_pParentProcessNetConsole->SendString( va( " pid : %d\n", i, pChild->m_nPid ), nConsoleIdx );
  368. g_pParentProcessNetConsole->SendString( va( " map : %s\n", pChild->m_pszMapName ), nConsoleIdx );
  369. g_pParentProcessNetConsole->SendString( va( " numplayers : %d\n", pChild->m_nNumPlayers ), nConsoleIdx );
  370. }
  371. }
  372. g_pParentProcessNetConsole->SendString( "#end\n", nConsoleIdx );
  373. }
  374. static void s_DoQuit( char const *pArgs, int nConsoleIdx )
  375. {
  376. g_pParentProcessNetConsole->SendString( "Killing all children and exiting\n", nConsoleIdx );
  377. for( int i = 0; i < g_nNumChildInstances; i++ )
  378. {
  379. CServerInstance *pChild = g_pChildProcesses + i;
  380. if ( pChild && ( pChild->m_nSocketToChild != -1 ) )
  381. {
  382. g_pParentProcessNetConsole->SendString( va( "killing child %d\n", i ), nConsoleIdx );
  383. kill( pChild->m_nPid, SIGKILL );
  384. }
  385. }
  386. s_bQuit = true;
  387. }
  388. static void s_DoBroadCastCmd( char const *pArgs, int nConsoleIdx )
  389. {
  390. if ( ! pArgs )
  391. {
  392. g_pParentProcessNetConsole->SendString( "Format of command is \"broadcast <concommand>\"\n" );
  393. }
  394. else
  395. {
  396. for( int i = 0; i < g_nNumChildInstances; i++ )
  397. {
  398. CServerInstance *pChild = g_pChildProcesses + i;
  399. if ( pChild && ( pChild->m_nSocketToChild != -1 ) )
  400. {
  401. send( pChild->m_nSocketToChild, pArgs, strlen( pArgs ), MSG_NOSIGNAL );
  402. send( pChild->m_nSocketToChild, "\n", 1, MSG_NOSIGNAL );
  403. }
  404. }
  405. }
  406. }
  407. static void s_DoShutdown( char const *pArgs, int nConsoleIdx )
  408. {
  409. s_bDelayedQuit = ! s_bDelayedQuit;
  410. if ( s_bDelayedQuit )
  411. {
  412. g_pParentProcessNetConsole->SendString( "Server will shutdown when all games are finished and children have exited.\n" );
  413. }
  414. else
  415. {
  416. g_pParentProcessNetConsole->SendString( "Server shutdown cancelled.\n" );
  417. }
  418. for( int i = 0; i < g_nNumChildInstances; i++ )
  419. {
  420. CServerInstance *pChild = g_pChildProcesses + i;
  421. if ( pChild && ( pChild->m_nSocketToChild != -1 ) )
  422. {
  423. if ( pChild->m_nNumPlayers == 0 )
  424. {
  425. kill( pChild->m_nPid, SIGKILL );
  426. }
  427. }
  428. }
  429. }
  430. static void s_DoFind( char const *pArgs, int nConsoleIdx );
  431. static CommandDescriptor s_CmdTable[]={
  432. { "status", s_DoStatusCmd, "List the status of all subprocesses." },
  433. { "broadcast", s_DoBroadCastCmd, "Send a command to all subprocesses." },
  434. { "find", s_DoFind, "find commands containing a string." },
  435. { "shutdown", s_DoShutdown, "Tell the server shutdown once all players have left. This is a toggle." },
  436. { "quit", s_DoQuit, "immediately shut down the server and all its child processes." },
  437. };
  438. static void s_DoFind( char const *pArgs, int nConsoleIdx )
  439. {
  440. for( int i = 0; i < ARRAYSIZE( s_CmdTable ); i++ )
  441. {
  442. if ( ( pArgs[0] == 0 ) || ( V_stristr( s_CmdTable[i].m_pCmdName, pArgs ) ) )
  443. {
  444. g_pParentProcessNetConsole->SendString( va( "%s:\t%s\n", s_CmdTable[i].m_pCmdName, s_CmdTable[i].m_pCmdHelp) , nConsoleIdx );
  445. }
  446. }
  447. }
  448. void CParentProcessNetConsoleMgr::Execute( CConnectedNetConsoleData *pData, int idx )
  449. {
  450. if ( memcmp( pData->m_pszInputCommandBuffer, "PASS ", 5 ) == 0 )
  451. {
  452. if ( V_strcmp( pData->m_pszInputCommandBuffer + 5, m_pPassword ) == 0 )
  453. {
  454. pData->m_bAuthorized = true;
  455. }
  456. else
  457. {
  458. // bad password
  459. Warning( "Bad password attempt from net console\n" );
  460. pData->m_bAuthorized = false;
  461. }
  462. }
  463. else
  464. {
  465. if ( pData->m_bAuthorized )
  466. {
  467. char const *pCmd = pData->m_pszInputCommandBuffer;
  468. pCmd += strspn( pCmd, " \t" );
  469. char const *pArgs = strchr( pCmd, ' ' );
  470. int nCmdLen;
  471. if ( pArgs )
  472. {
  473. nCmdLen = pArgs - pCmd;
  474. pArgs += strspn( pArgs, " \t" ); // skip to first char of first word
  475. }
  476. else
  477. {
  478. nCmdLen = strlen( pCmd );
  479. pArgs = pCmd + strlen( pCmd ); // point at trailing 0 bytes
  480. }
  481. for( int i = 0; i < ARRAYSIZE( s_CmdTable ); i++ )
  482. {
  483. char const *pTblCmd = s_CmdTable[i].m_pCmdName;
  484. if ( ( strlen( pTblCmd ) == nCmdLen ) &&
  485. ( memcmp( pTblCmd, pCmd, nCmdLen ) == 0 ) )
  486. {
  487. // found it
  488. ( *s_CmdTable[i].m_pCmdFn )( pArgs, idx );
  489. break;
  490. }
  491. }
  492. }
  493. else
  494. {
  495. SendString( "This server is password protected. Enter PASS <passwd> for access\n", idx );
  496. }
  497. }
  498. }
  499. static void HandleDeadChildProcesses( void )
  500. {
  501. for(;;)
  502. {
  503. int nStatus;
  504. pid_t nWait = waitpid( -1, &nStatus, WNOHANG );
  505. if ( nWait > 0 )
  506. {
  507. // find the process that exited
  508. CServerInstance *pFound = NULL;
  509. int nFound = -1;
  510. for( int i = 0; i < g_nNumChildInstances; i++ )
  511. {
  512. if ( g_pChildProcesses[i].m_nPid == nWait )
  513. {
  514. pFound = g_pChildProcesses + i;
  515. nFound = i;
  516. break;
  517. }
  518. }
  519. if ( ! pFound )
  520. {
  521. Warning( "unknown child process %d exited?\n", nWait );
  522. }
  523. else
  524. {
  525. if ( WIFEXITED( nStatus ) )
  526. {
  527. Msg( "Child %d exited with status %d\n", nFound, WEXITSTATUS( nStatus ) );
  528. }
  529. if ( WIFSIGNALED( nStatus ) )
  530. {
  531. Msg( "Child %d aborted with signal %d\n", nFound, WTERMSIG( nStatus ) );
  532. }
  533. if ( WCOREDUMP( nStatus ) )
  534. {
  535. Msg( "Child wrote a core dump\n");
  536. }
  537. pFound->m_bRunning = false;
  538. if ( pFound->m_nSocketToChild != -1 )
  539. {
  540. close( pFound->m_nSocketToChild );
  541. pFound->m_nSocketToChild = -1;
  542. }
  543. }
  544. }
  545. else
  546. {
  547. break; // no dead children
  548. }
  549. }
  550. }
  551. #define MAX_ACTIVE_PARENT_NETCONSOLE_SOCKETS 20
  552. static void ServiceChildProcesses( void )
  553. {
  554. // for any children that aren't running (or not running yet), start them
  555. pollfd pollFds[MAX_CHILD_PROCESSSES + 1 + MAX_ACTIVE_PARENT_NETCONSOLE_SOCKETS ];
  556. int nPoll = 0;
  557. int nNumRunning = 0;
  558. for( int i = 0; i < g_nNumChildInstances; i++ )
  559. {
  560. if ( g_pChildProcesses[i].m_bRunning == false )
  561. {
  562. if (! s_bDelayedQuit )
  563. {
  564. int nSockets[2];
  565. int nRslt = socketpair( AF_UNIX, SOCK_STREAM, 0, nSockets );
  566. if ( nRslt != 0 )
  567. {
  568. Error( "socket pair returned error errno = %d\n", errno );
  569. }
  570. pid_t nChild = fork();
  571. if ( nChild == 0 ) // are we the forked child?
  572. {
  573. //ResetBaseTime(); // start plat_float time over at 0 for precision
  574. PerformCommandLineSubstitutions( i + 1 );
  575. close( nSockets[1] );
  576. engine->SetSubProcessID( i + 1, nSockets[0] );
  577. g_nSubProcessId = i + 1;
  578. RunServer( true );
  579. syscall( SYS_exit, 0 ); // we are not going to perform a normal c++ exit. We _dont_ want to run destructors, etc.
  580. }
  581. else
  582. {
  583. g_pChildProcesses[i].m_nPid = nChild;
  584. g_pChildProcesses[i].m_pszStatus[0] = 0;
  585. g_pChildProcesses[i].m_bRunning = true;
  586. close( nSockets[0] );
  587. g_pChildProcesses[i].m_nSocketToChild = nSockets[1];
  588. }
  589. }
  590. }
  591. else
  592. {
  593. nNumRunning++;
  594. }
  595. if ( g_pChildProcesses[i].m_nSocketToChild != -1 )
  596. {
  597. pollFds[nPoll].fd = g_pChildProcesses[i].m_nSocketToChild;
  598. pollFds[nPoll].events = POLLIN | POLLERR | POLLHUP;
  599. pollFds[nPoll].revents = 0;
  600. nPoll++;
  601. }
  602. }
  603. if ( s_bDelayedQuit && ( nNumRunning == 0 ) )
  604. {
  605. _exit( 0 );
  606. }
  607. // now, wait for activity on any of our sockets or stdin
  608. // pollFds[nPoll].fd = STDIN_FILENO;
  609. // pollFds[nPoll].events = POLLIN;
  610. // pollFds[nPoll].revents = 0;
  611. // nPoll++;
  612. if ( g_pParentProcessNetConsole && ( g_pParentProcessNetConsole->m_bActive ) )
  613. {
  614. pollFds[nPoll].fd = g_pParentProcessNetConsole->m_Socket.m_hListenSocket;
  615. pollFds[nPoll].events = POLLIN;
  616. pollFds[nPoll].revents = 0;
  617. nPoll++;
  618. int nCount = g_pParentProcessNetConsole->m_Socket.GetAcceptedSocketCount();
  619. for( int i = 0; ( i < nCount ) && ( nPoll < ARRAYSIZE( pollFds ) ); i++ )
  620. {
  621. SocketHandle_t hSocket = g_pParentProcessNetConsole->m_Socket.GetAcceptedSocketHandle( i );
  622. pollFds[nPoll].fd = hSocket;
  623. pollFds[nPoll].events = POLLIN;
  624. pollFds[nPoll].revents = 0;
  625. nPoll++;
  626. }
  627. }
  628. int nPollResult = poll( pollFds, nPoll, 10 * 1000 ); // wait up to 10 seconds. Could wait forever, really
  629. // check for activity on the sockets from our children
  630. int np = 0;
  631. for( int i = 0; i < g_nNumChildInstances; i++ )
  632. {
  633. if ( g_pChildProcesses[i].m_nSocketToChild != -1 )
  634. {
  635. if ( pollFds[np].revents & POLLIN ) // data ready to read?
  636. {
  637. g_pChildProcesses[i].HandleSocketInput();
  638. }
  639. np++;
  640. }
  641. }
  642. // see if any children have exited
  643. HandleDeadChildProcesses();
  644. g_pParentProcessNetConsole->RunFrame();
  645. }
  646. void RunServerSubProcesses( int nNumChildren )
  647. {
  648. g_nNumChildInstances = nNumChildren;
  649. g_pChildProcesses = new CServerInstance[g_nNumChildInstances];
  650. g_pParentProcessNetConsole = new CParentProcessNetConsoleMgr;
  651. while( ! s_bQuit )
  652. {
  653. ServiceChildProcesses();
  654. }
  655. _exit( 0 );
  656. }
  657. static bool DecodeParam( char const *pParamName, char const *pInput, char const **pOutPtr )
  658. {
  659. // if the left of the string matches pParamName, return the right of the string else return null
  660. int nPLen = strlen( pParamName );
  661. if ( memcmp( pParamName, pInput, nPLen ) == 0 )
  662. {
  663. *pOutPtr= pInput + nPLen;
  664. }
  665. else
  666. {
  667. *pOutPtr = NULL;
  668. }
  669. return ( *pOutPtr );
  670. }
  671. void CServerInstance::ProcessInputFromChild( void )
  672. {
  673. if ( m_pszInputBuffer[0] == '#' ) // spew?
  674. {
  675. puts( m_pszInputBuffer );
  676. }
  677. else
  678. {
  679. char *pSpace = strchr( m_pszInputBuffer, ' ' );
  680. if ( pSpace )
  681. {
  682. *( pSpace++ ) = 0;
  683. pSpace += strspn( pSpace, " \t" );
  684. }
  685. else
  686. {
  687. pSpace = m_pszInputBuffer + strlen( m_pszInputBuffer );
  688. }
  689. if ( !strcmp( m_pszInputBuffer, "status" ) )
  690. {
  691. CUtlStringList statusRecords;
  692. V_SplitString( pSpace, ";", statusRecords );
  693. for( int i = 0; i < statusRecords.Count(); i++ )
  694. {
  695. char const *pRecord = statusRecords[i];
  696. char const *pParm;
  697. if ( DecodeParam( "map=", pRecord, &pParm ) )
  698. {
  699. V_strncpy( m_pszMapName, pParm, sizeof( m_pszMapName ) );
  700. }
  701. else if ( DecodeParam( "players=", pRecord, &pParm ) )
  702. {
  703. m_nNumPlayers = atoi( pParm );
  704. }
  705. }
  706. }
  707. else
  708. {
  709. Warning("got unknown cmd %s args %s\n", m_pszInputBuffer, pSpace );
  710. }
  711. }
  712. }
  713. void CServerInstance::HandleSocketInput( void )
  714. {
  715. char *pDest = m_pszInputBuffer + m_nNumCharsInInputBuffer;
  716. int nRead = recv( m_nSocketToChild, pDest, sizeof( m_pszInputBuffer ) - m_nNumCharsInInputBuffer, MSG_DONTWAIT );
  717. if ( nRead > 0 )
  718. {
  719. m_nNumCharsInInputBuffer += nRead;
  720. if ( m_pszInputBuffer[m_nNumCharsInInputBuffer - 1] == 0 )
  721. {
  722. ProcessInputFromChild();
  723. m_nNumCharsInInputBuffer = 0;
  724. }
  725. if ( m_nNumCharsInInputBuffer == MAX_INPUT_FROM_CHILD )
  726. m_nNumCharsInInputBuffer = 0;
  727. }
  728. }
  729. #endif //linux