bool Tick(float DeltaTime) { if (!FPlatformProcess::IsProcRunning(CurrentlyRunningCrashReporter)) { FPlatformProcess::CloseProc(CurrentlyRunningCrashReporter); CurrentlyRunningCrashReporter = FProcHandle(); FTicker::GetCoreTicker().RemoveTicker(CurrentTicker); CurrentTicker.Reset(); UE_LOG(LogLinux, Log, TEXT("Done sending crash report for ensure().")); return false; } // tick again return true; }
FProcHandle FDesktopPlatformBase::InvokeUnrealBuildToolAsync(const FString& InCmdLineParams, FOutputDevice &Ar, void*& OutReadPipe, void*& OutWritePipe, bool bSkipBuildUBT) { FString CmdLineParams = InCmdLineParams; #if PLATFORM_WINDOWS #if _MSC_VER >= 1900 CmdLineParams += TEXT(" -2015"); #elif _MSC_VER >= 1800 CmdLineParams += TEXT(" -2013"); #else CmdLineParams += TEXT(" -2012"); #endif #endif // PLATFORM_WINDOWS // UnrealBuildTool is currently always located in the Binaries/DotNET folder FString ExecutableFileName = GetUnrealBuildToolExecutableFilename(FPaths::RootDir()); // Rocket never builds UBT, UnrealBuildTool should already exist bool bSkipBuild = FApp::IsEngineInstalled() || bSkipBuildUBT; if (!bSkipBuild) { // When not using rocket, we should attempt to build UBT to make sure it is up to date // Only do this if we have not already successfully done it once during this session. static bool bSuccessfullyBuiltUBTOnce = false; if (!bSuccessfullyBuiltUBTOnce) { Ar.Log(TEXT("Building UnrealBuildTool...")); if (BuildUnrealBuildTool(FPaths::RootDir(), Ar)) { bSuccessfullyBuiltUBTOnce = true; } else { // Failed to build UBT Ar.Log(TEXT("Failed to build UnrealBuildTool.")); return FProcHandle(); } } } #if PLATFORM_LINUX CmdLineParams += (" -progress"); #endif // PLATFORM_LINUX Ar.Logf(TEXT("Launching UnrealBuildTool... [%s %s]"), *ExecutableFileName, *CmdLineParams); #if PLATFORM_MAC // On Mac we launch UBT with Mono FString ScriptPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir() / TEXT("Build/BatchFiles/Mac/RunMono.sh")); CmdLineParams = FString::Printf(TEXT("\"%s\" \"%s\" %s"), *ScriptPath, *ExecutableFileName, *CmdLineParams); ExecutableFileName = TEXT("/bin/sh"); #elif PLATFORM_LINUX // Real men run Linux (with Mono??) FString ScriptPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir() / TEXT("Build/BatchFiles/Linux/RunMono.sh")); CmdLineParams = FString::Printf(TEXT("\"%s\" \"%s\" %s"), *ScriptPath, *ExecutableFileName, *CmdLineParams); ExecutableFileName = TEXT("/bin/bash"); #endif // Run UnrealBuildTool const bool bLaunchDetached = false; const bool bLaunchHidden = true; const bool bLaunchReallyHidden = bLaunchHidden; FProcHandle ProcHandle = FPlatformProcess::CreateProc(*ExecutableFileName, *CmdLineParams, bLaunchDetached, bLaunchHidden, bLaunchReallyHidden, NULL, 0, NULL, OutWritePipe); if (!ProcHandle.IsValid()) { Ar.Logf(TEXT("Failed to launch Unreal Build Tool. (%s)"), *ExecutableFileName); } return ProcHandle; }
FProcHandle FWindowsPlatformProcess::CreateProc( const TCHAR* URL, const TCHAR* Parms, bool bLaunchDetached, bool bLaunchHidden, bool bLaunchReallyHidden, uint32* OutProcessID, int32 PriorityModifier, const TCHAR* OptionalWorkingDirectory, void* PipeWrite ) { //UE_LOG(LogWindows, Log, TEXT("CreateProc %s %s"), URL, Parms ); // initialize process attributes SECURITY_ATTRIBUTES Attr; Attr.nLength = sizeof(SECURITY_ATTRIBUTES); Attr.lpSecurityDescriptor = NULL; Attr.bInheritHandle = true; // initialize process creation flags uint32 CreateFlags = NORMAL_PRIORITY_CLASS; if (PriorityModifier < 0) { CreateFlags = (PriorityModifier == -1) ? BELOW_NORMAL_PRIORITY_CLASS : IDLE_PRIORITY_CLASS; } else if (PriorityModifier > 0) { CreateFlags = (PriorityModifier == 1) ? ABOVE_NORMAL_PRIORITY_CLASS : HIGH_PRIORITY_CLASS; } if (bLaunchDetached) { CreateFlags |= DETACHED_PROCESS; } // initialize window flags uint32 dwFlags = 0; uint16 ShowWindowFlags = SW_HIDE; if (bLaunchReallyHidden) { dwFlags = STARTF_USESHOWWINDOW; } else if (bLaunchHidden) { dwFlags = STARTF_USESHOWWINDOW; ShowWindowFlags = SW_SHOWMINNOACTIVE; } if (PipeWrite != nullptr) { dwFlags |= STARTF_USESTDHANDLES; } // initialize startup info STARTUPINFO StartupInfo = { sizeof(STARTUPINFO), NULL, NULL, NULL, (::DWORD)CW_USEDEFAULT, (::DWORD)CW_USEDEFAULT, (::DWORD)CW_USEDEFAULT, (::DWORD)CW_USEDEFAULT, (::DWORD)0, (::DWORD)0, (::DWORD)0, (::DWORD)dwFlags, ShowWindowFlags, 0, NULL, ::GetStdHandle(ProcessConstants::WIN_STD_INPUT_HANDLE), HANDLE(PipeWrite), HANDLE(PipeWrite) }; // create the child process FString CommandLine = FString::Printf(TEXT("\"%s\" %s"), URL, Parms); PROCESS_INFORMATION ProcInfo; if (!CreateProcess(NULL, CommandLine.GetCharArray().GetData(), &Attr, &Attr, true, (::DWORD)CreateFlags, NULL, OptionalWorkingDirectory, &StartupInfo, &ProcInfo)) { UE_LOG(LogWindows, Warning, TEXT("CreateProc failed (%u) %s %s"), ::GetLastError(), URL, Parms); if (OutProcessID != nullptr) { *OutProcessID = 0; } return FProcHandle(); } if (OutProcessID != nullptr) { *OutProcessID = ProcInfo.dwProcessId; } ::CloseHandle( ProcInfo.hThread ); return FProcHandle(ProcInfo.hProcess); }
FProcHandle FWindowsPlatformProcess::OpenProcess(uint32 ProcessID) { return FProcHandle(::OpenProcess(PROCESS_ALL_ACCESS, 0, ProcessID)); }
FProcHandle FLinuxPlatformProcess::CreateProc(const TCHAR* URL, const TCHAR* Parms, bool bLaunchDetached, bool bLaunchHidden, bool bLaunchReallyHidden, uint32* OutProcessID, int32 PriorityModifier, const TCHAR* OptionalWorkingDirectory, void* PipeWrite) { // @TODO bLaunchHidden bLaunchReallyHidden are not handled // We need an absolute path to executable FString ProcessPath = URL; if (*URL != TEXT('/')) { ProcessPath = FPaths::ConvertRelativePathToFull(ProcessPath); } if (!FPaths::FileExists(ProcessPath)) { return FProcHandle(); } FString Commandline = ProcessPath; Commandline += TEXT(" "); Commandline += Parms; UE_LOG(LogHAL, Verbose, TEXT("FLinuxPlatformProcess::CreateProc: '%s'"), *Commandline); TArray<FString> ArgvArray; int Argc = Commandline.ParseIntoArray(ArgvArray, TEXT(" "), true); char* Argv[PlatformProcessLimits::MaxArgvParameters + 1] = { NULL }; // last argument is NULL, hence +1 struct CleanupArgvOnExit { int Argc; char** Argv; // relying on it being long enough to hold Argc elements CleanupArgvOnExit( int InArgc, char *InArgv[] ) : Argc(InArgc) , Argv(InArgv) {} ~CleanupArgvOnExit() { for (int Idx = 0; Idx < Argc; ++Idx) { FMemory::Free(Argv[Idx]); } } } CleanupGuard(Argc, Argv); // make sure we do not lose arguments with spaces in them due to Commandline.ParseIntoArray breaking them apart above // @todo this code might need to be optimized somehow and integrated with main argument parser below it TArray<FString> NewArgvArray; if (Argc > 0) { if (Argc > PlatformProcessLimits::MaxArgvParameters) { UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: too many (%d) commandline arguments passed, will only pass %d"), Argc, PlatformProcessLimits::MaxArgvParameters); Argc = PlatformProcessLimits::MaxArgvParameters; } FString MultiPartArg; for (int32 Index = 0; Index < Argc; Index++) { if (MultiPartArg.IsEmpty()) { if ((ArgvArray[Index].StartsWith(TEXT("\"")) && !ArgvArray[Index].EndsWith(TEXT("\""))) // check for a starting quote but no ending quote, excludes quoted single arguments || (ArgvArray[Index].Contains(TEXT("=\"")) && !ArgvArray[Index].EndsWith(TEXT("\""))) // check for quote after =, but no ending quote, this gets arguments of the type -blah="string string string" || ArgvArray[Index].EndsWith(TEXT("=\""))) // check for ending quote after =, this gets arguments of the type -blah=" string string string " { MultiPartArg = ArgvArray[Index]; } else { if (ArgvArray[Index].Contains(TEXT("=\""))) { FString SingleArg = ArgvArray[Index]; SingleArg = SingleArg.Replace(TEXT("=\""), TEXT("=")); NewArgvArray.Add(SingleArg.TrimQuotes(NULL)); } else { NewArgvArray.Add(ArgvArray[Index].TrimQuotes(NULL)); } } } else { MultiPartArg += TEXT(" "); MultiPartArg += ArgvArray[Index]; if (ArgvArray[Index].EndsWith(TEXT("\""))) { if (MultiPartArg.StartsWith(TEXT("\""))) { NewArgvArray.Add(MultiPartArg.TrimQuotes(NULL)); } else { NewArgvArray.Add(MultiPartArg); } MultiPartArg.Empty(); } } } } // update Argc with the new argument count Argc = NewArgvArray.Num(); if (Argc > 0) // almost always, unless there's no program name { if (Argc > PlatformProcessLimits::MaxArgvParameters) { UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: too many (%d) commandline arguments passed, will only pass %d"), Argc, PlatformProcessLimits::MaxArgvParameters); Argc = PlatformProcessLimits::MaxArgvParameters; } for (int Idx = 0; Idx < Argc; ++Idx) { auto AnsiBuffer = StringCast<ANSICHAR>(*NewArgvArray[Idx]); const char* Ansi = AnsiBuffer.Get(); size_t AnsiSize = FCStringAnsi::Strlen(Ansi) + 1; check(AnsiSize); Argv[Idx] = reinterpret_cast< char* >( FMemory::Malloc(AnsiSize) ); check(Argv[Idx]); FCStringAnsi::Strncpy(Argv[Idx], Ansi, AnsiSize); } // last Argv should be NULL check(Argc <= PlatformProcessLimits::MaxArgvParameters + 1); Argv[Argc] = NULL; } extern char ** environ; // provided by libc pid_t ChildPid = -1; posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_init(&FileActions); if (PipeWrite) { const FPipeHandle* PipeWriteHandle = reinterpret_cast< const FPipeHandle* >(PipeWrite); posix_spawn_file_actions_adddup2(&FileActions, PipeWriteHandle->GetHandle(), STDOUT_FILENO); } int PosixSpawnErrNo = posix_spawn(&ChildPid, TCHAR_TO_ANSI(*ProcessPath), &FileActions, nullptr, Argv, environ); posix_spawn_file_actions_destroy(&FileActions); if (PosixSpawnErrNo != 0) { UE_LOG(LogHAL, Fatal, TEXT("FLinuxPlatformProcess::CreateProc: posix_spawn() failed (%d, %s)"), PosixSpawnErrNo, ANSI_TO_TCHAR(strerror(PosixSpawnErrNo))); return FProcHandle(); // produce knowingly invalid handle if for some reason Fatal log (above) returns } // renice the child (subject to race condition). // Why this instead of posix_spawn_setschedparam()? // Because posix_spawnattr priority is unusable under Linux due to min/max priority range being [0;0] for the default scheduler if (PriorityModifier != 0) { errno = 0; int TheirCurrentPrio = getpriority(PRIO_PROCESS, ChildPid); if (errno != 0) { int ErrNo = errno; UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: could not get child's priority, errno=%d (%s)"), ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)) ); // proceed anyway... TheirCurrentPrio = 0; } rlimit PrioLimits; int MaxPrio = 0; if (getrlimit(RLIMIT_NICE, &PrioLimits) == -1) { int ErrNo = errno; UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: could not get priority limits (RLIMIT_NICE), errno=%d (%s)"), ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)) ); // proceed anyway... } else { MaxPrio = PrioLimits.rlim_cur; } int NewPrio = TheirCurrentPrio; if (PriorityModifier > 0) { // decrease the nice value - will perhaps fail, it's up to the user to run with proper permissions NewPrio -= 10; } else { NewPrio += 10; } // cap to [RLIMIT_NICE, 19] NewPrio = FMath::Min(19, NewPrio); NewPrio = FMath::Max(MaxPrio, NewPrio); // MaxPrio is actually the _lowest_ numerically priority if (setpriority(PRIO_PROCESS, ChildPid, NewPrio) == -1) { int ErrNo = errno; UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: could not change child's priority (nice value) from %d to %d, errno=%d (%s)"), TheirCurrentPrio, NewPrio, ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)) ); } else { UE_LOG(LogHAL, Log, TEXT("Changed child's priority (nice value) to %d (change from %d)"), NewPrio, TheirCurrentPrio); } } else { UE_LOG(LogHAL, Log, TEXT("FLinuxPlatformProcess::CreateProc: spawned child %d"), ChildPid); } if (OutProcessID) { *OutProcessID = ChildPid; } // [RCL] 2015-03-11 @FIXME: is bLaunchDetached usable when determining whether we're in 'fire and forget' mode? This doesn't exactly match what bLaunchDetached is used for. return FProcHandle(new FProcState(ChildPid, bLaunchDetached)); }
FProcHandle FLinuxPlatformProcess::CreateProc(const TCHAR* URL, const TCHAR* Parms, bool bLaunchDetached, bool bLaunchHidden, bool bLaunchReallyHidden, uint32* OutProcessID, int32 PriorityModifier, const TCHAR* OptionalWorkingDirectory, void* PipeWrite) { // @TODO bLaunchHidden bLaunchReallyHidden are not handled // We need an absolute path to executable FString ProcessPath = URL; if (*URL != '/') { ProcessPath = FPaths::ConvertRelativePathToFull(ProcessPath); } if (!FPaths::FileExists(ProcessPath)) { return FProcHandle(); } FString Commandline = ProcessPath; Commandline += TEXT(" "); Commandline += Parms; UE_LOG(LogHAL, Verbose, TEXT("FLinuxPlatformProcess::CreateProc: '%s'"), *Commandline); TArray<FString> ArgvArray; int Argc = Commandline.ParseIntoArray(&ArgvArray, TEXT(" "), true); char* Argv[PlatformProcessLimits::MaxArgvParameters + 1] = { NULL }; // last argument is NULL, hence +1 struct CleanupArgvOnExit { int Argc; char** Argv; // relying on it being long enough to hold Argc elements CleanupArgvOnExit( int InArgc, char *InArgv[] ) : Argc(InArgc) , Argv(InArgv) {} ~CleanupArgvOnExit() { for (int Idx = 0; Idx < Argc; ++Idx) { FMemory::Free(Argv[Idx]); } } } CleanupGuard(Argc, Argv); // make sure we do not lose arguments with spaces in them due to Commandline.ParseIntoArray breaking them apart above // @todo this code might need to be optimized somehow and integrated with main argument parser below it TArray<FString> NewArgvArray; if (Argc > 0) { if (Argc > PlatformProcessLimits::MaxArgvParameters) { UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: too many (%d) commandline arguments passed, will only pass %d"), Argc, PlatformProcessLimits::MaxArgvParameters); Argc = PlatformProcessLimits::MaxArgvParameters; } FString MultiPartArg; for (int32 Index = 0; Index < Argc; Index++) { if (MultiPartArg.IsEmpty()) { if ((ArgvArray[Index].StartsWith(TEXT("\"")) && !ArgvArray[Index].EndsWith(TEXT("\""))) // check for a starting quote but no ending quote, excludes quoted single arguments || (ArgvArray[Index].Contains(TEXT("=\"")) && !ArgvArray[Index].EndsWith(TEXT("\""))) // check for quote after =, but no ending quote, this gets arguments of the type -blah="string string string" || ArgvArray[Index].EndsWith(TEXT("=\""))) // check for ending quote after =, this gets arguments of the type -blah=" string string string " { MultiPartArg = ArgvArray[Index]; } else { if (ArgvArray[Index].Contains(TEXT("=\""))) { FString SingleArg = ArgvArray[Index]; SingleArg = SingleArg.Replace(TEXT("=\""), TEXT("=")); NewArgvArray.Add(SingleArg.TrimQuotes(NULL)); } else { NewArgvArray.Add(ArgvArray[Index].TrimQuotes(NULL)); } } } else { MultiPartArg += TEXT(" "); MultiPartArg += ArgvArray[Index]; if (ArgvArray[Index].EndsWith(TEXT("\""))) { if (MultiPartArg.StartsWith(TEXT("\""))) { NewArgvArray.Add(MultiPartArg.TrimQuotes(NULL)); } else { NewArgvArray.Add(MultiPartArg); } MultiPartArg.Empty(); } } } } // update Argc with the new argument count Argc = NewArgvArray.Num(); if (Argc > 0) // almost always, unless there's no program name { if (Argc > PlatformProcessLimits::MaxArgvParameters) { UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: too many (%d) commandline arguments passed, will only pass %d"), Argc, PlatformProcessLimits::MaxArgvParameters); Argc = PlatformProcessLimits::MaxArgvParameters; } for (int Idx = 0; Idx < Argc; ++Idx) { auto AnsiBuffer = StringCast<ANSICHAR>(*NewArgvArray[Idx]); const char* Ansi = AnsiBuffer.Get(); size_t AnsiSize = FCStringAnsi::Strlen(Ansi) + 1; check(AnsiSize); Argv[Idx] = reinterpret_cast< char* >( FMemory::Malloc(AnsiSize) ); check(Argv[Idx]); FCStringAnsi::Strncpy(Argv[Idx], Ansi, AnsiSize); } // last Argv should be NULL check(Argc <= PlatformProcessLimits::MaxArgvParameters + 1); Argv[Argc] = NULL; } extern char ** environ; // provided by libc pid_t ChildPid = -1; posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_init(&FileActions); if (PipeWrite) { const FPipeHandle* PipeWriteHandle = reinterpret_cast< const FPipeHandle* >(PipeWrite); posix_spawn_file_actions_adddup2(&FileActions, PipeWriteHandle->GetHandle(), STDOUT_FILENO); } int ErrNo = posix_spawn(&ChildPid, TCHAR_TO_ANSI(*ProcessPath), &FileActions, NULL, Argv, environ); posix_spawn_file_actions_destroy(&FileActions); if (ErrNo != 0) { UE_LOG(LogHAL, Fatal, TEXT("FLinuxPlatformProcess::CreateProc: posix_spawn() failed (%d, %s)"), ErrNo, ANSI_TO_TCHAR(strerror(ErrNo))); return FProcHandle(); // produce knowingly invalid handle if for some reason Fatal log (above) returns } else { UE_LOG(LogHAL, Log, TEXT("FLinuxPlatformProcess::CreateProc: spawned child %d"), ChildPid); } if (OutProcessID) { *OutProcessID = ChildPid; } return FProcHandle( ChildPid ); }