2023-11-29 18:47:11 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2024-03-03 23:33:30 -05:00
# include "UbaAWS.h"
2024-06-15 01:11:17 -04:00
# include "UbaConfig.h"
2024-03-03 23:33:30 -05:00
# include "UbaDirectoryIterator.h"
# include "UbaNetworkBackendMemory.h"
2023-11-29 18:47:11 -05:00
# include "UbaNetworkBackendQuic.h"
# include "UbaNetworkBackendTcp.h"
2024-03-08 18:31:48 -05:00
# include "UbaNetworkMessage.h"
2023-11-29 18:47:11 -05:00
# include "UbaNetworkServer.h"
# include "UbaSessionClient.h"
# include "UbaStorageClient.h"
# include "UbaStorageProxy.h"
# include "UbaSentry.h"
# include "UbaVersion.h"
# if defined(UBA_USE_SENTRY)
# pragma comment (lib, "WinHttp.lib")
# pragma comment (lib, "Version.lib")
# pragma comment (lib, "sentry.lib")
# pragma comment (lib, "crashpad_client.lib")
# pragma comment (lib, "crashpad_compat.lib")
# pragma comment (lib, "crashpad_getopt.lib")
# pragma comment (lib, "crashpad_handler_lib.lib")
# pragma comment (lib, "crashpad_minidump.lib")
# pragma comment (lib, "crashpad_snapshot.lib")
# pragma comment (lib, "crashpad_tools.lib")
# pragma comment (lib, "crashpad_util.lib")
# pragma comment (lib, "mini_chromium.lib")
# endif
# if PLATFORM_WINDOWS
# define UBA_AUTO_UPDATE 1
2024-03-03 23:33:30 -05:00
# define UBA_USE_EXCEPTION_HANDLER 0
2023-11-29 18:47:11 -05:00
# else
# define UBA_AUTO_UPDATE 0
2024-03-03 23:33:30 -05:00
# define UBA_USE_EXCEPTION_HANDLER 0
2023-11-29 18:47:11 -05:00
# endif
//#include <dbghelp.h>
//#pragma comment (lib, "Dbghelp.lib")
namespace uba
{
2024-02-07 20:36:18 -05:00
const tchar * Version = GetVersionString ( ) ;
2023-12-14 13:08:17 -05:00
constexpr u32 DefaultCapacityGb = 20 ;
constexpr u32 DefaultListenTimeout = 5 ;
2023-11-29 18:47:11 -05:00
const tchar * DefaultRootDir = [ ] ( ) {
static tchar buf [ 256 ] ;
if ( IsWindows )
ExpandEnvironmentStringsW ( TC ( " %ProgramData% \\ Epic \\ " UE_APP_NAME ) , buf , sizeof ( buf ) ) ;
else
2023-12-09 03:11:34 -05:00
GetFullPathNameW ( TC ( " ~/ " UE_APP_NAME ) , sizeof_array ( buf ) , buf , nullptr ) ;
2023-11-29 18:47:11 -05:00
return buf ;
} ( ) ;
u32 DefaultProcessorCount = [ ] ( ) { return GetLogicalProcessorCount ( ) ; } ( ) ;
const tchar * DefaultAgentName = [ ] ( ) { static tchar buf [ 256 ] ; GetComputerNameW ( buf , sizeof_array ( buf ) ) ; return buf ; } ( ) ;
2024-03-08 18:31:48 -05:00
u32 DefaultMaxConnectionCount = 4 ;
2023-11-29 18:47:11 -05:00
int PrintHelp ( const tchar * message )
{
LoggerWithWriter logger ( g_consoleLogWriter , TC ( " " ) ) ;
if ( * message )
{
logger . Info ( TC ( " " ) ) ;
logger . Error ( TC ( " %s " ) , message ) ;
}
logger . Info ( TC ( " " ) ) ;
2023-12-09 03:11:34 -05:00
logger . Info ( TC ( " ------------------------------------------- " ) ) ;
2024-02-07 20:36:18 -05:00
logger . Info ( TC ( " UbaAgent v%s " ) , Version ) ;
2023-12-09 03:11:34 -05:00
logger . Info ( TC ( " ------------------------------------------- " ) ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " " ) ) ;
logger . Info ( TC ( " When started UbaAgent will keep trying to connect to provided host address. " ) ) ;
logger . Info ( TC ( " Once connected it will start helping out. Nothing else is needed :) " ) ) ;
logger . Info ( TC ( " " ) ) ;
logger . Info ( TC ( " -dir=<rootdir> The directory used to store data. Defaults to \" %s \" " ) , DefaultRootDir ) ;
logger . Info ( TC ( " -host=<host>[:<port>] The ip/name and port (default: %u) of the machine we want to help " ) , DefaultPort ) ;
logger . Info ( TC ( " -listen[=port] Agent will listen for connections on port (default: %u) and help when connected " ) , DefaultPort ) ;
2023-12-14 13:08:17 -05:00
logger . Info ( TC ( " -listenTimeout=<sec> Number of seconds agent will listen for host before giving up (default: %u) " ) , DefaultListenTimeout ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " -proxyport=<port> Which port that agent will use if being assigned to be proxy for other agents (default: %u) " ) , DefaultStorageProxyPort ) ;
logger . Info ( TC ( " -maxcpu=<number> Max number of processes that can be started. Defaults to \" %u \" on this machine " ) , DefaultProcessorCount ) ;
logger . Info ( TC ( " -mulcpu=<number> This value multiplies with number of cpu to figure out max cpu. Defaults to 1.0 " ) ) ;
2024-02-13 23:53:47 -05:00
logger . Info ( TC ( " -maxcon=<number> Max number of connections that can be started by agent. Defaults to \" %u \" (amount up to max will depend on ping) " ) , DefaultMaxConnectionCount ) ;
2024-08-28 13:33:28 -04:00
logger . Info ( TC ( " -maxworkers=<number> Max number of workers is started by agent. Defaults to \" %u \" " ) , DefaultProcessorCount ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " -capacity=<gigaby> Capacity of local store. Defaults to %u gigabytes " ) , DefaultCapacityGb ) ;
2024-06-15 01:11:17 -04:00
logger . Info ( TC ( " -config=<file> Config file that contains options for various systems " ) ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " -quic Use Quic instead of tcp backend. " ) ) ;
logger . Info ( TC ( " -name=<name> The identifier of this agent. Defaults to \" %s \" on this machine " ) , DefaultAgentName ) ;
logger . Info ( TC ( " -stats[=<threshold>] Print stats for each process if higher than threshold " ) ) ;
logger . Info ( TC ( " -verbose Print debug information to console " ) ) ;
logger . Info ( TC ( " -log Log all processes detouring information to file (only works with debug builds) " ) ) ;
logger . Info ( TC ( " -nocustomalloc Disable custom allocator for processes. If you see odd crashes this can be tested " ) ) ;
logger . Info ( TC ( " -storeraw Disable compression of storage. This will use more storage and might improve performance " ) ) ;
logger . Info ( TC ( " -sendraw Disable compression of send. This will use more bandwidth but less cpu " ) ) ;
logger . Info ( TC ( " -sendsize Max size of messages being sent from client to server (does not affect server to client) " ) ) ;
logger . Info ( TC ( " -named=<name> Use named events and file mappings by providing the base name in this option " ) ) ;
logger . Info ( TC ( " -nopoll Does not keep polling for work; attempts to connect once then exits " ) ) ;
logger . Info ( TC ( " -nostore Does not use storage to store files (with a few exceptions such as binaries) " ) ) ;
2023-11-30 03:08:02 -05:00
logger . Info ( TC ( " -resetstore Delete all cas " ) ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " -quiet Does not output any logging in console " ) ) ;
logger . Info ( TC ( " -maxidle=<seconds> Max time agent will idle before disconnecting. Ignored if -nopoll is not set " ) ) ;
logger . Info ( TC ( " -binasversion Will use binaries as version. This will cause updates everytime binaries change on host side " ) ) ;
logger . Info ( TC ( " -summary Print summary at the end of a session " ) ) ;
logger . Info ( TC ( " -eventfile=<file> File containing external events to agent. Things like machine is about to be terminated etc " ) ) ;
logger . Info ( TC ( " -sentry Enable sentry " ) ) ;
logger . Info ( TC ( " -zone Set the zone this machine exists in. This info is used to figure out if proxies should be created. " ) ) ;
2024-08-29 17:30:46 -04:00
logger . Info ( TC ( " -noproxy Does not allow this agent to be a storage proxy for other agents " ) ) ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " -killrandom Kills random process and exit session " ) ) ;
logger . Info ( TC ( " -memwait=<percent> The amount of memory needed to spawn a process. Set this to 100 to disable. Defaults to 80%% " ) ) ;
logger . Info ( TC ( " -memkill=<percent> The amount of memory needed before processes starts to be killed. Set this to 100 to disable. Defaults to 90%% " ) ) ;
2024-06-10 23:37:00 -04:00
logger . Info ( TC ( " -crypto=<key> 32 character (16 bytes) crypto key used for secure network transfer " ) ) ;
2023-12-05 16:21:36 -05:00
logger . Info ( TC ( " -populateCas=<dir> Prepopulate cas database with files in dir. If files needed exists on machine this can be an optimization " ) ) ;
2023-12-13 01:13:24 -05:00
# if PLATFORM_MAC
2023-12-14 17:22:19 -05:00
logger . Info ( TC ( " -populateCasFromXcodeVersion=<version> Prepopulate cas database with files from local xcode installation that matches the version. " ) ) ;
logger . Info ( TC ( " -populateCasFromAllXcodes Prepopulate cas database with files from local xcode installation that matches the version. " ) ) ;
2023-12-13 01:13:24 -05:00
# endif
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " " ) ) ;
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
}
2024-05-02 12:53:20 -04:00
ReaderWriterLock * g_exitLock = new ReaderWriterLock ( ) ;
LoggerWithWriter * g_logger ;
SessionClient * g_sessionClient ;
Atomic < bool > g_shouldExit ;
2024-08-28 00:16:19 -04:00
Atomic < bool > g_ctrlPressed ;
2024-05-02 12:53:20 -04:00
bool ShouldExit ( )
{
return g_shouldExit | | IsEscapePressed ( ) ;
}
2023-11-29 18:47:11 -05:00
void CtrlBreakPressed ( )
{
2024-08-28 00:16:19 -04:00
if ( g_ctrlPressed )
FatalError ( 13 , TC ( " Force terminate " ) ) ;
2024-05-02 12:53:20 -04:00
g_shouldExit = true ;
2024-08-28 00:16:19 -04:00
g_ctrlPressed = true ;
2024-03-08 18:31:48 -05:00
2024-05-02 12:53:20 -04:00
g_exitLock - > EnterWrite ( ) ;
if ( g_logger )
g_logger - > Info ( TC ( " Exiting... " ) ) ;
if ( g_sessionClient )
g_sessionClient - > Stop ( ) ;
g_exitLock - > LeaveWrite ( ) ;
2023-11-29 18:47:11 -05:00
}
# if PLATFORM_WINDOWS
2024-03-03 23:33:30 -05:00
int ReportSEH ( LPEXCEPTION_POINTERS exceptionInfo )
{
StringBuffer < 4096 > assertInfo ;
assertInfo . Appendf ( TC ( " SEH EXCEPTION %u (0x%llx) " ) , exceptionInfo - > ExceptionRecord - > ExceptionCode , exceptionInfo - > ExceptionRecord - > ExceptionAddress ) ;
WriteAssertInfo ( assertInfo , nullptr , nullptr , 0 , nullptr , 1 ) ;
LoggerWithWriter ( g_consoleLogWriter ) . Log ( LogEntryType_Error , assertInfo . data , assertInfo . count ) ;
return EXCEPTION_CONTINUE_SEARCH ;
}
2023-11-29 18:47:11 -05:00
BOOL ConsoleHandler ( DWORD signal )
{
2024-05-02 12:53:20 -04:00
CtrlBreakPressed ( ) ;
return TRUE ;
2023-11-29 18:47:11 -05:00
}
# else
void ConsoleHandler ( int sig )
{
CtrlBreakPressed ( ) ;
}
# endif
StringBuffer < > g_rootDir ( DefaultRootDir ) ;
2024-03-03 23:33:30 -05:00
# if UBA_USE_EXCEPTION_HANDLER
LONG WINAPI UbaUnhandledExceptionFilter ( EXCEPTION_POINTERS * ExceptionInfo )
{
StringBuffer < 4096 > assertInfo ;
WriteAssertInfo ( assertInfo , TC ( " " ) , nullptr , 0 , nullptr , 1 ) ;
LoggerWithWriter ( g_consoleLogWriter ) . Log ( LogEntryType_Error , assertInfo . data , assertInfo . count ) ;
2023-11-29 18:47:11 -05:00
// time_t rawtime;
// time(&rawtime);
// tm ti;
// localtime_s(&ti, &rawtime);
//
// StringBuffer<> dumpFile;
// dumpFile.Append(g_rootDir).EnsureEndsWithSlash().Appendf(TC("UbaAgentCrash_%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 = CreateFile(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);
2024-03-03 23:33:30 -05:00
return EXCEPTION_EXECUTE_HANDLER ;
}
# endif
2023-11-29 18:47:11 -05:00
# if UBA_AUTO_UPDATE
const tchar * g_ubaAgentBinaries [ ] = { UBA_AGENT_EXECUTABLE , UBA_DETOURS_LIBRARY } ;
2024-05-02 12:53:20 -04:00
bool DownloadBinaries ( StorageClient & storageClient , CasKey * keys )
2023-11-29 18:47:11 -05:00
{
StringBuffer < 256 > binDir ( g_rootDir ) ;
binDir . Append ( TC ( " \\ binaries \\ " ) ) ;
2024-05-02 12:53:20 -04:00
storageClient . CreateDirectory ( binDir . data ) ;
2023-11-29 18:47:11 -05:00
u32 index = 0 ;
for ( auto file : g_ubaAgentBinaries )
{
Storage : : RetrieveResult result ;
2024-05-02 12:53:20 -04:00
if ( ! storageClient . RetrieveCasFile ( result , keys [ index + + ] , file ) )
2023-11-29 18:47:11 -05:00
return false ;
StringBuffer < 256 > fullFile ( binDir ) ;
fullFile . Append ( file ) ;
2024-05-02 12:53:20 -04:00
if ( ! storageClient . CopyOrLink ( result . casKey , fullFile . data , DefaultAttributes ( ) ) )
2023-11-29 18:47:11 -05:00
return false ;
}
return true ;
}
bool LaunchProcess ( tchar * args )
{
STARTUPINFOW si ;
ZeroMemory ( & si , sizeof ( si ) ) ;
si . cb = sizeof ( si ) ;
PROCESS_INFORMATION pi ;
ZeroMemory ( & pi , sizeof ( pi ) ) ;
if ( ! CreateProcessW ( NULL , args , NULL , NULL , false , 0 , NULL , NULL , & si , & pi ) )
return false ;
CloseHandle ( pi . hProcess ) ;
CloseHandle ( pi . hThread ) ;
return true ;
}
bool LaunchTemp ( Logger & logger , int argc , tchar * argv [ ] )
{
StringBuffer < 256 > currentDir ;
if ( ! GetDirectoryOfCurrentModule ( logger , currentDir ) )
return false ;
StringBuffer < > args ;
args . Append ( g_rootDir ) . Append ( TC ( " \\ binaries \\ " ) UBA_AGENT_EXECUTABLE ) ;
args . Append ( TC ( " -relaunch= \" " ) ) . Append ( currentDir ) . Append ( TC ( " \" " ) ) ;
args . Appendf ( TC ( " -waitid=%u " ) , GetCurrentProcessId ( ) ) ;
for ( int i = 1 ; i ! = argc ; + + i )
args . Append ( ' ' ) . Append ( argv [ i ] ) ;
return LaunchProcess ( args . data ) ;
}
bool WaitForProcess ( u32 procId )
{
HANDLE ph = OpenProcess ( SYNCHRONIZE , TRUE , procId ) ;
if ( ! ph )
return true ;
bool success = WaitForSingleObject ( ph , 10000 ) = = WAIT_OBJECT_0 ;
CloseHandle ( ph ) ;
return success ;
}
2024-08-21 03:00:44 -04:00
bool LaunchReal ( Logger & logger , StringBufferBase & relaunchPath , int argc , tchar * argv [ ] )
2023-11-29 18:47:11 -05:00
{
StringBuffer < 256 > currentDir ;
if ( ! GetDirectoryOfCurrentModule ( logger , currentDir ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
logger . Info ( TC ( " Copying new binaries... " ) ) ;
for ( auto file : g_ubaAgentBinaries )
{
StringBuffer < 256 > from ( currentDir ) ;
from . Append ( ' \\ ' ) . Append ( file ) ;
StringBuffer < 256 > to ( relaunchPath . data ) ;
to . Append ( ' \\ ' ) . Append ( file ) ;
if ( ! uba : : CopyFileW ( from . data , to . data , false ) )
2024-08-21 03:00:44 -04:00
return logger . Error ( TC ( " Failed to copy file for relaunch " ) ) ;
2023-11-29 18:47:11 -05:00
}
StringBuffer < > args ;
args . Append ( relaunchPath ) . Append ( PathSeparator ) . Append ( UBA_AGENT_EXECUTABLE ) ;
//args.Appendf(TC(" -waitid=%u"), GetCurrentProcessId());
for ( int i = 1 ; i ! = argc ; + + i )
if ( ! StartsWith ( argv [ i ] , TC ( " -relaunch " ) ) & & ! StartsWith ( argv [ i ] , TC ( " -waitid " ) ) )
args . Append ( ' ' ) . Append ( argv [ i ] ) ;
logger . Info ( TC ( " Relaunching new %s... " ) , UBA_AGENT_EXECUTABLE ) ;
logger . Info ( TC ( " " ) ) ;
2024-08-21 03:00:44 -04:00
return LaunchProcess ( args . data ) ;
2023-11-29 18:47:11 -05:00
}
# endif // UBA_AUTO_UPDATE
2024-08-21 03:00:44 -04:00
bool ExpandEnvironmentVariables ( StringBufferBase & str )
2023-11-29 18:47:11 -05:00
{
StringBuffer < > expandedDir ;
u64 offset = 0 ;
while ( true )
{
const tchar * begin = str . First ( ' % ' , offset ) ;
if ( ! begin )
{
expandedDir . Append ( str . data + offset ) ;
str . Clear ( ) . Append ( expandedDir ) ;
break ;
}
u64 beginOffset = begin - str . data ;
const tchar * end = str . First ( ' % ' , beginOffset + 1 ) ;
if ( ! end )
return PrintHelp ( TC ( " Missing closing % for environment variable in dir path " ) ) ;
u64 endOffset = end - str . data ;
StringBuffer < 256 > var ;
var . Append ( begin + 1 , endOffset - beginOffset - 1 ) ;
StringBuffer < > value ;
value . count = GetEnvironmentVariableW ( var . data , value . data , value . capacity ) ;
if ( ! value . count )
{
StringBuffer < 256 > err ;
err . Appendf ( TC ( " Can't find environment variable %s used in dir path " ) , var . data ) ;
return PrintHelp ( err . data ) ;
}
expandedDir . Append ( str . data + offset , beginOffset - offset ) . Append ( value ) ;
offset = endOffset + 1 ;
}
2024-08-21 03:00:44 -04:00
return true ;
2023-11-29 18:47:11 -05:00
}
bool IsTerminating ( Logger & logger , const tchar * eventFile , StringBufferBase & outReason , u64 & outTerminationTimeMs )
{
if ( ! * eventFile )
return false ;
u64 fileSize ;
if ( ! FileExists ( logger , eventFile , & fileSize ) )
return false ;
outTerminationTimeMs = 0 ;
Sleep ( 1000 ) ;
FileHandle fileHandle ;
if ( ! OpenFileSequentialRead ( logger , eventFile , fileHandle ) )
return true ; // Fail to open the file we treat as instant termination
auto g = MakeGuard ( [ & ] ( ) { CloseFile ( eventFile , fileHandle ) ; } ) ;
char buffer [ 2048 ] ;
u64 toRead = Min ( fileSize , u64 ( sizeof ( buffer ) - 1 ) ) ;
if ( ! ReadFile ( logger , eventFile , fileHandle , buffer , toRead ) )
return true ; // Fail to read the file we treat as instant termination
buffer [ toRead ] = 0 ;
StringBuffer < > reason ;
u64 terminateTimeMsUtc = 0 ;
char * lineBegin = buffer ;
u32 lineIndex = 0 ;
bool loop = true ;
while ( loop )
{
char * lineEnd = strchr ( lineBegin , ' \n ' ) ;
if ( lineEnd )
{
if ( lineBegin < lineEnd & & lineEnd [ - 1 ] = = ' \r ' )
lineEnd [ - 1 ] = 0 ;
else
lineEnd [ 0 ] = 0 ;
}
else
loop = false ;
switch ( lineIndex )
{
case 0 : // version
if ( strcmp ( lineBegin , " v1 " ) ! = 0 )
loop = false ;
break ;
case 1 : // Relative time
//strtoull(lineBegin, nullptr, 10);
break ;
case 2 : // Absolute time
terminateTimeMsUtc = strtoull ( lineBegin , nullptr , 10 ) ;
break ;
case 3 : // reason
2024-02-09 16:12:12 -05:00
outReason . Appendf ( PERCENT_HS , lineBegin ) ;
2023-11-29 18:47:11 -05:00
break ;
}
lineBegin = lineEnd + 1 ;
+ + lineIndex ;
}
if ( terminateTimeMsUtc ! = 0 )
{
u64 nowMsUtc = time ( 0 ) * 1000 ;
if ( terminateTimeMsUtc > nowMsUtc )
{
u64 relativeTime = terminateTimeMsUtc - nowMsUtc ;
outTerminationTimeMs = relativeTime ;
}
}
return true ;
}
2024-08-21 03:00:44 -04:00
bool WrappedMain ( int argc , tchar * argv [ ] )
2023-11-29 18:47:11 -05:00
{
2024-03-03 23:33:30 -05:00
# if UBA_USE_EXCEPTION_HANDLER
SetUnhandledExceptionFilter ( UbaUnhandledExceptionFilter ) ;
# endif
2023-11-29 18:47:11 -05:00
u32 maxProcessCount = DefaultProcessorCount ;
2024-08-28 13:33:28 -04:00
u32 maxWorkerCount = DefaultProcessorCount ;
2023-11-29 18:47:11 -05:00
float mulProcessValue = 1.0f ;
2024-02-13 23:53:47 -05:00
u32 maxConnectionCount = DefaultMaxConnectionCount ;
2023-11-29 18:47:11 -05:00
u32 outputStatsThresholdMs = 0 ;
u32 storageCapacityGb = DefaultCapacityGb ;
StringBuffer < 256 > host ;
StringBuffer < 256 > named ;
StringBuffer < 512 > relaunchPath ;
StringBuffer < 256 > eventFile ;
2024-06-15 01:11:17 -04:00
StringBuffer < 256 > configFile ;
2024-03-08 18:31:48 -05:00
TString command ;
2023-11-29 18:47:11 -05:00
u16 port = DefaultPort ;
u16 proxyPort = DefaultStorageProxyPort ;
StringBuffer < > agentName ( DefaultAgentName ) ;
bool useListen = false ;
bool logToFile = false ;
bool storeCompressed = true ;
bool sendCompressed = true ;
bool disableCustomAllocator = false ;
bool useBinariesAsVersion = false ;
bool useQuic = false ;
bool poll = true ;
2024-08-29 17:30:46 -04:00
bool allowProxy = true ;
2023-11-29 18:47:11 -05:00
bool useStorage = true ;
2023-11-30 03:08:02 -05:00
bool resetStore = false ;
2023-11-29 18:47:11 -05:00
bool quiet = false ;
bool verbose = false ;
bool printSummary = false ;
bool killRandom = false ;
StringBuffer < 512 > sentryUrl ;
StringBuffer < 128 > zone ;
u32 maxIdleSeconds = ~ 0u ;
u32 sendSize = SendDefaultSize ;
u32 waitProcessId = ~ 0u ;
u32 memWaitLoadPercent = 80 ;
u32 memKillLoadPercent = 90 ;
2023-12-14 13:08:17 -05:00
u32 listenTimeoutSec = DefaultListenTimeout ;
2023-11-29 18:47:11 -05:00
u8 crypto [ 16 ] ;
bool hasCrypto = false ;
2023-12-05 16:21:36 -05:00
Vector < TString > populateCasDirs ;
2023-11-29 18:47:11 -05:00
2023-12-13 01:13:24 -05:00
# if PLATFORM_MAC
2023-12-14 17:22:19 -05:00
StringBuffer < 32 > populateCasFromXcodeVersion ;
2024-08-21 20:13:16 -04:00
bool populateCasFromAllXcodes = false ;
2023-12-13 01:13:24 -05:00
# endif
2023-11-29 18:47:11 -05: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 ( " -verbose " ) ) )
{
verbose = true ;
}
else if ( name . Equals ( TC ( " -relaunch " ) ) )
{
relaunchPath . Append ( value ) ;
}
else if ( name . Equals ( TC ( " -waitid " ) ) )
{
value . Parse ( waitProcessId ) ;
}
else if ( name . Equals ( TC ( " -maxcpu " ) ) )
{
2024-09-03 17:12:48 -04:00
if ( ! ExpandEnvironmentVariables ( value ) )
return false ;
2024-09-03 11:44:40 -04:00
u32 defaultValue = maxProcessCount ;
2023-11-29 18:47:11 -05:00
if ( ! value . Parse ( maxProcessCount ) )
2024-09-03 11:44:40 -04:00
{
LoggerWithWriter ( g_consoleLogWriter , TC ( " " ) ) . Warning ( TC ( " Invalid value for -maxcpu, ignoring! " ) ) ;
maxProcessCount = defaultValue ;
}
2023-11-29 18:47:11 -05:00
}
else if ( name . Equals ( TC ( " -mulcpu " ) ) )
{
2024-09-03 17:12:48 -04:00
if ( ! ExpandEnvironmentVariables ( value ) )
return false ;
2024-09-03 11:44:40 -04:00
float defaultValue = mulProcessValue ;
2023-11-29 18:47:11 -05:00
if ( ! value . Parse ( mulProcessValue ) )
2024-09-03 11:44:40 -04:00
{
LoggerWithWriter ( g_consoleLogWriter , TC ( " " ) ) . Warning ( TC ( " Invalid value for -mulcpu, ignoring! " ) ) ;
mulProcessValue = defaultValue ;
}
2023-11-29 18:47:11 -05:00
}
2024-02-13 23:53:47 -05:00
else if ( name . Equals ( TC ( " -maxcon " ) ) | | name . Equals ( TC ( " -maxtcp " ) ) )
2023-11-29 18:47:11 -05:00
{
2024-02-13 23:53:47 -05:00
if ( ! value . Parse ( maxConnectionCount ) | | maxConnectionCount = = 0 )
return PrintHelp ( TC ( " Invalid value for -maxcon " ) ) ;
2023-11-29 18:47:11 -05:00
}
2024-08-28 13:33:28 -04:00
else if ( name . Equals ( TC ( " -maxworkers " ) ) )
{
if ( ! value . Parse ( maxWorkerCount ) )
return PrintHelp ( TC ( " Invalid value for -maxworkers " ) ) ;
}
2023-11-29 18:47:11 -05:00
else if ( name . Equals ( TC ( " -capacity " ) ) )
{
if ( ! value . Parse ( storageCapacityGb ) )
return PrintHelp ( TC ( " Invalid value for -capacity " ) ) ;
}
2024-06-15 01:11:17 -04:00
else if ( name . Equals ( TC ( " -config " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -dir needs a value " ) ) ;
2024-08-21 03:00:44 -04:00
if ( ! ExpandEnvironmentVariables ( value ) )
return false ;
2024-06-15 01:11:17 -04:00
configFile . Append ( value ) ;
}
2023-11-29 18:47:11 -05:00
else if ( name . Equals ( TC ( " -stats " ) ) )
{
if ( value . IsEmpty ( ) )
outputStatsThresholdMs = 1 ;
else if ( ! value . Parse ( outputStatsThresholdMs ) )
return PrintHelp ( TC ( " Invalid for -stats " ) ) ;
}
else if ( name . Equals ( TC ( " -host " ) ) )
{
if ( const tchar * portIndex = value . First ( ' : ' ) )
{
StringBuffer < > portStr ( portIndex + 1 ) ;
if ( ! portStr . Parse ( port ) )
return PrintHelp ( TC ( " Invalid value for port in -host " ) ) ;
value . Resize ( portIndex - value . data ) ;
}
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -host needs a name/ip " ) ) ;
host . Append ( value ) ;
}
else if ( name . Equals ( TC ( " -listen " ) ) )
{
if ( ! value . IsEmpty ( ) )
if ( ! value . Parse ( port ) )
return PrintHelp ( TC ( " Invalid value for -capacity " ) ) ;
useListen = true ;
}
2023-12-14 13:08:17 -05:00
else if ( name . Equals ( TC ( " -listenTimeout " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -listenTimeout needs a value " ) ) ;
if ( ! value . Parse ( listenTimeoutSec ) )
return PrintHelp ( TC ( " Invalid value for -listenTimeout " ) ) ;
}
2023-11-29 18:47:11 -05:00
else if ( name . Equals ( TC ( " -named " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -named needs a value " ) ) ;
named . Append ( value ) ;
}
else if ( name . Equals ( TC ( " -log " ) ) )
{
logToFile = true ;
}
else if ( name . Equals ( TC ( " -quiet " ) ) )
{
quiet = true ;
}
else if ( name . Equals ( TC ( " -nocustomalloc " ) ) )
{
disableCustomAllocator = true ;
}
else if ( name . Equals ( TC ( " -storeraw " ) ) )
{
storeCompressed = false ;
}
else if ( name . Equals ( TC ( " -sendraw " ) ) )
{
sendCompressed = false ;
}
else if ( name . Equals ( TC ( " -sendsize " ) ) )
{
if ( ! value . Parse ( sendSize ) )
return PrintHelp ( TC ( " Invalid value for -sendsize " ) ) ;
}
else if ( name . Equals ( TC ( " -dir " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -dir needs a value " ) ) ;
2024-08-21 03:00:44 -04:00
if ( ! ExpandEnvironmentVariables ( value ) )
return false ;
2023-12-09 03:11:34 -05:00
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 " ) , value . data ) . data ) ;
2023-11-29 18:47:11 -05:00
}
else if ( name . Equals ( TC ( " -name " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -name needs a value " ) ) ;
agentName . Append ( value ) ;
}
else if ( name . Equals ( TC ( " -nopoll " ) ) )
{
poll = false ;
}
else if ( name . Equals ( TC ( " -nostore " ) ) )
{
useStorage = false ;
}
2023-11-30 03:08:02 -05:00
else if ( name . Equals ( TC ( " -resetstore " ) ) )
{
resetStore = true ;
}
2023-11-29 18:47:11 -05:00
else if ( name . Equals ( TC ( " -binasversion " ) ) )
{
useBinariesAsVersion = true ;
}
else if ( name . Equals ( TC ( " -quic " ) ) )
{
# if !UBA_USE_QUIC
return PrintHelp ( TC ( " -quic not supported. Quic is not compiled into this binary " ) ) ;
# endif
useQuic = true ;
}
else if ( name . Equals ( TC ( " -maxidle " ) ) )
{
if ( ! value . Parse ( maxIdleSeconds ) )
return PrintHelp ( TC ( " Invalid value for -maxidle " ) ) ;
}
else if ( name . Equals ( TC ( " -proxyport " ) ) )
{
if ( ! value . Parse ( proxyPort ) )
return PrintHelp ( TC ( " Invalid value for -proxyport " ) ) ;
}
else if ( name . Equals ( TC ( " -summary " ) ) )
{
printSummary = true ;
}
else if ( name . Equals ( TC ( " -eventfile " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -eventfile needs a value " ) ) ;
2024-08-21 03:00:44 -04:00
if ( ! ExpandEnvironmentVariables ( value ) )
return false ;
2023-12-09 03:11:34 -05:00
if ( ( eventFile . count = GetFullPathNameW ( value . Replace ( ' \\ ' , PathSeparator ) . data , eventFile . capacity , eventFile . data , nullptr ) ) = = 0 )
return PrintHelp ( StringBuffer < > ( ) . Appendf ( TC ( " -eventfile has invalid path %s " ) , value . data ) . data ) ;
2023-11-29 18:47:11 -05:00
}
else if ( name . Equals ( TC ( " -killrandom " ) ) )
{
killRandom = true ;
}
else if ( name . Equals ( TC ( " -memwait " ) ) )
{
if ( ! value . Parse ( memWaitLoadPercent ) | | memWaitLoadPercent > 100 )
return PrintHelp ( TC ( " Invalid value for -memwait " ) ) ;
}
else if ( name . Equals ( TC ( " -memkill " ) ) )
{
if ( ! value . Parse ( memKillLoadPercent ) | | memKillLoadPercent > 100 )
return PrintHelp ( TC ( " Invalid value for -memkill " ) ) ;
}
else if ( name . Equals ( TC ( " -crypto " ) ) )
{
if ( value . count ! = 32 )
return PrintHelp ( TC ( " Invalid number of characters in crypto string. Should be 32 " ) ) ;
( ( u64 * ) crypto ) [ 0 ] = StringToValue ( value . data , 16 ) ;
( ( u64 * ) crypto ) [ 1 ] = StringToValue ( value . data + 16 , 16 ) ;
hasCrypto = true ;
}
2023-12-05 16:21:36 -05:00
else if ( name . Equals ( TC ( " -populateCas " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -populateCas needs a dir " ) ) ;
populateCasDirs . push_back ( value . data ) ;
}
2023-12-13 01:13:24 -05:00
# if PLATFORM_MAC
2023-12-14 17:22:19 -05:00
else if ( name . Equals ( TC ( " -populateCasFromXcodeVersion " ) ) )
2023-12-13 01:13:24 -05:00
{
2023-12-14 17:22:19 -05:00
populateCasFromXcodeVersion . Append ( value . data ) ;
}
else if ( name . Equals ( TC ( " -populateCasFromAllXcodes " ) ) )
{
populateCasFromAllXcodes = true ;
2023-12-13 01:13:24 -05:00
}
# endif
2023-11-29 18:47:11 -05:00
else if ( name . Equals ( TC ( " -sentry " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -sentry needs a url value " ) ) ;
sentryUrl . Append ( value ) ;
}
else if ( name . Equals ( TC ( " -zone " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -zone needs a value " ) ) ;
zone . Append ( value ) ;
}
2024-08-29 17:30:46 -04:00
else if ( name . Equals ( TC ( " -noproxy " ) ) )
{
allowProxy = false ;
}
2024-03-08 18:31:48 -05:00
else if ( name . Equals ( TC ( " -command " ) ) )
{
if ( value . IsEmpty ( ) )
return PrintHelp ( TC ( " -command needs a value " ) ) ;
command = value . data ;
poll = false ;
quiet = true ;
}
2023-11-29 18:47:11 -05:00
else if ( name . Equals ( TC ( " -? " ) ) )
{
return PrintHelp ( TC ( " " ) ) ;
}
else if ( relaunchPath . IsEmpty ( ) )
{
StringBuffer < > msg ;
msg . Appendf ( TC ( " Unknown argument '%s' " ) , name . data ) ;
return PrintHelp ( msg . data ) ;
}
}
if ( ! named . IsEmpty ( ) ) // We only run once with named connection
poll = false ;
maxProcessCount = u32 ( float ( maxProcessCount ) * mulProcessValue ) ;
if ( poll ) // no point disconnect on idle since agent will just reconnect immediately again
maxIdleSeconds = ~ 0u ;
if ( memKillLoadPercent < memWaitLoadPercent )
memKillLoadPercent = memWaitLoadPercent ;
FilteredLogWriter logWriter ( g_consoleLogWriter , verbose ? LogEntryType_Debug : 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 ( ) ; } ) ;
2023-11-29 18:47:11 -05:00
# if UBA_AUTO_UPDATE
if ( waitProcessId ! = ~ 0u )
if ( ! WaitForProcess ( waitProcessId ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
if ( relaunchPath . count )
return LaunchReal ( logger , relaunchPath , argc , argv ) ;
# endif // UBA_AUTO_UPDATE
if ( host . IsEmpty ( ) & & named . IsEmpty ( ) & & ! useListen )
return PrintHelp ( TC ( " No host provided. Add -host=<host> (or use -listen) " )) ;
StringBuffer < 256 > extraInfo ;
# if defined(UBA_USE_SENTRY)
if ( ! sentryUrl . IsEmpty ( ) )
{
char release [ 128 ] ;
char url [ 512 ] ;
size_t urlLen ;
2024-02-07 20:36:18 -05:00
sprintf_s ( release , sizeof_array ( release ) , " BoxAgent@%ls " , Version ) ;
2023-11-29 18:47:11 -05:00
wcstombs_s ( & urlLen , url , sizeof_array ( url ) , sentryUrl . data , sizeof_array ( url ) - 1 ) ;
sentry_options_t * options = sentry_options_new ( ) ;
sentry_options_set_dsn ( options , url ) ;
sentry_options_set_database_path ( options , " .sentry-native " ) ;
sentry_options_set_release ( options , release ) ;
//sentry_options_set_debug(options, 1);
sentry_init ( options ) ;
extraInfo . Append ( TC ( " , SentryEnabled " ) ) ;
}
auto sentryGuard = MakeGuard ( [ & ] ( ) { if ( ! sentryUrl . IsEmpty ( ) ) sentry_close ( ) ; } ) ;
# endif
// Check if AWS
2023-12-09 03:11:34 -05:00
# if UBA_USE_AWS
2023-11-29 18:47:11 -05:00
AWS aws ;
2023-12-09 03:11:34 -05:00
{
DirectoryCache dirCache ;
dirCache . CreateDirectory ( logger , g_rootDir . data ) ;
aws . QueryInformation ( logger , extraInfo , g_rootDir . data ) ;
if ( zone . IsEmpty ( ) )
zone . Append ( aws . GetAvailabilityZone ( ) ) ;
}
2023-11-29 18:47:11 -05:00
# endif
2024-02-08 11:38:00 -05:00
if ( ! zone . count )
2024-08-25 01:23:53 -04:00
GetZone ( zone ) ;
2024-02-08 11:38:00 -05:00
2023-12-05 16:21:36 -05:00
if ( zone . count )
extraInfo . Append ( TC ( " , " ) ) . Append ( zone ) ;
2023-11-29 18:47:11 -05:00
if ( IsRunningWine ( ) )
extraInfo . Append ( TC ( " , Linux/WINE " ) ) ;
if ( useQuic )
extraInfo . Append ( TC ( " , MsQuic " ) ) ;
if ( hasCrypto )
extraInfo . Append ( TC ( " , Encrypted " ) ) ;
//logger.Info(TC("\033[39m\n"));
const tchar * dbgStr = TC ( " " ) ;
# if UBA_DEBUG
dbgStr = TC ( " (DEBUG) " ) ;
# endif
2024-02-13 23:53:47 -05:00
logger . Info ( TC ( " UbaAgent v%s%s (Cpu: %u, MaxCon: %u, Dir: \" %s \" , StoreCapacity: %uGb%s) " ) , Version , dbgStr , maxProcessCount , maxConnectionCount , g_rootDir . data , storageCapacityGb , extraInfo . data ) ;
2024-06-15 01:11:17 -04:00
Config config ;
if ( ! configFile . IsEmpty ( ) )
config . LoadFromFile ( logger , configFile . data ) ;
2023-11-29 18:47:11 -05:00
if ( ! eventFile . IsEmpty ( ) )
logger . Info ( TC ( " Will poll for external events in file %s " ) , eventFile . data ) ;
logger . Info ( TC ( " " ) ) ;
# if PLATFORM_WINDOWS
{
StringBuffer < 256 > consoleTitle ;
2024-02-07 20:36:18 -05:00
consoleTitle . Appendf ( TC ( " UbaAgent v%s%s " ) , Version , dbgStr ) ;
2023-11-29 18:47:11 -05:00
SetConsoleTitleW ( consoleTitle . data ) ;
}
# endif
u64 storageCapacity = u64 ( storageCapacityGb ) * 1000 * 1000 * 1000 ;
2024-03-08 18:31:48 -05:00
if ( command . empty ( ) )
2023-11-29 18:47:11 -05:00
{
// Create a uba storage quickly just to fix non-graceful shutdowns
StorageCreateInfo info ( g_rootDir . data , logWriter ) ;
2024-06-15 01:11:17 -04:00
info . Apply ( config ) ;
2023-11-29 18:47:11 -05:00
info . rootDir = g_rootDir . data ;
info . casCapacityBytes = storageCapacity ;
info . storeCompressed = storeCompressed ;
StorageImpl storage ( info ) ;
2023-11-30 03:08:02 -05:00
if ( resetStore )
{
if ( ! storage . Reset ( ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-30 03:08:02 -05:00
}
else if ( ! storage . LoadCasTable ( false ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
}
2024-08-21 03:00:44 -04:00
StringBuffer < 512 > terminationReason ;
2023-12-13 01:13:24 -05:00
# if PLATFORM_MAC
2023-12-14 17:22:19 -05:00
Vector < TString > xcodeDirectories ;
if ( populateCasFromXcodeVersion . count > 0 | | populateCasFromAllXcodes )
2023-12-13 01:13:24 -05:00
{
2023-12-14 17:22:19 -05:00
// look for all xcodes in /Applications (is there a function to get Applications dir location for other locales?)
StringBuffer < > applicationsDir ;
applicationsDir . Append ( " /Applications " ) ;
TraverseDir ( logger , applicationsDir . data ,
[ & ] ( const DirectoryEntry & e )
{
if ( IsDirectory ( e . attributes ) & & StartsWith ( e . name , " Xcode " ) )
{
StringBuffer < 128 > xcodeDir ( " /Applications/ " ) ;
xcodeDir . Append ( e . name ) . Append ( " /Contents/Developer/ " ) ;
if ( FileExists ( logger , xcodeDir . data ) )
{
if ( populateCasFromAllXcodes )
{
xcodeDirectories . push_back ( xcodeDir . data ) ;
}
else
{
StringBuffer < 512 > command ;
StringBuffer < 32 > xcodeVer ;
// look for short version like 15.1 or 15, or BuildVersion like 15C610
bool bUseShortVersion = ( populateCasFromXcodeVersion . Contains ( ' . ' ) ) | | populateCasFromXcodeVersion . count < = 3 ;
const char * key = bUseShortVersion ? " CFBundleShortVersionString " : " ProductBuildVersion " ;
command . Append ( " /usr/bin/defaults read /Applications/ " ) . Append ( e . name ) . Append ( " /Contents/version.plist " ) . Append ( key ) ;
FILE * getver = popen ( command . data , " r " ) ;
if ( getver = = nullptr | | fgets ( xcodeVer . data , xcodeVer . capacity , getver ) = = nullptr )
{
pclose ( getver ) ;
logger . Error ( " Failed to get DTXcodeBuild from /Applications/%s " , e . name ) ;
return ;
}
pclose ( getver ) ;
xcodeVer . count = strlen ( xcodeVer . data ) ;
while ( isspace ( xcodeVer . data [ xcodeVer . count - 1 ] ) )
{
xcodeVer . data [ xcodeVer . count - 1 ] = 0 ;
xcodeVer . count - - ;
}
logger . Info ( " /Applications/%s has version '%s' (looking for %s) " , e . name , xcodeVer . data , populateCasFromXcodeVersion . data ) ;
if ( xcodeVer . Equals ( populateCasFromXcodeVersion . data ) )
{
xcodeDirectories . push_back ( xcodeDir . data ) ;
}
}
}
}
} ) ;
}
// if we didn't want a single version, or all xcodes, then use active xcode (useful for user running their own agents)
else
{
2023-12-14 18:08:38 -05:00
StringBuffer < 512 > xcodeSelectOutput ;
2023-12-14 17:22:19 -05:00
FILE * xcodeSelect = popen ( " /usr/bin/xcode-select -p " , " r " ) ;
if ( xcodeSelect = = nullptr | | fgets ( xcodeSelectOutput . data , xcodeSelectOutput . capacity , xcodeSelect ) = = nullptr | | pclose ( xcodeSelect ) ! = 0 )
2024-08-21 03:00:44 -04:00
terminationReason . Append ( " Failed to get an Xcode from xcode-select " ) ;
else
2023-12-13 01:13:24 -05:00
{
2024-08-21 03:00:44 -04:00
xcodeSelectOutput . count = strlen ( xcodeSelectOutput . data ) ;
while ( isspace ( xcodeSelectOutput . data [ xcodeSelectOutput . count - 1 ] ) )
xcodeSelectOutput . data [ - - xcodeSelectOutput . count ] = 0 ;
xcodeDirectories . push_back ( xcodeSelectOutput . data ) ;
2023-12-13 01:13:24 -05:00
}
2023-12-14 17:22:19 -05:00
}
2023-12-13 01:13:24 -05:00
2023-12-14 17:22:19 -05:00
if ( xcodeDirectories . size ( ) = = 0 )
2024-08-21 03:00:44 -04:00
terminationReason . Append ( " Unable to populate from any Xcodes. Agent is unusable. " ) ;
2023-12-14 17:22:19 -05:00
for ( TString & xcodeDir : xcodeDirectories )
{
logger . Info ( " Populating cas with %s " , xcodeDir . data ( ) ) ;
const char * subDirs [ ] = { " /Toolchains " , " /Platforms " } ;
2023-12-13 01:13:24 -05:00
for ( auto subDir : subDirs )
{
2023-12-14 17:22:19 -05:00
TString populateDir ( xcodeDir ) ;
populateDir . append ( subDir ) ;
populateCasDirs . push_back ( populateDir ) ;
2023-12-13 01:13:24 -05:00
}
}
# endif
2023-11-29 18:47:11 -05:00
Vector < ProcessLogLine > logLines [ 2 ] ;
u32 logLinesIndex = 0 ;
ReaderWriterLock logLinesLock ;
Event logLinesAvailable ( false ) ;
auto processFinished = [ & ] ( const ProcessHandle & process )
{
u32 errorCode = process . GetExitCode ( ) ;
if ( errorCode = = ProcessCancelExitCode )
return ;
const Vector < ProcessLogLine > & processLogLines = process . GetLogLines ( ) ;
if ( ! processLogLines . empty ( ) )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( logLinesLock , lock ) ;
2023-11-29 18:47:11 -05:00
for ( auto & line : processLogLines )
logLines [ logLinesIndex ] . push_back ( line ) ;
if ( errorCode )
{
StringBuffer < > errorMsg ;
errorMsg . Appendf ( TC ( " (exit code: %u) " ) , errorCode ) ;
logLines [ logLinesIndex ] . back ( ) . text + = errorMsg . data ;
}
}
else
{
2024-06-15 01:11:17 -04:00
const TString & desc = process . GetStartInfo ( ) . GetDescription ( ) ;
2023-11-29 18:47:11 -05:00
StringBuffer < > name ;
if ( ! desc . empty ( ) )
name . Append ( desc ) ;
else
2024-07-07 15:08:15 -04:00
GenerateNameForProcess ( name , process . GetStartInfo ( ) . arguments , 0 ) ;
2023-11-29 18:47:11 -05:00
LogEntryType entryType = LogEntryType_Info ;
if ( errorCode )
{
name . Appendf ( TC ( " (exit code: %u) " ) , errorCode ) ;
entryType = LogEntryType_Error ;
}
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( logLinesLock , lock ) ;
2023-11-29 18:47:11 -05:00
logLines [ logLinesIndex ] . push_back ( { TString ( name . data ) , entryType } ) ;
}
logLinesAvailable . Set ( ) ;
} ;
# if PLATFORM_WINDOWS
SetConsoleCtrlHandler ( ConsoleHandler , TRUE ) ;
# else
signal ( SIGINT , ConsoleHandler ) ;
2024-05-02 12:53:20 -04:00
signal ( SIGTERM , ConsoleHandler ) ;
2023-11-29 18:47:11 -05:00
# endif
bool relaunch = false ;
u64 terminationTimeMs = 0 ;
2023-12-09 03:11:34 -05:00
# if UBA_USE_AWS
2023-11-29 18:47:11 -05:00
if ( aws . IsTerminating ( logger , terminationReason , terminationTimeMs ) )
{
LoggerWithWriter ( g_consoleLogWriter , TC ( " " ) ) . Info ( TC ( " %s. Exiting UbaAgent before starting session " ) , terminationReason . data ) ;
2024-08-21 03:00:44 -04:00
return true ;
2023-11-29 18:47:11 -05:00
}
# endif
bool isTerminating = false ;
do
{
// TODO: Revisit.. it could be that something stalls during startup so we can't have this timeout
//u32 receiveTimeoutSeconds = 60; // We are sending pings at 5 seconds cadence, so timeout if no data appears on recv socket within 60 seconds
u32 receiveTimeoutSeconds = 0 ;
2024-03-03 23:44:00 -05:00
NetworkBackendMemory networkBackendMem ( logWriter ) ;
2023-11-29 18:47:11 -05:00
NetworkBackend * networkBackend ;
# if UBA_USE_QUIC
if ( useQuic )
networkBackend = new NetworkBackendQuic ( logWriter ) ;
else
# endif
networkBackend = new NetworkBackendTcp ( logWriter ) ;
auto backendGuard = MakeGuard ( [ networkBackend ] ( ) { delete networkBackend ; } ) ;
NetworkClientCreateInfo ncci ( logWriter ) ;
2024-06-15 01:11:17 -04:00
//ncci.Apply(config);
2023-11-29 18:47:11 -05:00
ncci . sendSize = sendSize ;
ncci . receiveTimeoutSeconds = receiveTimeoutSeconds ;
2024-08-28 13:33:28 -04:00
ncci . workerCount = maxWorkerCount ;
2023-11-29 18:47:11 -05:00
if ( hasCrypto )
ncci . cryptoKey128 = crypto ;
bool ctorSuccess = true ;
NetworkClient * client = new NetworkClient ( ctorSuccess , ncci ) ;
2024-05-02 12:53:20 -04:00
auto csg = MakeGuard ( [ & ] ( ) { client - > Disconnect ( ) ; delete client ; } ) ;
2023-11-29 18:47:11 -05:00
if ( ! ctorSuccess )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
if ( useListen )
{
client - > StartListen ( * networkBackend , port ) ;
u64 startTime = GetTime ( ) ;
2023-12-14 13:31:47 -05:00
while ( ! client - > IsOrWasConnected ( 200 ) )
2023-11-29 18:47:11 -05:00
{
2024-05-02 12:53:20 -04:00
if ( ShouldExit ( ) )
2024-08-21 03:00:44 -04:00
return true ;
2023-11-29 18:47:11 -05:00
u64 waitTime = GetTime ( ) - startTime ;
2023-12-14 13:08:17 -05:00
if ( ! poll & & TimeToMs ( waitTime ) > listenTimeoutSec * 1000 )
2024-08-21 03:00:44 -04:00
return logger . Error ( TC ( " Failed to get connection while listening for %s " ) , TimeToText ( waitTime ) . str ) ;
2023-11-29 18:47:11 -05:00
}
}
else
{
logger . Info ( TC ( " Waiting to connect to %s:%u " ) , host . data , port ) ;
int retryCount = 5 ;
u64 startTime = GetTime ( ) ;
bool timedOut = false ;
while ( ! client - > Connect ( * networkBackend , host . data , port , & timedOut ) )
{
2024-05-02 12:53:20 -04:00
if ( ShouldExit ( ) )
2024-08-21 03:00:44 -04:00
return true ;
2024-05-02 12:53:20 -04:00
2023-11-29 18:47:11 -05:00
if ( ! timedOut )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
if ( ! poll & & ! - - retryCount )
2024-08-21 03:00:44 -04:00
return logger . Error ( TC ( " Failed to connect to %s:%u (after %s) " ) , host . data , port , TimeToText ( GetTime ( ) - startTime ) . str ) ;
2023-11-29 18:47:11 -05:00
}
}
2024-03-08 18:31:48 -05:00
if ( ! command . empty ( ) )
{
StackBinaryWriter < 128 > writer ;
NetworkMessage msg ( * client , SessionServiceId , SessionMessageType_Command , writer ) ;
writer . WriteString ( command ) ;
StackBinaryReader < 8 * 1024 > reader ;
if ( ! msg . Send ( reader ) )
2024-08-21 03:00:44 -04:00
return logger . Error ( TC ( " Failed to send command to host " ) ) ;
2024-03-08 18:31:48 -05:00
LoggerWithWriter commandLogger ( g_consoleLogWriter , TC ( " " ) ) ;
commandLogger . Info ( TC ( " ---------------------------------- " ) ) ;
while ( true )
{
auto logType = ( LogEntryType ) reader . ReadByte ( ) ;
if ( logType = = 255 )
break ;
TString result = reader . ReadString ( ) ;
commandLogger . Log ( logType , result . c_str ( ) , u32 ( result . size ( ) ) ) ;
}
commandLogger . Info ( TC ( " ---------------------------------- " ) ) ;
2024-08-21 03:00:44 -04:00
return true ;
2024-03-08 18:31:48 -05:00
}
2023-11-29 18:47:11 -05:00
Event wakeupSessionWait ( false ) ;
2024-02-21 16:49:26 -05:00
Atomic < u32 > targetConnectionCount = 1 ;
2023-11-29 18:47:11 -05:00
struct Proxy
{
LogWriter & logWriter ;
2024-03-03 23:44:00 -05:00
NetworkBackend & networkBackend ;
NetworkBackend & networkBackendMem ;
2023-11-29 18:47:11 -05:00
NetworkClient * client ;
Event & wakeupSessionWait ;
2024-02-13 23:53:47 -05:00
u32 & maxConnectionCount ;
2024-02-21 16:49:26 -05:00
Atomic < u32 > & targetConnectionCount ;
2024-03-08 18:31:48 -05:00
Atomic < NetworkServer * > server ;
2023-11-29 18:47:11 -05:00
StorageProxy * storage = nullptr ;
2024-01-17 15:28:30 -05:00
StorageClient * storageClient = nullptr ;
2023-11-29 18:47:11 -05:00
TString serverPrefix ;
2024-03-03 23:44:00 -05:00
} proxy { g_consoleLogWriter , * networkBackend , networkBackendMem , client , wakeupSessionWait , maxConnectionCount , targetConnectionCount } ;
2023-11-29 18:47:11 -05:00
auto psg = MakeGuard ( [ & ] ( ) { delete proxy . server ; } ) ;
auto pg = MakeGuard ( [ & ] ( ) { delete proxy . storage ; } ) ;
static auto startProxy = [ ] ( void * userData , u16 proxyPort , const Guid & storageServerUid )
{
auto & proxy = * ( Proxy * ) userData ;
NetworkServerCreateInfo nsci ( proxy . logWriter ) ;
2024-02-26 01:37:10 -05:00
nsci . workerCount = 192 ;
2023-11-29 18:47:11 -05:00
nsci . receiveTimeoutSeconds = 60 ;
StringBuffer < 256 > prefix ;
prefix . Append ( TC ( " UbaProxyServer ( " ) ) . Append ( GuidToString ( proxy . client - > GetUid ( ) ) . str ) . Append ( ' ) ' ) ;
proxy . serverPrefix = prefix . data ;
bool ctorSuccess = true ;
2024-03-08 18:31:48 -05:00
auto proxyServer = new NetworkServer ( ctorSuccess , nsci , proxy . serverPrefix . c_str ( ) ) ;
2023-11-29 18:47:11 -05:00
if ( ! ctorSuccess )
{
2024-03-08 18:31:48 -05:00
delete proxyServer ;
2023-11-29 18:47:11 -05:00
return false ;
}
2024-03-03 23:44:00 -05:00
2024-03-08 18:31:48 -05:00
proxy . storage = new StorageProxy ( * proxyServer , * proxy . client , storageServerUid , TC ( " Wooohoo " ) , proxy . storageClient ) ;
2024-03-03 23:44:00 -05:00
2024-03-08 18:31:48 -05:00
proxyServer - > RegisterOnClientConnected ( 0 , [ p = & proxy ] ( const Guid & clientUid , u32 clientId ) { p - > wakeupSessionWait . Set ( ) ; } ) ;
proxyServer - > SetWorkTracker ( proxy . client - > GetWorkTracker ( ) ) ;
proxyServer - > StartListen ( proxy . networkBackendMem , proxyPort ) ;
proxyServer - > StartListen ( proxy . networkBackend , proxyPort ) ;
2024-02-13 23:53:47 -05:00
proxy . targetConnectionCount = proxy . maxConnectionCount ;
2024-03-03 23:44:00 -05:00
2024-03-08 18:31:48 -05:00
proxy . server = proxyServer ;
2023-11-29 18:47:11 -05:00
proxy . wakeupSessionWait . Set ( ) ;
return true ;
} ;
2024-08-21 19:56:16 -04:00
Atomic < bool > isDisconnected ;
client - > RegisterOnDisconnected ( [ & ] ( ) { isDisconnected = true ; networkBackend - > StopListen ( ) ; if ( auto proxyServer = proxy . server . load ( ) ) proxyServer - > DisconnectClients ( ) ; } ) ;
2024-03-08 18:31:48 -05:00
2024-03-03 23:44:00 -05:00
struct NetworkBackends
{
NetworkBackend & tcp ;
NetworkBackend & mem ;
} backends { * networkBackend , networkBackendMem } ;
static auto getProxyBackend = [ ] ( void * userData , const tchar * host ) - > NetworkBackend &
{
auto & backends = * ( NetworkBackends * ) userData ;
return Equals ( host , TC ( " inprocess " ) ) ? backends . mem : backends . tcp ;
} ;
2023-11-29 18:47:11 -05:00
StorageClientCreateInfo storageInfo ( * client , g_rootDir . data ) ;
2024-06-15 01:11:17 -04:00
storageInfo . Apply ( config ) ;
storageInfo . rootDir = g_rootDir . data ;
2023-11-29 18:47:11 -05:00
storageInfo . casCapacityBytes = storageCapacity ;
storageInfo . storeCompressed = storeCompressed ;
storageInfo . sendCompressed = sendCompressed ;
storageInfo . workManager = client ;
2024-03-03 23:44:00 -05:00
storageInfo . getProxyBackendCallback = getProxyBackend ;
storageInfo . getProxyBackendUserData = & backends ;
2024-08-29 17:30:46 -04:00
storageInfo . allowProxy = allowProxy ;
2023-11-29 18:47:11 -05:00
storageInfo . startProxyCallback = startProxy ;
storageInfo . startProxyUserData = & proxy ;
storageInfo . zone = zone . data ;
storageInfo . proxyPort = proxyPort ;
auto storageClient = new StorageClient ( storageInfo ) ;
2024-05-02 12:53:20 -04:00
auto bscsg = MakeGuard ( [ & ] ( ) { delete storageClient ; } ) ;
2023-11-29 18:47:11 -05:00
if ( ! storageClient - > LoadCasTable ( true ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
2024-01-17 15:28:30 -05:00
proxy . storageClient = storageClient ;
2023-11-29 18:47:11 -05:00
SessionClient * sessionClient = nullptr ;
CasKey keys [ 2 ] ;
client - > RegisterOnVersionMismatch ( [ & ] ( const CasKey & exeKey , const CasKey & dllKey )
{
keys [ 0 ] = exeKey ;
keys [ 1 ] = dllKey ;
} ) ;
SessionClientCreateInfo info ( * storageClient , * client , logWriter ) ;
2024-06-15 01:11:17 -04:00
info . Apply ( config ) ;
2023-11-29 18:47:11 -05:00
info . maxProcessCount = maxProcessCount ;
info . dedicated = poll ;
info . maxIdleSeconds = maxIdleSeconds ;
info . outputStatsThresholdMs = outputStatsThresholdMs ;
info . name . Append ( agentName ) ;
info . extraInfo = extraInfo . data ;
info . deleteSessionsOlderThanSeconds = 1 ; // Delete all old sessions
//if (!awsInstanceId.IsEmpty())
// info.name.Append(TC(" (")).Append(awsInstanceId).Append(TC(")"));
info . rootDir = g_rootDir . data ;
info . logToFile = logToFile ;
info . disableCustomAllocator = disableCustomAllocator ;
info . useBinariesAsVersion = useBinariesAsVersion ;
info . killRandom = killRandom ;
info . useStorage = useStorage ;
info . memWaitLoadPercent = u8 ( memWaitLoadPercent ) ;
info . memKillLoadPercent = u8 ( memKillLoadPercent ) ;
2024-02-21 01:43:26 -05:00
if ( ! quiet )
info . processFinished = processFinished ;
2023-11-29 18:47:11 -05:00
sessionClient = new SessionClient ( info ) ;
auto secsg = MakeGuard ( [ & ] ( ) { delete sessionClient ; } ) ;
Atomic < bool > loopLogging = true ;
Thread loggingThread ( [ & ] ( )
{
while ( loopLogging )
{
logLinesAvailable . IsSet ( ) ;
u32 logLinesIndexPrev ;
2024-02-26 01:14:42 -05:00
{
SCOPED_WRITE_LOCK ( logLinesLock , l ) ;
logLinesIndexPrev = logLinesIndex ;
logLinesIndex = ( logLinesIndex + 1 ) % 2 ;
}
2023-11-29 18:47:11 -05:00
logger . BeginScope ( ) ;
for ( auto & s : logLines [ logLinesIndexPrev ] )
logger . Log ( LogEntryType_Detail , s . text . c_str ( ) , u32 ( s . text . size ( ) ) ) ;
logger . EndScope ( ) ;
logLines [ logLinesIndexPrev ] . clear ( ) ;
}
return 0 ;
} ) ;
2024-05-02 12:53:20 -04:00
g_sessionClient = sessionClient ;
2023-11-29 18:47:11 -05:00
auto disconnectAndStopLoggingThread = MakeGuard ( [ & ] ( )
{
2024-05-02 12:53:20 -04:00
g_exitLock - > EnterWrite ( ) ;
g_sessionClient = nullptr ;
g_exitLock - > LeaveWrite ( ) ;
2024-03-08 18:31:48 -05:00
networkBackend - > StopListen ( ) ;
2023-11-29 18:47:11 -05:00
storageClient - > StopProxy ( ) ;
2024-03-08 18:31:48 -05:00
auto proxyServer = proxy . server . load ( ) ;
if ( proxyServer )
proxyServer - > DisconnectClients ( ) ;
2024-03-03 23:33:30 -05:00
sessionClient - > Stop ( ) ;
2024-03-08 18:31:48 -05:00
sessionClient - > SendSummary ( [ & ] ( Logger & logger ) { if ( proxyServer ) proxyServer - > PrintSummary ( logger ) ; } ) ;
client - > Disconnect ( ) ;
2024-05-02 12:53:20 -04:00
2023-11-29 18:47:11 -05:00
loopLogging = false ;
logLinesAvailable . Set ( ) ;
loggingThread . Wait ( ) ;
} ) ;
// We got version mismatch and have the cas keys for the needed Agent/Detours binaries
if ( keys [ 0 ] ! = CasKeyZero )
{
# if UBA_AUTO_UPDATE
logger . Info ( TC ( " Downloading new binaries... " ) ) ;
2024-05-02 12:53:20 -04:00
if ( ! DownloadBinaries ( * storageClient , keys ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
relaunch = true ;
break ;
# else
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
# endif
}
if ( quiet )
logger . Info ( TC ( " Client session %s started " ) , sessionClient - > GetId ( ) ) ;
else
logger . Info ( TC ( " ----------- Session %s started ----------- " ) , sessionClient - > GetId ( ) ) ;
2024-02-07 20:36:18 -05:00
#if 0
2023-11-29 18:47:11 -05:00
u64 lastLogTime = GetTime ( ) ;
2024-02-07 20:36:18 -05:00
# endif
2023-11-29 18:47:11 -05:00
2024-02-13 23:53:47 -05:00
u32 connectionCount = 1 ;
2023-11-29 18:47:11 -05:00
2024-03-03 23:33:30 -05:00
//#if PLATFORM_WINDOWS
//SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
//#endif
2024-08-21 19:56:16 -04:00
bool needPrepopulate = ! populateCasDirs . empty ( ) ;
if ( needPrepopulate )
sessionClient - > SetAllowSpawn ( false ) ;
2024-03-08 18:31:48 -05:00
storageClient - > Start ( ) ;
2024-03-03 23:33:30 -05:00
sessionClient - > Start ( ) ;
2024-08-21 19:56:16 -04:00
// We do population here to make sure session thread is running which will send pings to host (to prevent timeouts
if ( needPrepopulate )
{
if ( storageClient - > PopulateCasFromDirs ( populateCasDirs , maxProcessCount , [ & ] ( ) { return isDisconnected . load ( ) ; } ) )
sessionClient - > SetAllowSpawn ( true ) ;
else
terminationReason . Append ( TC ( " Failed to prepopulate cas from local directory " ) ) ;
}
2024-08-21 03:00:44 -04:00
if ( terminationReason . count )
{
isTerminating = true ;
sessionClient - > SetIsTerminating ( terminationReason . data , 0 ) ;
}
2024-05-02 12:53:20 -04:00
while ( ! ShouldExit ( ) )
2023-11-29 18:47:11 -05:00
{
if ( useListen )
{
2024-02-13 23:53:47 -05:00
if ( connectionCount ! = targetConnectionCount )
2023-11-29 18:47:11 -05:00
{
2024-02-13 23:53:47 -05:00
client - > SetConnectionCount ( targetConnectionCount ) ;
connectionCount = targetConnectionCount ;
2023-11-29 18:47:11 -05:00
}
}
else
{
2024-02-13 23:53:47 -05:00
while ( connectionCount < targetConnectionCount )
2023-11-29 18:47:11 -05:00
{
bool timedOut = false ;
if ( client - > Connect ( * networkBackend , host . data , port , & timedOut ) )
2024-02-13 23:53:47 -05:00
+ + connectionCount ;
2023-11-29 18:47:11 -05:00
}
}
if ( sessionClient - > Wait ( 5 * 1000 , & wakeupSessionWait ) )
break ;
2024-03-03 23:44:00 -05:00
// If we are the proxy server and have external connections we lower max process count. Note that it will always have one connection which is itself
2024-03-08 18:31:48 -05:00
if ( auto proxyServer = proxy . server . load ( ) )
if ( proxyServer - > GetConnectionCount ( ) > 1 )
sessionClient - > SetMaxProcessCount ( maxProcessCount - 2 ) ;
2024-03-03 23:44:00 -05:00
2023-11-29 18:47:11 -05:00
// This is an estimation based on tcp limitations (ack and sliding windows).
2024-02-13 23:53:47 -05:00
// For every 15ms latency on "best ping") we increase targetConnectionCount up to maxConnectionCount
2023-11-29 18:47:11 -05:00
if ( ! storageClient - > IsUsingProxy ( ) )
if ( u64 bestPing = sessionClient - > GetBestPing ( ) )
2024-02-13 23:53:47 -05:00
targetConnectionCount = Min ( u32 ( TimeToMs ( bestPing ) / 15 ) , maxConnectionCount ) ;
2023-11-29 18:47:11 -05:00
if ( ! isTerminating )
{
if ( IsTerminating ( logger , eventFile . data , terminationReason , terminationTimeMs ) )
isTerminating = true ;
2023-12-09 03:11:34 -05:00
# if UBA_USE_AWS
2023-11-29 18:47:11 -05:00
else if ( aws . IsTerminating ( logger , terminationReason , terminationTimeMs ) )
isTerminating = true ;
# endif
if ( isTerminating )
{
sessionClient - > SetIsTerminating ( terminationReason . data , terminationTimeMs ) ;
if ( quiet )
LoggerWithWriter ( g_consoleLogWriter , TC ( " " ) ) . Info ( TC ( " %s " ) , terminationReason . data ) ;
}
}
2024-02-07 20:36:18 -05:00
#if 0 // Not needed anymore I think.. horde is now pinging in the background for itself
2023-11-29 18:47:11 -05:00
if ( quiet )
{
u64 time = GetTime ( ) ;
u64 timePast = time - lastLogTime ;
if ( TimeToMs ( timePast ) > 90 * 1000 )
{
logger . Info ( TC ( " Break the silence after %s (here to keep horde happy...). Agent active process count: %u " ) , TimeToText ( timePast ) . str , sessionClient - > GetActiveProcessCount ( ) ) ;
lastLogTime = time ;
}
}
2024-02-07 20:36:18 -05:00
# endif
2023-11-29 18:47:11 -05:00
}
disconnectAndStopLoggingThread . Execute ( ) ;
if ( quiet )
{
logger . Info ( TC ( " Client session %s done " ) , sessionClient - > GetId ( ) ) ;
}
else
{
logger . BeginScope ( ) ;
if ( printSummary )
{
sessionClient - > PrintSummary ( logger ) ;
storageClient - > PrintSummary ( logger ) ;
client - > PrintSummary ( logger ) ;
2024-06-02 18:14:50 -04:00
KernelStats : : GetGlobal ( ) . Print ( logger , true ) ;
2023-11-29 18:47:11 -05:00
}
logger . Info ( TC ( " ----------- Session %s done! ----------- " ) , sessionClient - > GetId ( ) ) ;
logger . Info ( TC ( " " ) ) ;
logger . EndScope ( ) ;
}
2024-02-26 01:14:42 -05:00
//if (proxy.storage)
// proxy.storage->PrintSummary();
//if (proxy.server)
// proxy.server->PrintSummary(logger);
# if UBA_TRACK_CONTENTION
LoggerWithWriter contLogger ( g_consoleLogWriter , TC ( " " ) ) ;
PrintContentionSummary ( contLogger ) ;
# endif
2023-11-29 18:47:11 -05:00
}
2024-05-02 12:53:20 -04:00
while ( poll & & ! isTerminating & & ! ShouldExit ( ) ) ;
2023-11-29 18:47:11 -05:00
# if UBA_AUTO_UPDATE
if ( relaunch )
if ( ! LaunchTemp ( logger , argc , argv ) )
2024-08-21 03:00:44 -04:00
return false ;
2023-11-29 18:47:11 -05:00
# endif
2024-08-21 03:00:44 -04:00
return true ;
2023-11-29 18:47:11 -05:00
}
}
# if PLATFORM_WINDOWS
int wmain ( int argc , wchar_t * argv [ ] )
{
2024-03-03 23:33:30 -05:00
using namespace uba ;
__try
{
2024-08-21 03:00:44 -04:00
return WrappedMain ( argc , argv ) ? 0 : - 1 ;
2024-03-03 23:33:30 -05:00
}
__except ( ReportSEH ( GetExceptionInformation ( ) ) )
{
return - 1 ;
}
2023-11-29 18:47:11 -05:00
}
# else
int main ( int argc , char * argv [ ] )
{
2024-08-21 03:00:44 -04:00
return uba : : WrappedMain ( argc , argv ) ? 0 : - 1 ;
2023-11-29 18:47:11 -05:00
}
2023-12-14 17:22:19 -05:00
# endif