2024-04-10 20:29:18 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UbaCacheServer.h"
# include "UbaFile.h"
2024-06-10 23:37:00 -04:00
# include "UbaHttpServer.h"
2024-04-10 20:29:18 -04:00
# include "UbaNetworkBackendTcp.h"
# include "UbaNetworkServer.h"
# include "UbaPlatform.h"
# include "UbaProtocol.h"
# include "UbaStorageServer.h"
# include "UbaVersion.h"
# if PLATFORM_WINDOWS
# include <dbghelp.h>
# include <io.h>
# pragma comment (lib, "Dbghelp.lib")
# endif
namespace uba
{
const tchar * Version = GetVersionString ( ) ;
u32 DefaultCapacityGb = 500 ;
2024-05-29 01:09:06 -04:00
u32 DefaultExpiration = 3 * 24 * 60 * 60 ;
2024-04-10 20:29:18 -04:00
const tchar * DefaultRootDir = [ ] ( ) {
static tchar buf [ 256 ] ;
if ( IsWindows )
ExpandEnvironmentStringsW ( TC ( " %ProgramData% \\ Epic \\ " UE_APP_NAME ) , buf , sizeof ( buf ) ) ;
else
GetFullPathNameW ( TC ( " ~/ " UE_APP_NAME ) , sizeof_array ( buf ) , buf , nullptr ) ;
return buf ;
} ( ) ;
u32 DefaultProcessorCount = [ ] ( ) { return GetLogicalProcessorCount ( ) ; } ( ) ;
int PrintHelp ( const tchar * message )
{
LoggerWithWriter logger ( g_consoleLogWriter , TC ( " " ) ) ;
if ( * message )
{
logger . Info ( TC ( " " ) ) ;
logger . Error ( TC ( " %s " ) , message ) ;
}
logger . Info ( TC ( " " ) ) ;
logger . Info ( TC ( " ------------------------------------------- " ) ) ;
2024-05-24 14:04:37 -04:00
logger . Info ( TC ( " UbaCacheService v%s (%u) " ) , Version , CacheNetworkVersion ) ;
2024-04-10 20:29:18 -04:00
logger . Info ( TC ( " ------------------------------------------- " ) ) ;
logger . Info ( TC ( " " ) ) ;
2024-04-12 19:42:04 -04:00
logger . Info ( TC ( " -dir=<rootdir> The directory used to store data. Defaults to \" %s \" " ) , DefaultRootDir ) ;
logger . Info ( TC ( " -port=[<host>:]<port> The ip/name and port (default: %u) to listen for clients on " ) , DefaultCachePort ) ;
logger . Info ( TC ( " -capacity=<gigaby> Capacity of local store. Defaults to %u gigabytes " ) , DefaultCapacityGb ) ;
2024-05-29 01:09:06 -04:00
logger . Info ( TC ( " -expiration=<seconds> Time until unused cache entries get deleted. Defaults to %s (%u seconds) " ) , TimeToText ( MsToTime ( DefaultExpiration * 1000 ) ) . str , DefaultExpiration ) ;
2024-06-10 23:37:00 -04:00
logger . Info ( TC ( " -http=<port> If set, a http server will be started and listen on <port> " ) ) ;
2024-04-10 20:29:18 -04:00
logger . Info ( TC ( " " ) ) ;
return - 1 ;
}
2024-05-02 12:53:20 -04:00
ReaderWriterLock * g_exitLock = new ReaderWriterLock ( ) ;
LoggerWithWriter * g_logger ;
Atomic < bool > g_shouldExit ;
bool ShouldExit ( )
{
return g_shouldExit | | IsEscapePressed ( ) ;
}
2024-04-10 20:29:18 -04:00
void CtrlBreakPressed ( )
{
2024-05-02 12:53:20 -04:00
g_shouldExit = true ;
g_exitLock - > EnterWrite ( ) ;
if ( g_logger )
g_logger - > Info ( TC ( " Exiting... " ) ) ;
g_exitLock - > LeaveWrite ( ) ;
2024-04-10 20:29:18 -04:00
}
# if PLATFORM_WINDOWS
BOOL ConsoleHandler ( DWORD signal )
{
2024-05-02 12:53:20 -04:00
CtrlBreakPressed ( ) ;
return TRUE ;
2024-04-10 20:29:18 -04:00
}
# else
void ConsoleHandler ( int sig )
{
CtrlBreakPressed ( ) ;
}
# endif
StringBuffer < > g_rootDir ( DefaultRootDir ) ;
//LONG WINAPI UbaUnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo)
//{
// time_t rawtime;
// time(&rawtime);
// tm ti;
// localtime_s(&ti, &rawtime);
//
// StringBuffer<> dumpFile;
// dumpFile.Append(g_rootDir).EnsureEndsWithSlash().Appendf(TC("UbaCliCrash_%02u%02u%02u_%02u%02u%02u.dmp"), ti.tm_year - 100, ti.tm_mon + 1, ti.tm_mday, ti.tm_hour, ti.tm_min, ti.tm_sec);
//
// wprintf(TC("Unhandled exception - Writing minidump %s\n"), dumpFile.data);
// HANDLE hFile = ::CreateFileW(dumpFile.data, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// MINIDUMP_EXCEPTION_INFORMATION mei;
// mei.ThreadId = GetCurrentThreadId();
// mei.ClientPointers = TRUE;
// mei.ExceptionPointers = ExceptionInfo;
// MiniDumpWriteDump(GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &mei, NULL, NULL);
// return EXCEPTION_EXECUTE_HANDLER;
//}
int WrappedMain ( int argc , tchar * argv [ ] )
{
using namespace uba ;
//SetUnhandledExceptionFilter(UbaUnhandledExceptionFilter);
u32 storageCapacityGb = DefaultCapacityGb ;
StringBuffer < 256 > workDir ;
StringBuffer < 128 > listenIp ;
u16 port = DefaultCachePort ;
2024-06-10 23:37:00 -04:00
u16 httpPort = 0 ;
2024-04-10 20:29:18 -04:00
bool quiet = false ;
bool storeCompressed = true ;
2024-05-29 01:09:06 -04:00
u32 expirationTimeSeconds = DefaultExpiration ;
2024-04-10 20:29:18 -04:00
for ( int i = 1 ; i ! = argc ; + + i )
{
StringBuffer < > name ;
StringBuffer < > value ;
if ( const tchar * equals = TStrchr ( argv [ i ] , ' = ' ) )
{
name . Append ( argv [ i ] , equals - argv [ i ] ) ;
value . Append ( equals + 1 ) ;
}
else
{
name . Append ( argv [ i ] ) ;
}
if ( name . Equals ( TC ( " -port " ) ) )
{
if ( const tchar * portIndex = value . First ( ' : ' ) )
{
StringBuffer < > portStr ( portIndex + 1 ) ;
if ( ! portStr . Parse ( port ) )
return PrintHelp ( TC ( " Invalid value for port in -port " ) ) ;
listenIp . Append ( value . data , portIndex - value . data ) ;
}
else
{
if ( ! value . Parse ( port ) )
return PrintHelp ( TC ( " Invalid value for -port " ) ) ;
}
}
else if ( name . Equals ( TC ( " -dir " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -dir needs a value " ) ) ;
if ( ( g_rootDir . count = GetFullPathNameW ( value . Replace ( ' / ' , PathSeparator ) . data , g_rootDir . capacity , g_rootDir . data , nullptr ) ) = = 0 )
return PrintHelp ( StringBuffer < > ( ) . Appendf ( TC ( " -dir has invalid path %s " ) , g_rootDir . data ) . data ) ;
}
2024-04-12 19:42:04 -04:00
else if ( name . Equals ( TC ( " -capacity " ) ) )
{
if ( ! value . Parse ( storageCapacityGb ) )
return PrintHelp ( TC ( " Invalid value for -capacity " ) ) ;
}
2024-05-29 01:07:06 -04:00
else if ( name . Equals ( TC ( " -expiration " ) ) )
{
if ( ! value . Parse ( expirationTimeSeconds ) )
return PrintHelp ( TC ( " Invalid value for -expire " ) ) ;
}
2024-06-10 23:37:00 -04:00
else if ( name . Equals ( TC ( " -http " ) ) )
{
if ( ! value . Parse ( httpPort ) )
httpPort = 80 ;
}
2024-04-10 20:29:18 -04:00
else if ( name . Equals ( TC ( " -? " ) ) )
{
return PrintHelp ( TC ( " " ) ) ;
}
else
{
StringBuffer < > msg ;
msg . Appendf ( TC ( " Unknown argument '%s' " ) , name . data ) ;
return PrintHelp ( msg . data ) ;
}
}
FilteredLogWriter logWriter ( g_consoleLogWriter , quiet ? LogEntryType_Info : LogEntryType_Detail ) ;
LoggerWithWriter logger ( logWriter , TC ( " " ) ) ;
2024-05-02 12:53:20 -04:00
g_exitLock - > EnterWrite ( ) ;
g_logger = & logger ;
g_exitLock - > LeaveWrite ( ) ;
auto glg = MakeGuard ( [ ] ( ) { g_exitLock - > EnterWrite ( ) ; g_logger = nullptr ; g_exitLock - > LeaveWrite ( ) ; } ) ;
2024-04-10 20:29:18 -04:00
const tchar * dbgStr = TC ( " " ) ;
# if UBA_DEBUG
dbgStr = TC ( " (DEBUG) " ) ;
# endif
2024-07-26 16:44:00 -04:00
logger . Info ( TC ( " UbaCacheService v%s(%u)%s (Workers: %u, Rootdir: \" %s \" , StoreCapacity: %uGb, Expiration: %s) " ) , Version , CacheNetworkVersion , dbgStr , GetLogicalProcessorCount ( ) , g_rootDir . data , storageCapacityGb , TimeToText ( MsToTime ( expirationTimeSeconds ) * 1000 , true ) . str ) ;
u64 maintenanceReserveSizeMb = 128 ;
2024-07-29 13:48:36 -04:00
if ( SupportsHugePages ( ) )
{
u64 hugePageCount = GetHugePageCount ( ) ;
u64 recommendedHugePageCount = ( maintenanceReserveSizeMb * GetLogicalProcessorCount ( ) ) / 2 ;
if ( hugePageCount < recommendedHugePageCount )
logger . Info ( TC ( " Improve maintenance performance by enabling %llu huge pages on system (%llu enabled) " ) , recommendedHugePageCount , hugePageCount ) ;
}
2024-07-26 16:44:00 -04:00
logger . Info ( TC ( " " ) ) ;
2024-04-10 20:29:18 -04:00
u64 storageCapacity = u64 ( storageCapacityGb ) * 1000 * 1000 * 1000 ;
StringBuffer < 512 > currentDir ;
GetCurrentDirectoryW ( currentDir ) ;
if ( workDir . IsEmpty ( ) )
workDir . Append ( currentDir ) ;
// TODO: Change workdir to make it full
# if PLATFORM_WINDOWS
SetConsoleCtrlHandler ( ConsoleHandler , TRUE ) ;
# else
signal ( SIGINT , ConsoleHandler ) ;
2024-05-08 19:24:24 -04:00
signal ( SIGTERM , ConsoleHandler ) ;
2024-04-10 20:29:18 -04:00
# endif
NetworkBackendTcp networkBackend ( logWriter ) ;
NetworkServerCreateInfo nsci ( logWriter ) ;
//nsci.workerCount = 4;
bool ctorSuccess = true ;
NetworkServer networkServer ( ctorSuccess , nsci ) ;
if ( ! ctorSuccess )
return - 1 ;
StorageServerCreateInfo storageInfo ( networkServer , g_rootDir . data , logWriter ) ;
storageInfo . casCapacityBytes = storageCapacity ;
storageInfo . storeCompressed = storeCompressed ;
storageInfo . allowFallback = false ;
2024-04-12 19:42:04 -04:00
storageInfo . manuallyHandleOverflow = true ;
2024-04-10 20:29:18 -04:00
storageInfo . writeRecievedCasFilesToDisk = true ;
StorageServer storageServer ( storageInfo ) ;
2024-05-29 01:07:06 -04:00
if ( ! storageServer . LoadCasTable ( true , true ) )
2024-04-10 20:29:18 -04:00
return - 1 ;
2024-05-29 13:59:57 -04:00
CacheServerCreateInfo cacheInfo ( storageServer , g_rootDir . data , logWriter ) ;
2024-05-29 01:07:06 -04:00
cacheInfo . expirationTimeSeconds = expirationTimeSeconds ;
2024-07-26 16:44:00 -04:00
cacheInfo . maintenanceReserveSize = maintenanceReserveSizeMb * 1024 * 1024 ;
2024-05-29 01:07:06 -04:00
CacheServer cacheServer ( cacheInfo ) ;
2024-04-10 20:29:18 -04:00
if ( ! cacheServer . Load ( ) )
return - 1 ;
2024-05-02 12:53:20 -04:00
if ( ! cacheServer . RunMaintenance ( true , ShouldExit ) )
2024-04-10 20:29:18 -04:00
return - 1 ;
2024-06-10 23:37:00 -04:00
HttpServer httpServer ( logWriter , networkBackend ) ;
if ( httpPort )
{
httpServer . AddCommandHandler ( [ & ] ( const tchar * command , tchar * arguments )
{
if ( ! Equals ( command , TC ( " addcrypto " ) ) )
return " Unknown command ('addcrypto' only available) " ;
u64 expirationSeconds = 60 ;
if ( tchar * comma = TStrchr ( arguments , ' , ' ) )
{
* comma = 0 ;
if ( ! Parse ( expirationSeconds , comma + 1 , TStrlen ( comma + 1 ) ) )
return " Failed to parse expiration seconds " ;
}
u8 crypto128Data [ 16 ] ;
if ( ! CryptoFromString ( crypto128Data , 16 , arguments ) )
return " Failed to read crypto argument " ;
u64 expirationTime = GetTime ( ) + MsToTime ( expirationSeconds * 1000 ) ;
networkServer . RegisterCryptoKey ( crypto128Data , expirationTime ) ;
return ( const char * ) nullptr ;
} ) ;
httpServer . StartListen ( httpPort ) ;
}
2024-04-10 20:29:18 -04:00
{
auto stopListen = MakeGuard ( [ & ] ( ) { networkBackend . StopListen ( ) ; } ) ;
auto stopServer = MakeGuard ( [ & ] ( ) { networkServer . DisconnectClients ( ) ; } ) ;
if ( ! networkServer . StartListen ( networkBackend , port , listenIp . data ) )
return - 1 ;
2024-05-09 15:06:47 -04:00
while ( ! ShouldExit ( ) & & ! cacheServer . ShouldShutdown ( ) )
2024-04-10 20:29:18 -04:00
{
Sleep ( 1000 ) ;
2024-05-02 12:53:20 -04:00
if ( ! cacheServer . RunMaintenance ( false , ShouldExit ) )
2024-04-10 20:29:18 -04:00
break ;
}
}
2024-05-29 01:07:06 -04:00
storageServer . SaveCasTable ( true ) ;
2024-04-10 20:29:18 -04:00
cacheServer . Save ( ) ;
return 0 ;
}
}
# if PLATFORM_WINDOWS
int wmain ( int argc , wchar_t * argv [ ] )
{
2024-07-26 16:44:00 -04:00
int res = uba : : WrappedMain ( argc , argv ) ;
return res ;
2024-04-10 20:29:18 -04:00
}
# else
int main ( int argc , char * argv [ ] )
{
return uba : : WrappedMain ( argc , argv ) ;
}
# endif