001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.lang3; 019 020import java.io.IOException; 021import java.nio.charset.Charset; 022import java.nio.file.Files; 023import java.nio.file.Paths; 024import java.util.Arrays; 025 026/** 027 * Helps query the runtime environment. 028 * 029 * @since 3.15.0 030 */ 031public class RuntimeEnvironment { 032 033 private static boolean fileExists(final String path) { 034 return Files.exists(Paths.get(path)); 035 } 036 037 /** 038 * Tests whether we are running in a container like Docker or Podman. 039 * 040 * @return whether we are running in a container like Docker or Podman. Never null 041 */ 042 public static Boolean inContainer() { 043 return inContainer(StringUtils.EMPTY); 044 } 045 046 static boolean inContainer(final String dirPrefix) { 047 /* 048 Roughly follow the logic in SystemD: 049 https://github.com/systemd/systemd/blob/0747e3b60eb4496ee122066c844210ce818d76d9/src/basic/virt.c#L692 050 051 We check the `container` environment variable of process 1: 052 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. 053 If the variable is not empty, we return true. 054 If the variable is absent, we continue. 055 056 We check files in the container. According to SystemD: 057 /.dockerenv is used by Docker. 058 /run/.containerenv is used by PodMan. 059 060 */ 061 final String value = readFile(dirPrefix + "/proc/1/environ", "container"); 062 if (value != null) { 063 return !value.isEmpty(); 064 } 065 return fileExists(dirPrefix + "/.dockerenv") || fileExists(dirPrefix + "/run/.containerenv"); 066 } 067 068 /** 069 * Tests whether the {@code /proc/N/environ} file at the given path string contains a specific line prefix. 070 * 071 * @param envVarFile The path to a /proc/N/environ file. 072 * @param key The env var key to find. 073 * @return value The env var value or null. 074 */ 075 private static String readFile(final String envVarFile, final String key) { 076 try { 077 final byte[] bytes = Files.readAllBytes(Paths.get(envVarFile)); 078 final String content = new String(bytes, Charset.defaultCharset()); 079 // Split by null byte character 080 final String[] lines = content.split(String.valueOf(CharUtils.NUL)); 081 final String prefix = key + "="; 082 return Arrays.stream(lines) 083 .filter(line -> line.startsWith(prefix)) 084 .map(line -> line.split("=", 2)) 085 .map(keyValue -> keyValue[1]) 086 .findFirst() 087 .orElse(null); 088 } catch (final IOException e) { 089 return null; 090 } 091 } 092 093 /** 094 * Constructs a new instance. 095 * 096 * @deprecated Will be removed in 4.0.0. 097 */ 098 @Deprecated 099 public RuntimeEnvironment() { 100 // empty 101 } 102}