|
1 | 1 | #include "platform_util.h" |
2 | 2 |
|
| 3 | +#include <fstream> |
| 4 | + |
3 | 5 | // clang-format off |
4 | 6 | #if defined(__x86_64__) || defined(_M_X64) |
5 | 7 | # define DD_SDK_CPU_ARCH "x86_64" |
|
24 | 26 | # define DD_SDK_OS "GNU/Linux" |
25 | 27 | # define DD_SDK_KERNEL "Linux" |
26 | 28 | # include "string_util.h" |
| 29 | +# include <errno.h> |
27 | 30 | # include <fstream> |
| 31 | +# include <fcntl.h> |
28 | 32 | # include <sys/types.h> |
29 | 33 | # include <sys/mman.h> |
30 | | -# include <fcntl.h> |
31 | | -# include <errno.h> |
| 34 | +# include <sys/stat.h> |
| 35 | +# include <sys/statfs.h> |
32 | 36 | # endif |
33 | 37 | #elif defined(_MSC_VER) |
34 | 38 | # include <windows.h> |
@@ -102,7 +106,7 @@ std::tuple<std::string, std::string> get_windows_info() { |
102 | 106 | // application manifest, which is the lowest version supported by the |
103 | 107 | // application. Use `RtlGetVersion` to obtain the accurate OS version |
104 | 108 | // regardless of the manifest. |
105 | | - using RtlGetVersion = auto(*)(LPOSVERSIONINFOEXW)->NTSTATUS; |
| 109 | + using RtlGetVersion = auto (*)(LPOSVERSIONINFOEXW)->NTSTATUS; |
106 | 110 |
|
107 | 111 | RtlGetVersion func = |
108 | 112 | (RtlGetVersion)GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); |
@@ -281,5 +285,126 @@ Expected<InMemoryFile> InMemoryFile::make(StringView) { |
281 | 285 | } |
282 | 286 | #endif |
283 | 287 |
|
| 288 | +namespace container { |
| 289 | +namespace { |
| 290 | +#if defined(__linux__) || defined(__unix__) |
| 291 | +/// Magic numbers from linux/magic.h: |
| 292 | +/// <https://github.com/torvalds/linux/blob/ca91b9500108d4cf083a635c2e11c884d5dd20ea/include/uapi/linux/magic.h#L71> |
| 293 | +constexpr uint64_t CGROUP_SUPER_MAGIC = 0x27e0eb; |
| 294 | +constexpr uint64_t CGROUP2_SUPER_MAGIC = 0x63677270; |
| 295 | + |
| 296 | +/// Magic number from linux/proc_ns.h: |
| 297 | +/// <https://github.com/torvalds/linux/blob/5859a2b1991101d6b978f3feb5325dad39421f29/include/linux/proc_ns.h#L41-L49> |
| 298 | +constexpr ino_t HOST_CGROUP_NAMESPACE_INODE = 0xeffffffb; |
| 299 | + |
| 300 | +/// Represents the cgroup version of the current process. |
| 301 | +enum class Cgroup : char { v1, v2 }; |
| 302 | + |
| 303 | +Optional<ino_t> get_inode(std::string_view path) { |
| 304 | + struct stat buf; |
| 305 | + if (stat(path.data(), &buf) != 0) { |
| 306 | + return nullopt; |
| 307 | + } |
| 308 | + |
| 309 | + return buf.st_ino; |
| 310 | +} |
| 311 | + |
| 312 | +// Host namespace inode number are hardcoded, which allows for dectection of |
| 313 | +// whether the binary is running in host or not. However, it does not work when |
| 314 | +// running in a Docker in Docker environment. |
| 315 | +bool is_running_in_host_namespace() { |
| 316 | + if (auto inode = get_inode("/proc/self/ns/cgroup")) { |
| 317 | + return *inode == HOST_CGROUP_NAMESPACE_INODE; |
| 318 | + } |
| 319 | + |
| 320 | + return false; |
| 321 | +} |
| 322 | + |
| 323 | +Optional<Cgroup> get_cgroup_version() { |
| 324 | + struct statfs buf; |
| 325 | + |
| 326 | + if (statfs("/sys/fs/cgroup", &buf) != 0) { |
| 327 | + return nullopt; |
| 328 | + } |
| 329 | + |
| 330 | + if (buf.f_type == CGROUP_SUPER_MAGIC) |
| 331 | + return Cgroup::v1; |
| 332 | + else if (buf.f_type == CGROUP2_SUPER_MAGIC) |
| 333 | + return Cgroup::v2; |
| 334 | + |
| 335 | + return nullopt; |
| 336 | +} |
| 337 | + |
| 338 | +Optional<std::string> find_docker_container_id_from_cgroup() { |
| 339 | + constexpr std::string_view cgroup_path = "/proc/self/cgroup"; |
| 340 | + |
| 341 | + auto cgroup_fd = std::ifstream(cgroup_path.data(), std::ios::in); |
| 342 | + if (!cgroup_fd.is_open()) return nullopt; |
| 343 | + |
| 344 | + return find_docker_container_id(cgroup_fd); |
| 345 | +} |
| 346 | +#endif |
| 347 | +} // namespace |
| 348 | + |
| 349 | +Optional<std::string> find_docker_container_id(std::istream& source) { |
| 350 | + constexpr std::string_view docker_str = "docker-"; |
| 351 | + |
| 352 | + std::string line; |
| 353 | + while (std::getline(source, line)) { |
| 354 | + // Example: |
| 355 | + // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` |
| 356 | + if (auto beg = line.find(docker_str); beg != std::string::npos) { |
| 357 | + beg += docker_str.size(); |
| 358 | + auto end = line.find(".scope", beg); |
| 359 | + if (end == std::string::npos || end - beg <= 0) { |
| 360 | + continue; |
| 361 | + } |
| 362 | + |
| 363 | + auto container_id = line.substr(beg, end - beg); |
| 364 | + return container_id; |
| 365 | + } |
| 366 | + } |
| 367 | + |
| 368 | + return nullopt; |
| 369 | +} |
| 370 | + |
| 371 | +Optional<ContainerID> get_id() { |
| 372 | +#if defined(__linux__) || defined(__unix__) |
| 373 | + if (is_running_in_host_namespace()) { |
| 374 | + // Not in a container, no need to continue. |
| 375 | + return nullopt; |
| 376 | + } |
| 377 | + |
| 378 | + auto maybe_cgroup = get_cgroup_version(); |
| 379 | + if (!maybe_cgroup) return nullopt; |
| 380 | + |
| 381 | + ContainerID id; |
| 382 | + switch (*maybe_cgroup) { |
| 383 | + case Cgroup::v1: { |
| 384 | + if (auto maybe_id = find_docker_container_id_from_cgroup()) { |
| 385 | + id.value = *maybe_id; |
| 386 | + id.type = ContainerID::Type::container_id; |
| 387 | + break; |
| 388 | + } |
| 389 | + } |
| 390 | + // NOTE(@dmehala): failed to find the container ID, try getting the cgroup |
| 391 | + // inode. |
| 392 | + [[fallthrough]]; |
| 393 | + case Cgroup::v2: { |
| 394 | + if (auto maybe_inode = get_inode("/sys/fs/cgroup")) { |
| 395 | + id.type = ContainerID::Type::cgroup_inode; |
| 396 | + id.value = std::to_string(*maybe_inode); |
| 397 | + } |
| 398 | + }; break; |
| 399 | + } |
| 400 | + |
| 401 | + return id; |
| 402 | +#else |
| 403 | + return nullopt; |
| 404 | +#endif |
| 405 | +} |
| 406 | + |
| 407 | +} // namespace container |
| 408 | + |
284 | 409 | } // namespace tracing |
285 | 410 | } // namespace datadog |
0 commit comments