/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.plugins.platform;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Locale;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Information identifying a Linux distribution (e.g. RHEL 4 Update 4, Ubuntu Edgy, etc.).
*
* @author Ian Springer
*/
public class LinuxDistroInfo {
private static final String DEFAULT_NAME = "UNKNOWN";
private static final String DEFAULT_VERSION = "UNKNOWN";
private static final File LSB_RELEASE_FILE = new File("/etc/lsb-release");
private static final File REDHAT_RELEASE_FILE = new File("/etc/redhat-release");
private static final File REDHAT_VERSION_FILE = new File("/etc/redhat-version"); // rare
private static final File DEBIAN_VERSION_FILE = new File("/etc/debian_version");
private static final File OS_RELEASE_FILE = new File("/etc/os-release");
private static final String DISTRIB_ID_VARIABLE = "DISTRIB_ID";
private static final String DISTRIB_RELEASE_VARIABLE = "DISTRIB_RELEASE";
private static final String DISTRIB_CODENAME_VARIABLE = "DISTRIB_CODENAME";
private static LinuxDistroInfo instance;
private final Log log = LogFactory.getLog(LinuxDistroInfo.class);
private String name;
private String version;
/**
* Returns the Linux distro information for the platform this JVM is running on.
*
* @return the Linux distro information for the platform this JVM is running on
*/
public static LinuxDistroInfo getInstance() {
if (instance == null) {
if (!isLinux()) {
throw new IllegalStateException(
"Attempt to determine Linux distro information for a non-Linux machine.");
}
instance = new LinuxDistroInfo();
}
return instance;
}
public String getName() {
return this.name;
}
public String getVersion() {
return this.version;
}
private LinuxDistroInfo() {
String codename = null;
// First check for distro-specific release files (e.g. /etc/redhat-release).
if (REDHAT_RELEASE_FILE.exists() || REDHAT_VERSION_FILE.exists()) {
File releaseFile;
if (REDHAT_RELEASE_FILE.exists()) {
releaseFile = REDHAT_RELEASE_FILE;
} else {
releaseFile = REDHAT_VERSION_FILE;
}
String release = readSingleLineReleaseFile(releaseFile);
if (release != null) {
int index = release.indexOf("release ");
if (index != -1) {
this.name = release.substring(0, index).trim();
this.version = release.substring(index);
}
}
if (this.name == null) {
this.name = "Red Hat Linux";
}
} else if (DEBIAN_VERSION_FILE.exists()) {
String release = readSingleLineReleaseFile(DEBIAN_VERSION_FILE);
this.name = "Debian";
if (release != null) {
this.name += " " + release;
}
}
// TODO: Add support for other common distributions (SuSE, Slackware, Mandrake, etc.); for a nice reference of the
// release files supplied by various distros, see http://linuxmafia.com/faq/Admin/release-files.html.
// Now that we've parsed any distro-specific release files, check for the semi-standardized LSB (Linux Standards
// Base) release file that is present in many different distros. If distro-related information is defined in this
// file, it overrides the information defined in any distro-specific version files. For more info on
// /etc/lsb-release, see http://manpages.unixforum.co.uk/man-pages/linux/opensuse-10.2/1/lsb_release-man-page.html.
if (LSB_RELEASE_FILE.exists()) {
Properties releaseInfo = readMultiVariableReleaseFile(LSB_RELEASE_FILE);
String lsbName = releaseInfo.getProperty(DISTRIB_ID_VARIABLE);
if (lsbName != null) {
this.name = lsbName;
}
String lsbVersion = releaseInfo.getProperty(DISTRIB_RELEASE_VARIABLE);
if (lsbVersion != null) {
this.version = lsbVersion;
}
codename = releaseInfo.getProperty(DISTRIB_CODENAME_VARIABLE);
}
if (this.name == null || this.version == null) {
// Try the new os-release file, which seems to be the new over all
// way to do this going forward.
// See http://www.freedesktop.org/software/systemd/man/os-release.html
// Actually at one point in the future this should be the default.
if (OS_RELEASE_FILE.exists()) {
Properties releaseInfo = readMultiVariableReleaseFile(OS_RELEASE_FILE);
String name = releaseInfo.getProperty("NAME");
if (name!=null) {
this.name = name;
}
String version = releaseInfo.getProperty("VERSION");
if (version!=null) {
this.version = version;
}
}
}
if (this.name == null) {
this.name = DEFAULT_NAME;
}
if (this.version == null) {
this.version = DEFAULT_VERSION;
}
// Finally, append the release codename if we were able to ascertain it.
if (codename != null) {
this.version += " (" + codename + ")";
}
}
private String readSingleLineReleaseFile(File releaseFile) {
String release = null;
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(releaseFile));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.equals("")) {
release = line;
break;
}
}
} catch (IOException e) {
log.error("Failed reading single-line release file: " + releaseFile, e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.debug("Failed closing single-line release file: " + releaseFile, e);
}
}
}
return release;
}
private Properties readMultiVariableReleaseFile(File releaseFile) {
Properties variables = new Properties();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(releaseFile));
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.equals("") && (line.charAt(0) != '#')) {
int index = line.indexOf('=');
if (index != -1) {
String varName = line.substring(0, index);
String varValue = line.substring(index + 1);
varValue = stripSurroundingQuotes(varValue);
variables.setProperty(varName, varValue);
}
}
}
} catch (IOException e) {
log.error("Failed reading multi-variable release file: " + releaseFile, e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.debug("Failed closing multi-variable release file: " + releaseFile, e);
}
}
}
return variables;
}
private String stripSurroundingQuotes(String varValue) {
if (((varValue.charAt(0) == '"') && (varValue.charAt(varValue.length() - 1) == '"'))
|| ((varValue.charAt(0) == '\'') && (varValue.charAt(varValue.length() - 1) == '\''))) {
varValue = varValue.substring(1, varValue.length() - 1);
}
return varValue;
}
private static boolean isLinux() {
return System.getProperty("os.name").toLowerCase(Locale.US).indexOf("linux") != -1;
}
}