carb/launcher/ILauncher.h
File members: carb/launcher/ILauncher.h
// Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once
#include "../Interface.h"
#if CARB_PLATFORM_LINUX
# include <sys/prctl.h>
# include <sys/signal.h>
#endif
namespace carb
{
namespace launcher
{
// ****************************** structs, enums, and constants ***********************************
struct Process;
using ExitCode = int64_t;
using ProcessId = uint64_t;
#define OMNI_ILauncher_PRIpid PRIu64
constexpr ProcessId kBadId = ~0ull;
using OnProcessReadFn = void (*)(const void* data, size_t bytes, void* context);
constexpr size_t kDefaultProcessBufferSize = 1ull << 17;
using LauncherFlags = uint32_t;
constexpr LauncherFlags fLaunchFlagOpenStdin = 0x00000001;
constexpr LauncherFlags fLaunchFlagKillOnParentExit = 0x00000002;
constexpr LauncherFlags fLaunchFlagForce = 0x00000004;
constexpr LauncherFlags fLaunchFlagByteMode = 0x00000000;
constexpr LauncherFlags fLaunchFlagMessageMode = 0x00000008;
constexpr LauncherFlags fLaunchFlagNoInheritEnv = 0x00000010;
constexpr LauncherFlags fLaunchFlagAllowBadEnv = 0x00000020;
constexpr LauncherFlags fLaunchFlagScript = 0x00000040;
constexpr LauncherFlags fLaunchFlagNoStdOut = 0x00000080;
constexpr LauncherFlags fLaunchFlagNoStdErr = 0x00000100;
constexpr LauncherFlags fLaunchFlagNoStdStreams = fLaunchFlagNoStdOut | fLaunchFlagNoStdErr;
constexpr LauncherFlags fLaunchFlagAllowBadLog = 0x00000200;
constexpr LauncherFlags fLaunchFlagLaunchDetached = 0x00000400;
using WaitFlags = uint32_t;
constexpr WaitFlags fWaitFlagStdOutStream = 0x00000001;
constexpr WaitFlags fWaitFlagStdErrStream = 0x00000002;
constexpr WaitFlags fWaitFlagCloseStdOutStream = 0x00000004;
constexpr WaitFlags fWaitFlagCloseStdErrStream = 0x00000008;
constexpr WaitFlags fWaitFlagAnyStream = 0x00000010;
using KillFlags = uint32_t;
constexpr KillFlags fKillFlagKillChildProcesses = 0x00000001;
constexpr KillFlags fKillFlagForce = 0x00000002;
constexpr KillFlags fKillFlagFailOnDebugger = 0x00000004;
constexpr KillFlags fKillFlagSkipWait = 0x00000008;
constexpr size_t kNullTerminated = ~0ull;
constexpr ExitCode kStillActive = 0x8000000000000000ll;
constexpr uint64_t kInfiniteTimeout = ~0ull;
enum class KillStatus
{
eSuccess,
eDebuggerAttached,
eDebuggerFail,
eTerminateFailed,
eWaitFailed,
eInvalidParameter,
};
#if CARB_PLATFORM_WINDOWS
constexpr const char* const kInterpreterShellScript = "cmd /C";
constexpr const char* const kInterpreterShellScript2 = "cmd /C";
#else
constexpr const char* const kInterpreterShellScript = "sh";
constexpr const char* const kInterpreterShellScript2 = "bash";
#endif
constexpr const char* const kInterpreterPythonScript = "python";
constexpr const char* const kInterpreterPythonCommand = "python -c";
struct LaunchDesc
{
const char* const* argv = nullptr;
size_t argc = 0;
const char* path = nullptr;
OnProcessReadFn onReadStdout = nullptr;
OnProcessReadFn onReadStderr = nullptr;
void* readStdoutContext = nullptr;
void* readStderrContext = nullptr;
LauncherFlags flags = 0;
size_t bufferSize = kDefaultProcessBufferSize;
const char* const* env = nullptr;
size_t envCount = 0;
const char* interpreter = nullptr;
const char* stdoutLog = nullptr;
const char* stderrLog = nullptr;
void* ext = nullptr;
};
// ********************************** interface declaration ***************************************
struct ILauncher
{
CARB_PLUGIN_INTERFACE("carb::launcher::ILauncher", 1, 3)
Process*(CARB_ABI* launchProcess)(LaunchDesc& desc);
ProcessId launchProcessDetached(LaunchDesc& desc)
{
ProcessId id;
Process* proc;
// registering read callbacks is not allowed in a detached process since we'll be
// immediately destroying the handle before return. If there were set the child
// process would be killed on Linux if it ever tried to write to one of its streams.
// On both Windows and Linux having these as non-`nullptr` would also cause a lot
// of unnecessary additional work to be done both during and after the launch.
desc.onReadStderr = nullptr;
desc.onReadStdout = nullptr;
// add the 'launch detached' flag to ensure we don't need to wait on the child process
// on Linux. This is to avoid leaving a zombie process lying around.
desc.flags |= fLaunchFlagLaunchDetached;
proc = launchProcess(desc);
if (proc == nullptr)
return kBadId;
id = getProcessId(proc);
destroyProcessHandle(proc);
return id;
}
void(CARB_ABI* destroyProcessHandle)(Process* process);
ProcessId(CARB_ABI* getProcessId)(Process* process);
ExitCode(CARB_ABI* waitProcessExit)(Process* process, uint64_t timeout);
ExitCode(CARB_ABI* getProcessExitCode)(Process* process);
bool(CARB_ABI* writeProcessStdin)(Process* process, const void* data, size_t bytes);
void(CARB_ABI* closeProcessStdin)(Process* process);
void(CARB_ABI* killProcess)(Process* process, KillFlags flags);
inline bool isProcessActive(Process* process)
{
return getProcessExitCode(process) == kStillActive;
}
KillStatus(CARB_ABI* killProcessWithTimeout)(Process* process, KillFlags flags, uint64_t timeout);
bool(CARB_ABI* isDebuggerAttached)(Process* process);
bool(CARB_ABI* waitForStreamEnd)(Process* process, WaitFlags flags, uint64_t timeout);
};
inline void restoreParentDeathSignal(KillFlags flags = 0)
{
CARB_UNUSED(flags);
#if CARB_PLATFORM_LINUX
prctl(PR_SET_PDEATHSIG, (flags & fKillFlagForce) != 0 ? SIGKILL : SIGTERM);
#endif
}
} // namespace launcher
} // namespace carb