1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.lang3; 19 20 import java.io.IOException; 21 import java.nio.charset.Charset; 22 import java.nio.file.Files; 23 import java.nio.file.Paths; 24 import java.util.Arrays; 25 26 /** 27 * Helps query the runtime environment. 28 * 29 * @since 3.15.0 30 */ 31 public class RuntimeEnvironment { 32 33 private static boolean fileExists(final String path) { 34 return Files.exists(Paths.get(path)); 35 } 36 37 /** 38 * Tests whether we are running in a container like Docker or Podman. 39 * <p> 40 * <em>The following may change if we find better detection logic.</em> 41 * </p> 42 * <p> 43 * We roughly follow the logic in SystemD: 44 * </p> 45 * <p> 46 * <a href= 47 * "https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692">https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692</a> 48 * </p> 49 * <p> 50 * We check the `container` environment variable of process 1: 51 * </p> 52 * <ol> 53 * <li>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 54 * a container.</li> 55 * <li>If the variable is not empty, we return true.</li> 56 * <li>If the variable is absent, we continue.</li> 57 * <li>We check files in the container. According to SystemD:/ 58 * <ol> 59 * <li>/.dockerenv is used by Docker.</li> 60 * <li>/run/.containerenv is used by PodMan.</li> 61 * </ol> 62 * </li> 63 * </ol> 64 * 65 * @return whether we are running in a container like Docker or Podman. Never null. 66 * @see <a href="https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692">SystemD virt.c</a> 67 */ 68 public static Boolean inContainer() { 69 return inContainer(StringUtils.EMPTY); 70 } 71 72 /** 73 * Tests whether we are running in a container like Docker or Podman. 74 * <p> 75 * <em>The following may change if we find better detection logic.</em> 76 * </p> 77 * <p> 78 * We roughly follow the logic in SystemD: 79 * </p> 80 * <p> 81 * <a href= 82 * "https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692">https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692</a> 83 * </p> 84 * <p> 85 * We check the `container` environment variable of process 1: 86 * </p> 87 * <ol> 88 * <li>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 89 * a container.</li> 90 * <li>If the variable is not empty, we return true.</li> 91 * <li>If the variable is absent, we continue.</li> 92 * <li>We check files in the container. According to SystemD:/ 93 * <ol> 94 * <li>/.dockerenv is used by Docker.</li> 95 * <li>/run/.containerenv is used by PodMan.</li> 96 * </ol> 97 * </li> 98 * </ol> 99 * 100 * @return Whether we are running in a container like Docker or Podman. 101 * @see <a href="https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692">SystemD virt.c</a> 102 */ 103 static boolean inContainer(final String dirPrefix) { 104 final String value = readFile(dirPrefix + "/proc/1/environ", "container"); 105 if (value != null) { 106 return !value.isEmpty(); 107 } 108 return fileExists(dirPrefix + "/.dockerenv") || fileExists(dirPrefix + "/run/.containerenv"); 109 } 110 111 /** 112 * Tests whether the {@code /proc/N/environ} file at the given path string contains a specific line prefix. 113 * 114 * @param envVarFile The path to a /proc/N/environ file. 115 * @param key The env var key to find. 116 * @return value The env var value or null. 117 */ 118 private static String readFile(final String envVarFile, final String key) { 119 try { 120 final byte[] bytes = Files.readAllBytes(Paths.get(envVarFile)); 121 final String content = new String(bytes, Charset.defaultCharset()); 122 // Split by null byte character 123 final String[] lines = content.split(String.valueOf(CharUtils.NUL)); 124 final String prefix = key + "="; 125 // @formatter:off 126 return Arrays.stream(lines) 127 .filter(line -> line.startsWith(prefix)) 128 .map(line -> line.split("=", 2)) 129 .map(keyValue -> keyValue[1]) 130 .findFirst() 131 .orElse(null); 132 // @formatter:on 133 } catch (final IOException e) { 134 return null; 135 } 136 } 137 138 /** 139 * Constructs a new instance. 140 * 141 * @deprecated Will be removed in 4.0.0. 142 */ 143 @Deprecated 144 public RuntimeEnvironment() { 145 // empty 146 } 147 }