|
18 | 18 | package org.apache.commons.lang3; |
19 | 19 |
|
20 | 20 | import java.io.IOException; |
| 21 | +import java.nio.charset.Charset; |
21 | 22 | import java.nio.file.Files; |
22 | 23 | import java.nio.file.Paths; |
23 | | -import java.util.stream.Stream; |
| 24 | +import java.util.Arrays; |
24 | 25 |
|
25 | 26 | /** |
26 | 27 | * Helps query the runtime environment. |
|
30 | 31 | public class RuntimeEnvironment { |
31 | 32 |
|
32 | 33 | /** |
33 | | - * Tests whether the file at the given path string contains a specific line. |
| 34 | + * Tests whether the /proc/N/environ file at the given path string contains a specific line prefix. |
34 | 35 | * |
35 | | - * @param path The path to a file. |
36 | | - * @param line The line to find. |
37 | | - * @return whether the file at the given path string contains a specific line. |
| 36 | + * @param envVarFile The path to a /proc/N/environ file. |
| 37 | + * @param key The env var key to find. |
| 38 | + * @return value The env var value or null |
38 | 39 | */ |
39 | | - private static Boolean containsLine(final String path, final String line) { |
40 | | - try (Stream<String> stream = Files.lines(Paths.get(path))) { |
41 | | - return stream.anyMatch(test -> test.contains(line)); |
| 40 | + private static String getenv(final String envVarFile, final String key) { |
| 41 | + try { |
| 42 | + byte[] bytes = Files.readAllBytes(Paths.get(envVarFile)); |
| 43 | + String content = new String(bytes, Charset.defaultCharset()); |
| 44 | + // Split by null byte character |
| 45 | + String[] lines = content.split("\u0000"); |
| 46 | + String prefix = key + "="; |
| 47 | + return Arrays.stream(lines) |
| 48 | + .filter(line -> line.startsWith(prefix)) |
| 49 | + .map(line -> line.split("=", 2)) |
| 50 | + .map(keyValue -> keyValue[1]) |
| 51 | + .findFirst() |
| 52 | + .orElse(null); |
42 | 53 | } catch (final IOException e) { |
43 | | - return false; |
| 54 | + return null; |
44 | 55 | } |
45 | 56 | } |
46 | 57 |
|
47 | 58 | /** |
48 | 59 | * Tests whether we are running in a container like Docker or Podman. |
49 | 60 | * |
50 | | - * @return whether we are running in a container like Docker or Podman. |
| 61 | + * @return whether we are running in a container like Docker or Podman. Never null |
51 | 62 | */ |
52 | 63 | public static Boolean inContainer() { |
53 | | - return inDocker() || inPodman(); |
| 64 | + return inContainer(""); |
54 | 65 | } |
55 | 66 |
|
56 | | - /** |
57 | | - * Tests whether we are running in a Docker container. |
58 | | - * <p> |
59 | | - * Package-private for testing. |
60 | | - * </p> |
61 | | - * |
62 | | - * @return whether we are running in a Docker container. |
63 | | - */ |
64 | | - // Could be public at a later time. |
65 | | - static Boolean inDocker() { |
66 | | - return containsLine("/proc/1/cgroup", "/docker"); |
67 | | - } |
| 67 | + static boolean inContainer(final String dirPrefix) { |
| 68 | + /* |
| 69 | + Roughly follow the logic in SystemD: |
| 70 | + https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692 |
68 | 71 |
|
69 | | - /** |
70 | | - * Tests whether we are running in a Podman container. |
71 | | - * <p> |
72 | | - * Package-private for testing. |
73 | | - * </p> |
74 | | - * |
75 | | - * @return whether we are running in a Podman container. |
76 | | - */ |
77 | | - // Could be public at a later time. |
78 | | - static Boolean inPodman() { |
79 | | - return containsLine("/proc/1/environ", "container=podman"); |
| 72 | + We check the `container` environment variable of process 1: |
| 73 | + If the variable is empty, we return false. This includes the case, where the container developer wants to hide the fact that the application runs in a container. |
| 74 | + If the variable is not empty, we return true. |
| 75 | + If the variable is absent, we continue. |
| 76 | +
|
| 77 | + We check files in the container. According to SystemD: |
| 78 | + /.dockerenv is used by Docker. |
| 79 | + /run/.containerenv is used by PodMan. |
| 80 | +
|
| 81 | + */ |
| 82 | + String value = getenv(dirPrefix + "/proc/1/environ", "container"); |
| 83 | + if (value != null) { |
| 84 | + return !value.isEmpty(); |
| 85 | + } |
| 86 | + return fileExists(dirPrefix + "/.dockerenv") || fileExists(dirPrefix + "/run/.containerenv"); |
80 | 87 | } |
81 | 88 |
|
82 | | - /** |
83 | | - * Tests whether we are running in a Windows Subsystem for Linux (WSL). |
84 | | - * <p> |
85 | | - * Package-private for testing. |
86 | | - * </p> |
87 | | - * |
88 | | - * @return whether we are running in a Windows Subsystem for Linux (WSL). |
89 | | - */ |
90 | | - // Could be public at a later time. |
91 | | - static Boolean inWsl() { |
92 | | - return containsLine("/proc/1/environ", "container=wslcontainer_host_id"); |
| 89 | + private static boolean fileExists(String path) { |
| 90 | + return Files.exists(Paths.get(path)); |
93 | 91 | } |
94 | 92 |
|
95 | 93 | /** |
|
0 commit comments