/** * Oshi (https://github.com/oshi/oshi) * * Copyright (c) 2010 - 2017 The Oshi Project Team * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Maintainers: * dblock[at]dblock[dot]org * widdis[at]gmail[dot]com * enrico.bianchi[at]gmail[dot]com * * Contributors: * https://github.com/oshi/oshi/graphs/contributors */ package oshi.software.os.linux; import java.io.File; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import oshi.software.common.AbstractOSVersionInfoEx; import oshi.util.ExecutingCommand; import oshi.util.FileUtil; public class LinuxOSVersionInfoEx extends AbstractOSVersionInfoEx { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(LinuxOSVersionInfoEx.class); public LinuxOSVersionInfoEx() { this(null, null); } protected LinuxOSVersionInfoEx(String versionId, String codeName) { setVersion(versionId); setCodeName(codeName); if (getVersion() == null) { setVersionFromReleaseFiles(); } if (getCodeName() == null) { setCodeName(""); } List<String> procVersion = null; procVersion = FileUtil.readFile("/proc/version"); if (!procVersion.isEmpty()) { String[] split = procVersion.get(0).split("\\s+"); for (String s : split) { if (!"Linux".equals(s) && !"version".equals(s)) { setBuildNumber(s); return; } } } setBuildNumber(""); } /* * Code below this point is largely a copy of LinuxOperatingSystem class * except family is not set. If this class has been called from that class * then no new information would be added here. This is provided in the odd * event someone wants to instantiate this class without having first gone * through the LinuxOperatingSystem */ private void setVersionFromReleaseFiles() { // There are two competing options for family/version information. // Newer systems are adopting a standard /etc/os-release file: // https://www.freedesktop.org/software/systemd/man/os-release.html // // Some systems are still using the lsb standard which parses a // variety of /etc/*-release files and is most easily accessed via // the commandline lsb_release -a, see here: // http://linux.die.net/man/1/lsb_release // In this case, the /etc/lsb-release file (if it exists) has // optional overrides to the information in the /etc/distrib-release // files, which show: "Distributor release x.x (Codename)" // // Attempt to read /etc/os-release file. if (readOsRelease()) { // If successful, we're done. The version and possibly codeName // are set return; } // Attempt to execute the `lsb_release` command if (execLsbRelease()) { // If successful, we're done. The version and possibly codeName // are set return; } // The above two options should hopefully work on most // distributions. If not, we keep having fun. // Attempt to read /etc/lsb-release file if (readLsbRelease()) { // If successful, we're done. The version and possibly codeName // are set return; } // If we're still looking, we search for any /etc/*-release (or // similar) filename, for which the first line should be of the // "Distributor release x.x (Codename)" format or possibly a // "Distributor VERSION x.x (Codename)" format String etcDistribRelease = LinuxOperatingSystem.getReleaseFilename(); if (readDistribRelease(etcDistribRelease)) { // If successful, we're done. The version and possibly codeName // are set return; } // Finally, if all else fails if (getVersion() == null) { setVersion(System.getProperty("os.version")); } } /** * Attempts to read /etc/os-release * * @return true if file successfully read and NAME= found */ private boolean readOsRelease() { if (new File("/etc/os-release").exists()) { List<String> osRelease = FileUtil.readFile("/etc/os-release"); // Search for NAME= for (String line : osRelease) { if (line.startsWith("VERSION=")) { LOG.debug("os-release: {}", line); // remove beginning and ending '"' characters, etc from // VERSION="14.04.4 LTS, Trusty Tahr" (Ubuntu style) // or VERSION="17 (Beefy Miracle)" (os-release doc style) line = line.replace("VERSION=", "").replaceAll("^\"|\"$", "").trim(); String[] split = line.split("[()]"); if (split.length <= 1) { // If no parentheses, check for Ubuntu's comma format split = line.split(", "); } if (split.length > 0) { this.version = split[0].trim(); } if (split.length > 1) { this.codeName = split[1].trim(); } } else if (line.startsWith("VERSION_ID=") && this.version == null) { LOG.debug("os-release: {}", line); // remove beginning and ending '"' characters, etc from // VERSION_ID="14.04" this.version = line.replace("VERSION_ID=", "").replaceAll("^\"|\"$", "").trim(); } } } return this.version != null; } /** * Attempts to execute `lsb_release -a` * * @return true if the command successfully executed and Release: or * Description: found */ private boolean execLsbRelease() { // If description is of the format Distrib release x.x (Codename) // that is primary, otherwise use Distributor ID: which returns the // distribution concatenated, e.g., RedHat instead of Red Hat for (String line : ExecutingCommand.runNative("lsb_release -a")) { if (line.startsWith("Description:")) { LOG.debug("lsb_release -a: {}", line); line = line.replace("Description:", "").trim(); if (line.contains(" release ")) { this.version = parseRelease(line, " release "); } } else if (line.startsWith("Release:") && this.version == null) { LOG.debug("lsb_release -a: {}", line); this.version = line.replace("Release:", "").trim(); } else if (line.startsWith("Codename:") && this.codeName == null) { LOG.debug("lsb_release -a: {}", line); this.codeName = line.replace("Codename:", "").trim(); } } return this.version != null; } /** * Attempts to read /etc/lsb-release * * @return true if file successfully read and DISTRIB_RELEASE or * DISTRIB_DESCRIPTION found */ private boolean readLsbRelease() { if (new File("/etc/lsb-release").exists()) { List<String> osRelease = FileUtil.readFile("/etc/lsb-release"); // Search for NAME= for (String line : osRelease) { if (line.startsWith("DISTRIB_DESCRIPTION=")) { LOG.debug("lsb-release: {}", line); line = line.replace("DISTRIB_DESCRIPTION=", "").replaceAll("^\"|\"$", "").trim(); if (line.contains(" release ")) { this.version = parseRelease(line, " release "); } } else if (line.startsWith("DISTRIB_RELEASE=") && this.version == null) { LOG.debug("lsb-release: {}", line); this.version = line.replace("DISTRIB_RELEASE=", "").replaceAll("^\"|\"$", "").trim(); } else if (line.startsWith("DISTRIB_CODENAME=") && this.codeName == null) { LOG.debug("lsb-release: {}", line); this.codeName = line.replace("DISTRIB_CODENAME=", "").replaceAll("^\"|\"$", "").trim(); } } } return this.version != null; } /** * Attempts to read /etc/distrib-release (for some value of distrib) * * @return true if file successfully read and " release " or " VERSION " * found */ private boolean readDistribRelease(String filename) { if (new File(filename).exists()) { List<String> osRelease = FileUtil.readFile(filename); // Search for Distrib release x.x (Codename) for (String line : osRelease) { LOG.debug("{}: {}", filename, line); if (line.contains(" release ")) { this.version = parseRelease(line, " release "); // If this parses properly we're done break; } else if (line.contains(" VERSION ")) { this.version = parseRelease(line, " VERSION "); // If this parses properly we're done break; } } } return this.version != null; } /** * Helper method to parse version description line style * * @param line * a String of the form "Distributor release x.x (Codename)" * @param splitLine * A regex to split on, e.g. " release " * @return the parsed version (codeName may have also been set) */ private String parseRelease(String line, String splitLine) { String[] split = line.split(splitLine); if (split.length > 1) { split = split[1].split("[()]"); if (split.length > 0) { this.version = split[0].trim(); } if (split.length > 1) { this.codeName = split[1].trim(); } } return this.version; } }