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