Team Fortress 2 Source Code as on 22/4/2020
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.

260 lines
6.2 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "sv_fileservercleanup.h"
  5. #include "sv_replaycontext.h"
  6. #include "sv_recordingsession.h"
  7. #include "spew.h"
  8. #if BUILD_CURL
  9. #include "curl/curl.h"
  10. #endif
  11. #undef AddJob
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //----------------------------------------------------------------------------------------
  15. #if _DEBUG
  16. ConVar replay_fileserver_simulate_delete( "replay_fileserver_simulate_delete", "0", FCVAR_GAMEDLL, "Don't delete any actual files during replay cleanup." );
  17. #endif
  18. //----------------------------------------------------------------------------------------
  19. IFileserverCleanerJob *SV_CastJobToIFileserverCleanerJob( CBaseJob *pJob )
  20. {
  21. IFileserverCleanerJob *pResult = dynamic_cast< IFileserverCleanerJob * >( pJob );
  22. AssertMsg( pResult != NULL, "Cast failed! Are you sure this job is an IFileserverCleanerJob?" );
  23. return pResult;
  24. }
  25. //----------------------------------------------------------------------------------------
  26. CFileserverCleaner::CFileserverCleaner()
  27. : m_bRunning( false ),
  28. m_bPrintResult( false ),
  29. m_pCleanerJob( NULL ),
  30. m_pSpewer( NULL ),
  31. m_nNumFilesDeleted( 0 )
  32. {
  33. }
  34. void CFileserverCleaner::MarkFileForDelete( const char *pFilename )
  35. {
  36. if ( m_bRunning )
  37. return;
  38. // Create cleaner job now if need be
  39. if ( !m_pCleanerJob )
  40. {
  41. m_pCleanerJob = SV_CreateDeleteFileJob();
  42. m_nNumFilesDeleted = 0;
  43. }
  44. IFileserverCleanerJob *pCleanerJobImp = SV_CastJobToIFileserverCleanerJob( m_pCleanerJob );
  45. AssertMsg( pCleanerJobImp != NULL, "This cast should always work!" );
  46. if ( pCleanerJobImp )
  47. {
  48. pCleanerJobImp->AddFileForDelete( pFilename );
  49. }
  50. }
  51. void CFileserverCleaner::BlockForCompletion()
  52. {
  53. if ( !m_bRunning )
  54. return;
  55. if ( !m_pCleanerJob )
  56. return;
  57. m_pCleanerJob->WaitForFinish();
  58. Clear();
  59. }
  60. void CFileserverCleaner::DoCleanAsynchronous( bool bPrintResult/*=false*/, ISpewer *pSpewer/*=g_pDefaultSpewer*/ )
  61. {
  62. if ( m_bRunning )
  63. return;
  64. if ( !m_pCleanerJob )
  65. return;
  66. m_pSpewer = pSpewer;
  67. m_bPrintResult = bPrintResult;
  68. m_bRunning = true;
  69. SV_GetThreadPool()->AddJob( m_pCleanerJob );
  70. }
  71. void CFileserverCleaner::Clear()
  72. {
  73. m_pCleanerJob->Release();
  74. m_pCleanerJob = NULL;
  75. m_bPrintResult = false;
  76. m_bRunning = false;
  77. }
  78. void CFileserverCleaner::Think()
  79. {
  80. CBaseThinker::Think();
  81. if ( !m_bRunning )
  82. return;
  83. if ( !m_pCleanerJob->IsFinished() )
  84. return;
  85. IFileserverCleanerJob *pCleanerJobImp = SV_CastJobToIFileserverCleanerJob( m_pCleanerJob );
  86. if ( pCleanerJobImp )
  87. {
  88. m_nNumFilesDeleted += pCleanerJobImp->GetNumFilesDeleted();
  89. }
  90. PrintResult();
  91. Clear();
  92. }
  93. void CFileserverCleaner::PrintResult()
  94. {
  95. if ( !m_bPrintResult || !m_pSpewer )
  96. return;
  97. m_pSpewer->PrintEmptyLine();
  98. const int nNumFilesRemoved = SV_GetFileserverCleaner()->GetNumFilesDeleted();
  99. m_pSpewer->PrintValue( "Number of files removed", Replay_va( "%i", nNumFilesRemoved ) );
  100. m_pSpewer->PrintBlockEnd();
  101. }
  102. float CFileserverCleaner::GetNextThinkTime() const
  103. {
  104. return 0.0f;
  105. }
  106. //----------------------------------------------------------------------------------------
  107. CLocalFileDeleterJob::CLocalFileDeleterJob()
  108. : m_nNumDeleted( 0 )
  109. {
  110. }
  111. void CLocalFileDeleterJob::AddFileForDelete( const char *pFilename )
  112. {
  113. CFmtStr fmtFullFilename( "%s%s", g_pServerReplayContext->GetLocalFileServerPath(), pFilename );
  114. m_vecFiles.CopyAndAddToTail( fmtFullFilename.Access() );
  115. }
  116. JobStatus_t CLocalFileDeleterJob::DoExecute()
  117. {
  118. bool bResult = true;
  119. FOR_EACH_VEC( m_vecFiles, i )
  120. {
  121. const char *pCurFilename = m_vecFiles[ i ];
  122. // File exists?
  123. PrintEventStartMsg( "File exists?" );
  124. if ( !g_pFullFileSystem->FileExists( pCurFilename ) )
  125. {
  126. CFmtStr fmtError( "File '%s' does not exist", pCurFilename );
  127. SetError( ERROR_FILE_DOES_NOT_EXIST, fmtError.Access() ); // TODO: This will only catch the last filename
  128. PrintEventResult( false );
  129. bResult = false;
  130. continue;
  131. }
  132. PrintEventResult( true );
  133. // Delete the file
  134. PrintEventStartMsg( "Deleting file" );
  135. g_pFullFileSystem->RemoveFile( pCurFilename );
  136. // File gone?
  137. const bool bDeleted = !g_pFullFileSystem->FileExists( pCurFilename );
  138. PrintEventResult( bDeleted );
  139. // Increment # deleted if appropriate
  140. if ( bDeleted )
  141. {
  142. ++m_nNumDeleted;
  143. }
  144. bResult = bResult && bDeleted;
  145. }
  146. return bResult ? JOB_OK : JOB_FAILED;
  147. }
  148. //----------------------------------------------------------------------------------------
  149. CLocalFileDeleterJob *SV_CreateLocalFileDeleterJob()
  150. {
  151. return new CLocalFileDeleterJob();
  152. }
  153. //----------------------------------------------------------------------------------------
  154. bool SV_DoFileserverCleanup( bool bForceCleanAll, ISpewer *pSpewer )
  155. {
  156. CServerRecordingSessionManager *pSessionManager = SV_GetRecordingSessionManager();
  157. CBaseRecordingSession *pRecordingSession = SV_GetRecordingSessionInProgress();
  158. for ( int i = 0; i < pSessionManager->Count(); )
  159. {
  160. CServerRecordingSession *pCurSession = SV_CastSession( SV_GetRecordingSessionManager()->m_vecObjs[ i ] );
  161. // Skip session in progress
  162. bool bRemoved = false;
  163. if ( pCurSession != NULL && pCurSession != pRecordingSession )
  164. {
  165. // Session expired?
  166. if ( bForceCleanAll || pCurSession->SessionExpired() )
  167. {
  168. // The session's OnDelete() will add the session file to the cleanup system,
  169. // and also delete all associated blocks, whose OnDelete() will also add their
  170. // associated .block files to the cleanup system.
  171. pSessionManager->RemoveFromIndex( i );
  172. bRemoved = true;
  173. }
  174. }
  175. if ( !bRemoved )
  176. {
  177. ++i;
  178. }
  179. }
  180. pSpewer->PrintBlockStart();
  181. pSpewer->PrintMsg( "Attempting to clean up stale replay data..." );
  182. pSpewer->PrintEmptyLine();
  183. // NOTE: There may be files queued up in addition to those marked above
  184. if ( !SV_GetFileserverCleaner()->HasFilesQueuedForDelete() )
  185. {
  186. pSpewer->PrintMsg( "No replay data to clean up." );
  187. pSpewer->PrintBlockEnd();
  188. }
  189. else
  190. {
  191. // Asynchronously delete all collected files
  192. SV_GetFileserverCleaner()->DoCleanAsynchronous( true, g_pBlockSpewer );
  193. }
  194. return true;
  195. }
  196. CBaseJob *SV_CreateDeleteFileJob()
  197. {
  198. return SV_CreateLocalFileDeleterJob();
  199. }
  200. //----------------------------------------------------------------------------------------