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.

833 lines
19 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Header: $
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #include "server_pch.h"
  9. #include <time.h>
  10. #include "server.h"
  11. #include "sv_log.h"
  12. #include "filesystem.h"
  13. #include "filesystem_engine.h"
  14. #include "sv_main.h"
  15. #include "tier0/icommandline.h"
  16. #include <proto_oob.h>
  17. #include "GameEventManager.h"
  18. #include "netadr.h"
  19. #include "sv_steamauth.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. static ConVar sv_logsdir( "sv_logsdir", "logs", FCVAR_ARCHIVE, "Folder in the game directory where server logs will be stored." );
  23. static ConVar sv_logfile( "sv_logfile", "1", FCVAR_ARCHIVE, "Log server information in the log file." );
  24. static ConVar sv_logflush( "sv_logflush", "0", FCVAR_ARCHIVE, "Flush the log file to disk on each write (slow)." );
  25. static ConVar sv_logecho( "sv_logecho", "1", FCVAR_ARCHIVE, "Echo log information to the console." );
  26. static ConVar sv_log_onefile( "sv_log_onefile", "0", FCVAR_ARCHIVE, "Log server information to only one file." );
  27. static ConVar sv_logbans( "sv_logbans", "0", FCVAR_ARCHIVE, "Log server bans in the server logs." ); // should sv_banid() calls be logged in the server logs?
  28. static ConVar sv_logsecret( "sv_logsecret", "0", FCVAR_RELEASE, "If set then include this secret when doing UDP logging (will use 0x53 as packet type, not usual 0x52)" );
  29. static ConVar sv_logsocket( "sv_logsocket", ( new CFmtStr( "%d", NS_SERVER ) )->Access(), FCVAR_RELEASE, "Uses a specific outgoing socket for sv udp logging" );
  30. static ConVar sv_logsocket2( "sv_logsocket2", "1", FCVAR_RELEASE, "Uses a specific outgoing socket for second source of sv udp logging" );
  31. static ConVar sv_logsocket2_substr( "sv_logsocket2_substr", "", FCVAR_RELEASE, "Uses a substring match for second source of sv udp logging" );
  32. CLog g_Log; // global Log object
  33. CON_COMMAND( log, "Enables logging to file, console, and udp < on | off >." )
  34. {
  35. if ( args.ArgC() != 2 )
  36. {
  37. ConMsg( "Usage: log < on | off >\n" );
  38. if ( g_Log.IsActive() )
  39. {
  40. bool bHaveFirst = false;
  41. ConMsg( "currently logging to: " );
  42. if ( sv_logfile.GetInt() )
  43. {
  44. ConMsg( "file" );
  45. bHaveFirst = true;
  46. }
  47. if ( sv_logecho.GetInt() )
  48. {
  49. if ( bHaveFirst )
  50. {
  51. ConMsg( ", console" );
  52. }
  53. else
  54. {
  55. ConMsg( "console" );
  56. bHaveFirst = true;
  57. }
  58. }
  59. if ( g_Log.UsingLogAddress() )
  60. {
  61. if ( bHaveFirst )
  62. {
  63. ConMsg( ", udp" );
  64. }
  65. else
  66. {
  67. ConMsg( "udp" );
  68. bHaveFirst = true;
  69. }
  70. }
  71. if ( !bHaveFirst )
  72. {
  73. ConMsg( "no destinations! (file, console, or udp)\n" );
  74. ConMsg( "check \"sv_logfile\", \"sv_logecho\", and \"logaddress_list\"" );
  75. }
  76. ConMsg( "\n" );
  77. }
  78. else
  79. {
  80. ConMsg( "not currently logging\n" );
  81. }
  82. return;
  83. }
  84. if ( !Q_stricmp( args[1], "off" ) || !Q_stricmp( args[1], "0" ) )
  85. {
  86. if ( g_Log.IsActive() )
  87. {
  88. g_Log.Close();
  89. g_Log.SetLoggingState( false );
  90. ConMsg( "Server logging disabled.\n" );
  91. }
  92. }
  93. else if ( !Q_stricmp( args[1], "on" ) || !Q_stricmp( args[1], "1" ) )
  94. {
  95. g_Log.SetLoggingState( true );
  96. ConMsg( "Server logging enabled.\n" );
  97. g_Log.Open();
  98. }
  99. else
  100. {
  101. ConMsg( "log: unknown parameter %s, 'on' and 'off' are valid\n", args[1] );
  102. }
  103. }
  104. // changed log_addaddress back to logaddress_add to be consistent with GoldSrc
  105. static void helper_logaddress_add( const CCommand &args, char const *szCommand, uint64 ullToken )
  106. {
  107. netadr_t adr;
  108. const char *pszIP, *pszPort;
  109. if ( args.ArgC() != 4 && args.ArgC() != 2 )
  110. {
  111. ConMsg( "Usage: %s ip:port\n", szCommand );
  112. return;
  113. }
  114. pszIP = args[1];
  115. if ( args.ArgC() == 4 )
  116. {
  117. pszPort = args[3];
  118. }
  119. else
  120. {
  121. pszPort = Q_strstr( pszIP, ":" );
  122. // if we have "IP:port" as one argument inside quotes
  123. if ( pszPort )
  124. {
  125. // add one to remove the :
  126. pszPort++;
  127. }
  128. else
  129. {
  130. // default port
  131. pszPort = "27015";
  132. }
  133. }
  134. if ( !Q_atoi( pszPort ) )
  135. {
  136. ConMsg( "%s: must specify a valid port\n", szCommand );
  137. return;
  138. }
  139. if ( !pszIP || !pszIP[0] )
  140. {
  141. ConMsg( "%s: unparseable address\n", szCommand );
  142. return;
  143. }
  144. char szAdr[32];
  145. Q_snprintf( szAdr, sizeof( szAdr ), "%s:%s", pszIP, pszPort );
  146. if ( NET_StringToAdr( szAdr, &adr ) )
  147. {
  148. if ( g_Log.AddLogAddress( adr, ullToken ) )
  149. {
  150. if ( ullToken )
  151. ConMsg( "%s: %s [%016llX]\n", szCommand, adr.ToString(), ullToken );
  152. else
  153. ConMsg( "%s: %s\n", szCommand, adr.ToString() );
  154. }
  155. else
  156. {
  157. ConMsg( "%s: %s is already in the list\n", szCommand, adr.ToString() );
  158. }
  159. }
  160. else
  161. {
  162. ConMsg( "%s: unable to resolve %s\n", szCommand, szAdr );
  163. }
  164. }
  165. CON_COMMAND( logaddress_add, "Set address and port for remote host <ip:port>." )
  166. {
  167. helper_logaddress_add( args, "logaddress_add", 0ull );
  168. }
  169. CON_COMMAND( logaddress_add_ex, "Set address and port for remote host <ip:port> and supplies a unique token in the UDP packets." )
  170. {
  171. CRC64_t crcToken;
  172. CRC64_Init( &crcToken );
  173. const char *szCmdLine = CommandLine()->GetCmdLine();
  174. if ( szCmdLine && *szCmdLine )
  175. CRC64_ProcessBuffer( &crcToken, szCmdLine, V_strlen( szCmdLine ) );
  176. const char *szSteamToken = Steam3Server().GetAccountToken();
  177. if ( szSteamToken && *szSteamToken )
  178. CRC64_ProcessBuffer( &crcToken, szSteamToken, V_strlen( szSteamToken ) );
  179. const char *szLocalAdr = net_local_adr.ToString();
  180. if ( szLocalAdr && *szLocalAdr )
  181. CRC64_ProcessBuffer( &crcToken, szLocalAdr, V_strlen( szLocalAdr ) );
  182. CRC64_Final( &crcToken );
  183. if ( !crcToken )
  184. crcToken = 1;
  185. helper_logaddress_add( args, "logaddress_add_ex", crcToken );
  186. }
  187. static uint64 s_ullLogaddressTokenSecret = 0;
  188. void FnChangeCallback_logaddress_token_secret( IConVar *var, const char *pOldValue, float flOldValue )
  189. {
  190. ConVarRef cv( var );
  191. char const *szstring = cv.GetString();
  192. if ( !szstring || !*szstring )
  193. {
  194. ConMsg( "logaddress_token_secret: must use a non-empty string for checksum\n" );
  195. return;
  196. }
  197. CRC64_ProcessBuffer( &s_ullLogaddressTokenSecret, szstring, V_strlen( szstring ) );
  198. if ( !s_ullLogaddressTokenSecret )
  199. s_ullLogaddressTokenSecret = 1;
  200. ConMsg( "logaddress_token_secret: token checksum = %016llX\n", s_ullLogaddressTokenSecret );
  201. }
  202. ConVar logaddress_token_secret( "logaddress_token_secret", "", FCVAR_RELEASE, "Set a secret string that will be hashed when using logaddress with explicit token hash.", FnChangeCallback_logaddress_token_secret );
  203. CON_COMMAND( logaddress_add_ts, "Set address and port for remote host <ip:port> and uses a unique checksum from logaddress_token_secret in the UDP packets." )
  204. {
  205. if ( !s_ullLogaddressTokenSecret )
  206. {
  207. ConMsg( "logaddress_add_ts: must set logaddress_token_secret before adding this log address\n" );
  208. return;
  209. }
  210. helper_logaddress_add( args, "logaddress_add_ts", s_ullLogaddressTokenSecret );
  211. }
  212. CON_COMMAND( logaddress_delall, "Remove all udp addresses being logged to" )
  213. {
  214. g_Log.DelAllLogAddress();
  215. }
  216. CON_COMMAND( logaddress_del, "Remove address and port for remote host <ip:port>." )
  217. {
  218. netadr_t adr;
  219. const char *pszIP, *pszPort;
  220. if ( args.ArgC() != 4 && args.ArgC() != 2 )
  221. {
  222. ConMsg( "Usage: logaddress_del ip:port\n" );
  223. return;
  224. }
  225. pszIP = args[1];
  226. if ( args.ArgC() == 4 )
  227. {
  228. pszPort = args[3];
  229. }
  230. else
  231. {
  232. pszPort = Q_strstr( pszIP, ":" );
  233. // if we have "IP:port" as one argument inside quotes
  234. if ( pszPort )
  235. {
  236. // add one to remove the :
  237. pszPort++;
  238. }
  239. else
  240. {
  241. // default port
  242. pszPort = "27015";
  243. }
  244. }
  245. if ( !Q_atoi( pszPort ) )
  246. {
  247. ConMsg( "logaddress_del: must specify a valid port\n" );
  248. return;
  249. }
  250. if ( !pszIP || !pszIP[0] )
  251. {
  252. ConMsg( "logaddress_del: unparseable address\n" );
  253. return;
  254. }
  255. char szAdr[32];
  256. Q_snprintf( szAdr, sizeof( szAdr ), "%s:%s", pszIP, pszPort );
  257. if ( NET_StringToAdr( szAdr, &adr ) )
  258. {
  259. if ( g_Log.DelLogAddress( adr ) )
  260. {
  261. ConMsg( "logaddress_del: %s\n", adr.ToString() );
  262. }
  263. else
  264. {
  265. ConMsg( "logaddress_del: address %s not found in the list\n", adr.ToString() );
  266. }
  267. }
  268. else
  269. {
  270. ConMsg( "logaddress_del: unable to resolve %s\n", szAdr );
  271. }
  272. }
  273. CON_COMMAND( logaddress_list, "List all addresses currently being used by logaddress." )
  274. {
  275. g_Log.ListLogAddress();
  276. }
  277. CLog::CLog()
  278. {
  279. Reset();
  280. m_nDebugID = EVENT_DEBUG_ID_INIT;
  281. }
  282. CLog::~CLog()
  283. {
  284. m_nDebugID = EVENT_DEBUG_ID_SHUTDOWN;
  285. }
  286. void CLog::Reset( void ) // reset all logging streams
  287. {
  288. m_LogAddrDestinations.RemoveAll();
  289. m_hLogFile = FILESYSTEM_INVALID_HANDLE;
  290. m_bActive = false;
  291. m_flLastLogFlush = realtime;
  292. m_bFlushLog = false;
  293. if ( CommandLine()->CheckParm( "-flushlog" ) )
  294. {
  295. m_bFlushLog = true;
  296. }
  297. }
  298. void CLog::Init( void )
  299. {
  300. Reset();
  301. // listen to these events
  302. g_GameEventManager.AddListener( this, "server_spawn", true );
  303. g_GameEventManager.AddListener( this, "server_shutdown", true );
  304. g_GameEventManager.AddListener( this, "server_cvar", true );
  305. g_GameEventManager.AddListener( this, "server_message", true );
  306. g_GameEventManager.AddListener( this, "server_addban", true );
  307. g_GameEventManager.AddListener( this, "server_removeban", true );
  308. }
  309. void CLog::Shutdown()
  310. {
  311. Close();
  312. Reset();
  313. g_GameEventManager.RemoveListener( this );
  314. }
  315. void CLog::SetLoggingState( bool state )
  316. {
  317. m_bActive = state;
  318. }
  319. void CLog::RunFrame()
  320. {
  321. if ( m_bFlushLog && m_hLogFile != FILESYSTEM_INVALID_HANDLE && ( realtime - m_flLastLogFlush ) > 1.0f )
  322. {
  323. m_flLastLogFlush = realtime;
  324. g_pFileSystem->Flush( m_hLogFile );
  325. }
  326. }
  327. bool CLog::AddLogAddress( netadr_t addr, uint64 ullToken )
  328. {
  329. for ( int i = 0; i < m_LogAddrDestinations.Count(); ++i )
  330. {
  331. if ( m_LogAddrDestinations.Element(i).m_adr.CompareAdr(addr, false) )
  332. {
  333. if ( m_LogAddrDestinations.Element( i ).m_ullToken == ullToken )
  334. return false;
  335. m_LogAddrDestinations.Element( i ).m_ullToken = ullToken;
  336. return true;
  337. }
  338. }
  339. LogAddressDestination_t dest = { addr, ullToken };
  340. m_LogAddrDestinations.AddToTail( dest );
  341. return true;
  342. }
  343. bool CLog::DelLogAddress(netadr_t addr)
  344. {
  345. int i = 0;
  346. for ( i = 0; i < m_LogAddrDestinations.Count(); ++i )
  347. {
  348. if ( m_LogAddrDestinations.Element(i).m_adr.CompareAdr(addr, false) )
  349. {
  350. // found!
  351. break;
  352. }
  353. }
  354. if ( i < m_LogAddrDestinations.Count() )
  355. {
  356. m_LogAddrDestinations.Remove(i);
  357. return true;
  358. }
  359. return false;
  360. }
  361. void CLog::ListLogAddress( void )
  362. {
  363. LogAddressDestination_t *pElement;
  364. int count = m_LogAddrDestinations.Count();
  365. if ( count <= 0 )
  366. {
  367. ConMsg( "logaddress_list: no addresses in the list\n" );
  368. }
  369. else
  370. {
  371. if ( count == 1 )
  372. {
  373. ConMsg( "logaddress_list: %i entry\n", count );
  374. }
  375. else
  376. {
  377. ConMsg( "logaddress_list: %i entries\n", count );
  378. }
  379. for ( int i = 0 ; i < count ; ++i )
  380. {
  381. pElement = &m_LogAddrDestinations.Element(i);
  382. if ( pElement->m_ullToken )
  383. ConMsg( "%s [%016llX]\n", pElement->m_adr.ToString(), pElement->m_ullToken );
  384. else
  385. ConMsg( "%s\n", pElement->m_adr.ToString() );
  386. }
  387. }
  388. }
  389. bool CLog::UsingLogAddress( void )
  390. {
  391. return ( m_LogAddrDestinations.Count() > 0 );
  392. }
  393. void CLog::DelAllLogAddress( void )
  394. {
  395. if ( m_LogAddrDestinations.Count() > 0 )
  396. {
  397. ConMsg( "logaddress_delall: all addresses cleared\n" );
  398. m_LogAddrDestinations.RemoveAll();
  399. }
  400. else
  401. {
  402. ConMsg( "logaddress_delall: no addresses in the list\n" );
  403. }
  404. }
  405. /*
  406. ==================
  407. Log_PrintServerVars
  408. ==================
  409. */
  410. void CLog::PrintServerVars( void )
  411. {
  412. if ( !IsActive() )
  413. {
  414. return;
  415. }
  416. Printf( "server cvars start\n" );
  417. // Loop through cvars...
  418. ICvar::Iterator iter( g_pCVar );
  419. for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() )
  420. {
  421. ConCommandBase *var = iter.Get();
  422. if ( var->IsCommand() )
  423. continue;
  424. if ( !( var->IsFlagSet( FCVAR_NOTIFY ) ) )
  425. continue;
  426. Printf( "\"%s\" = \"%s\"\n", var->GetName(), ((ConVar*)var)->GetString() );
  427. }
  428. Printf( "server cvars end\n" );
  429. }
  430. bool CLog::IsActive( void )
  431. {
  432. return m_bActive;
  433. }
  434. /*
  435. ==================
  436. Log_Printf
  437. Prints a log message to the server's log file, console, and possible a UDP address
  438. ==================
  439. */
  440. void CLog::Printf( const char *fmt, ... )
  441. {
  442. va_list argptr;
  443. static char string[1024];
  444. if ( !IsActive() )
  445. {
  446. return;
  447. }
  448. va_start ( argptr, fmt );
  449. Q_vsnprintf ( string, sizeof( string ), fmt, argptr );
  450. va_end ( argptr );
  451. Print( string );
  452. }
  453. void CLog::Print( const char * text )
  454. {
  455. static char string[1100];
  456. if ( !IsActive() || !text || !text[0] )
  457. {
  458. return;
  459. }
  460. if ( Q_strlen( text ) > 1024 )
  461. {
  462. DevMsg( 1, "CLog::Print: string too long (>1024 bytes)." );
  463. return;
  464. }
  465. serverGameDLL->LogForHTTPListeners( text );
  466. tm today;
  467. Plat_GetLocalTime( &today );
  468. Q_snprintf( string, sizeof( string ), "L %02i/%02i/%04i - %02i:%02i:%02i: %s",
  469. today.tm_mon+1, today.tm_mday, 1900 + today.tm_year,
  470. today.tm_hour, today.tm_min, today.tm_sec, text );
  471. // Echo to server console
  472. if ( sv_logecho.GetInt() )
  473. {
  474. ConMsg( "%s", string );
  475. }
  476. // Echo to log file
  477. if ( sv_logfile.GetInt() && ( m_hLogFile != FILESYSTEM_INVALID_HANDLE ) )
  478. {
  479. g_pFileSystem->FPrintf( m_hLogFile, "%s", string );
  480. if ( sv_logflush.GetBool() )
  481. {
  482. g_pFileSystem->Flush( m_hLogFile );
  483. }
  484. }
  485. // Echo to UDP port
  486. if ( m_LogAddrDestinations.Count() > 0 )
  487. {
  488. // out of band sending
  489. for ( int i = 0 ; i < m_LogAddrDestinations.Count() ; i++ )
  490. {
  491. int nSocketFrom = sv_logsocket.GetInt();
  492. if ( sv_logsocket2_substr.GetString()[0] )
  493. {
  494. // Check if we should use secondary outgoing socket?
  495. if ( V_strstr( m_LogAddrDestinations.Element( i ).m_adr.ToString(), sv_logsocket2_substr.GetString() ) )
  496. nSocketFrom = sv_logsocket2.GetInt();
  497. }
  498. char chTokenBuffer[64] = {};
  499. if ( m_LogAddrDestinations.Element( i ).m_ullToken )
  500. V_sprintf_safe( chTokenBuffer, "T%016llX ", m_LogAddrDestinations.Element( i ).m_ullToken );
  501. else
  502. chTokenBuffer[0] = 0;
  503. if ( sv_logsecret.GetInt() != 0 )
  504. NET_OutOfBandPrintf( nSocketFrom, m_LogAddrDestinations.Element(i).m_adr, "%c%s%s%s", S2A_LOGSTRING2, sv_logsecret.GetString(), chTokenBuffer, string );
  505. else
  506. NET_OutOfBandPrintf( nSocketFrom, m_LogAddrDestinations.Element(i).m_adr, "%c%s%s", S2A_LOGSTRING, chTokenBuffer, string );
  507. }
  508. }
  509. }
  510. void CLog::FireGameEvent( IGameEvent *event )
  511. {
  512. if ( !IsActive() )
  513. {
  514. return;
  515. }
  516. // log server events
  517. const char * name = event->GetName();
  518. if ( !name || !name[0])
  519. {
  520. return;
  521. }
  522. if ( Q_strcmp(name, "server_spawn") == 0 )
  523. {
  524. Printf( "Started map \"%s\" (CRC \"%i\")\n", sv.GetMapName(), ( int ) sv.worldmapCRC );
  525. }
  526. else if ( Q_strcmp(name, "server_shutdown") == 0 )
  527. {
  528. Printf( "server_message: \"%s\"\n", event->GetString("reason") );
  529. }
  530. else if ( Q_strcmp(name, "server_cvar") == 0 )
  531. {
  532. Printf( "server_cvar: \"%s\" \"%s\"\n", event->GetString("cvarname"), event->GetString("cvarvalue") );
  533. }
  534. else if ( Q_strcmp(name, "server_message") == 0 )
  535. {
  536. Printf( "server_message: \"%s\"\n", event->GetString("text") );
  537. }
  538. else if ( Q_strcmp(name, "server_addban") == 0 )
  539. {
  540. if ( sv_logbans.GetInt() > 0 )
  541. {
  542. const int userid = event->GetInt( "userid" );
  543. const char *pszName = event->GetString( "name" );
  544. const char *pszNetworkid = event->GetString( "networkid" );
  545. const char *pszIP = event->GetString( "ip" );
  546. const char *pszDuration = event->GetString( "duration" );
  547. const char *pszCmdGiver = event->GetString( "by" );
  548. const char *pszResult = NULL;
  549. if ( Q_strlen( pszIP ) > 0 )
  550. {
  551. pszResult = event->GetInt( "kicked" ) > 0 ? "was kicked and banned by IP" : "was banned by IP";
  552. if ( userid > 0 )
  553. {
  554. Printf( "Addip: \"%s<%i><%s><>\" %s \"%s\" by \"%s\" (IP \"%s\")\n",
  555. pszName,
  556. userid,
  557. pszNetworkid,
  558. pszResult,
  559. pszDuration,
  560. pszCmdGiver,
  561. pszIP );
  562. }
  563. else
  564. {
  565. Printf( "Addip: \"<><><>\" %s \"%s\" by \"%s\" (IP \"%s\")\n",
  566. pszResult,
  567. pszDuration,
  568. pszCmdGiver,
  569. pszIP );
  570. }
  571. }
  572. else
  573. {
  574. pszResult = event->GetInt( "kicked" ) > 0 ? "was kicked and banned" : "was banned";
  575. if ( userid > 0 )
  576. {
  577. Printf( "Banid: \"%s<%i><%s><>\" %s \"%s\" by \"%s\"\n",
  578. pszName,
  579. userid,
  580. pszNetworkid,
  581. pszResult,
  582. pszDuration,
  583. pszCmdGiver );
  584. }
  585. else
  586. {
  587. Printf( "Banid: \"<><%s><>\" %s \"%s\" by \"%s\"\n",
  588. pszNetworkid,
  589. pszResult,
  590. pszDuration,
  591. pszCmdGiver );
  592. }
  593. }
  594. }
  595. }
  596. else if ( Q_strcmp(name, "server_removeban") == 0 )
  597. {
  598. if ( sv_logbans.GetInt() > 0 )
  599. {
  600. const char *pszNetworkid = event->GetString( "networkid" );
  601. const char *pszIP = event->GetString( "ip" );
  602. const char *pszCmdGiver = event->GetString( "by" );
  603. if ( Q_strlen( pszIP ) > 0 )
  604. {
  605. Printf( "Removeip: \"<><><>\" was unbanned by \"%s\" (IP \"%s\")\n",
  606. pszCmdGiver,
  607. pszIP );
  608. }
  609. else
  610. {
  611. Printf( "Removeid: \"<><%s><>\" was unbanned by \"%s\"\n",
  612. pszNetworkid,
  613. pszCmdGiver );
  614. }
  615. }
  616. }
  617. }
  618. int CLog::GetEventDebugID( void )
  619. {
  620. return m_nDebugID;
  621. }
  622. /*
  623. ====================
  624. Log_Close
  625. Close logging file
  626. ====================
  627. */
  628. void CLog::Close( void )
  629. {
  630. if ( m_hLogFile != FILESYSTEM_INVALID_HANDLE )
  631. {
  632. Printf( "Log file closed\n" );
  633. g_pFileSystem->Close( m_hLogFile );
  634. }
  635. m_hLogFile = FILESYSTEM_INVALID_HANDLE;
  636. }
  637. /*
  638. ====================
  639. Log_Open
  640. Open logging file
  641. ====================
  642. */
  643. void CLog::Open( void )
  644. {
  645. char szFileBase[ MAX_OSPATH ];
  646. char szTestFile[ MAX_OSPATH ];
  647. int i;
  648. FileHandle_t fp = 0;
  649. if ( !m_bActive || !sv_logfile.GetInt() )
  650. {
  651. return;
  652. }
  653. // do we already have a log file (and we only want one)?
  654. if ( m_hLogFile && sv_log_onefile.GetInt() )
  655. {
  656. return;
  657. }
  658. Close();
  659. // Find a new log file slot
  660. tm today;
  661. Plat_GetLocalTime( &today );
  662. const char *pszLogsDir = sv_logsdir.GetString();
  663. // safety check for invalid paths
  664. if ( !COM_IsValidPath( pszLogsDir ) )
  665. {
  666. pszLogsDir = "logs";
  667. }
  668. // Build a filename that will remain unique even if all games everywhere piled in to the same directory
  669. ConVar *hostip = cvar->FindVar( "hostip" );
  670. int ip = hostip->GetInt();
  671. int ipparts[4];
  672. ipparts[0] = (ip >> 24) & 0xff;
  673. ipparts[1] = (ip >> 16) & 0xff;
  674. ipparts[2] = (ip >> 8) & 0xff;
  675. ipparts[3] = (ip) & 0xff;
  676. int port = NET_GetUDPPort( NS_SERVER );
  677. Q_snprintf( szFileBase, sizeof( szFileBase ), "%s/L%03i_%03i_%03i_%03i_%i_%04i%02i%02i%02i%02i_",
  678. pszLogsDir, ipparts[0], ipparts[1], ipparts[2], ipparts[3], port, today.tm_year + 1900, today.tm_mon + 1, today.tm_mday, today.tm_hour, today.tm_min );
  679. for ( i = 0; i < 1000; i++ )
  680. {
  681. Q_snprintf( szTestFile, sizeof( szTestFile ), "%s%03i.log", szFileBase, i );
  682. Q_FixSlashes( szTestFile );
  683. COM_CreatePath( szTestFile );
  684. fp = g_pFileSystem->Open( szTestFile, "r", "LOGDIR" );
  685. if ( !fp )
  686. {
  687. COM_CreatePath( szTestFile );
  688. fp = g_pFileSystem->Open( szTestFile, "wt", "LOGDIR" );
  689. if ( !fp )
  690. {
  691. i = 1000;
  692. }
  693. else
  694. {
  695. ConMsg( "Server logging data to file %s\n", szTestFile );
  696. }
  697. break;
  698. }
  699. g_pFileSystem->Close( fp );
  700. }
  701. if ( i == 1000 )
  702. {
  703. ConMsg( "Unable to open logfiles under %s\nLogging disabled\n", szFileBase );
  704. return;
  705. }
  706. if ( fp )
  707. {
  708. m_hLogFile = fp;
  709. }
  710. Printf( "Log file started (file \"%s\") (game \"%s\") (version \"%i\")\n", szTestFile, com_gamedir, build_number() );
  711. }