omni/extras/ContainerHelper.h

File members: omni/extras/ContainerHelper.h

// Copyright (c) 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 "../../carb/Defines.h"

#if CARB_PLATFORM_LINUX
#    include <fcntl.h>
#    include <unistd.h>
#endif

namespace omni
{
namespace extras
{

#if CARB_PLATFORM_LINUX || defined(DOXYGEN_BUILD)

#    ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace detail
{

inline int readIntFromFile(const char* file) noexcept
{
    auto fd = ::open(file, O_RDONLY, 0);
    if (fd == -1)
    {
        return -1;
    }

    char buffer[64];
    auto size = CARB_RETRY_EINTR(::read(fd, buffer, sizeof(buffer) - 1));
    ::close(fd);

    if (size <= 0)
    {
        return -1;
    }

    buffer[size] = '\0';
    return std::atoi(buffer);
}

inline bool isRunningInContainer() noexcept
{
    FILE* fp;

    // first (and easiest) check is to check whether the `/.dockerenv` file exists.  This file
    // is not necessarily always present though.
    if (access("/.dockerenv", F_OK) == 0)
    {
        return true;
    }

    // a more reliable but more expensive check is to verify the control group of `init`.  If
    // running under docker, all of the entries will have a path that starts with `/docker` or
    // `/lxc` instead of just `/`.
    fp = fopen("/proc/1/cgroup", "r");

    if (fp != nullptr)
    {
        char line[256];

        while (fgets(line, CARB_COUNTOF(line), fp) != nullptr)
        {
            if (feof(fp) || ferror(fp))
                break;

            if (strstr(line, ":/docker") != nullptr || strstr(line, ":/lxc") != nullptr)
            {
                return true;
            }
        }

        fclose(fp);
    }

    return false;
}

} // namespace detail
#    endif

inline int getDockerCpuLimit() noexcept
{
    // See:
    // https://docs.docker.com/config/containers/resource_constraints/#cpu
    // https://engineering.squarespace.com/blog/2017/understanding-linux-container-scheduling
    const auto cfsQuota = detail::readIntFromFile("/sys/fs/cgroup/cpu/cpu.cfs_quota_us");
    const auto cfsPeriod = detail::readIntFromFile("/sys/fs/cgroup/cpu/cpu.cfs_period_us");
    if (cfsQuota > 0 && cfsPeriod > 0)
    {
        // Since we can have fractional CPUs, round up half a CPU to a whole CPU, but make sure we have an even number.
        return ::carb_max(1, (cfsQuota + (cfsPeriod / 2)) / cfsPeriod);
    }
    return -1;
}

inline bool isRunningInContainer()
{
    static bool s_inContainer = detail::isRunningInContainer();
    return s_inContainer;
}

#else

inline int getDockerCpuLimit() noexcept
{
    return -1;
}

inline bool isRunningInContainer() noexcept
{
    return false;
}

#endif

} // namespace extras
} // namespace omni