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