/*
* This file is part of LCMC written by Rasto Levrinc.
*
* Copyright (C) 2016, Rastislav Levrinc.
*
* The LCMC is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* The LCMC 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with LCMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package lcmc.host.domain.parser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lcmc.common.domain.ConvertCmdCallback;
import lcmc.common.domain.util.Tools;
import lcmc.configs.DistResource;
import lcmc.host.domain.Host;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lombok.Getter;
public class DistributionDetector {
private final Host host;
/** Whether dist info was already logged. */
private boolean distInfoAlreadyLogged = false;
private String detectedKernelName = "";
private String detectedDistVersion = "";
@Getter
private String detectedKernelVersion = "";
private String detectedDist = "";
private String detectedArch = "";
@Getter
private String distributionName = "";
@Getter
private String distributionVersion = "";
@Getter
private String distributionVersionString = "";
@Getter
private String kernelName = "";
@Getter
private String kernelVersion = "";
@Getter
private String arch = "";
private static final Logger LOG = LoggerFactory.getLogger(Host.class);
public DistributionDetector(final Host host) {
this.host = host;
}
/**
* Sets distribution info for this host from array of strings.
* Array consists of: kernel name, kernel version, arch, os, version
* and distribution.
* @param lines
*/
@SuppressWarnings("fallthrough")
public void detect(final List<String> lines) {
if (!distInfoAlreadyLogged) {
for (final String di : lines) {
LOG.debug1("setDistInfo: dist info: " + di);
}
}
/* no breaks in the switch statement are intentional */
String lsbVersion = null;
String lsbDist = null;
switch (lines.size()) {
case 9:
lsbVersion = lines.get(8); // TODO: not used
case 8:
lsbDist = lines.get(7);
case 7:
lsbVersion = lines.get(6); // TODO: not used
case 6:
lsbDist = lines.get(5);
case 5:
if (lsbDist == null || "linux".equals(lsbDist)) {
detectedDist = lines.get(4);
} else {
detectedDist = lsbDist;
}
case 4:
if (lsbVersion == null) {
detectedDistVersion = lines.get(3);
} else {
detectedDistVersion = lines.get(3) + '/' + lsbVersion;
}
case 3:
detectedKernelVersion = lines.get(2);
case 2:
detectedArch = lines.get(1);
case 1:
detectedKernelName = lines.get(0);
case 0:
break;
default:
LOG.appError("setDistInfo: list: ", Arrays.asList(lines).toString());
break;
}
distributionName = detectedDist;
distributionVersion = detectedDistVersion;
initDistInfo();
if (!distInfoAlreadyLogged) {
LOG.debug1("setDistInfo: kernel name: " + detectedKernelName);
LOG.debug1("setDistInfo: kernel version: " + detectedKernelVersion);
LOG.debug1("setDistInfo: arch: " + detectedArch);
LOG.debug1("setDistInfo: dist version: " + detectedDistVersion);
LOG.debug1("setDistInfo: dist: " + detectedDist);
}
distInfoAlreadyLogged = true;
}
/** Initializes dist info. Must be called after setDistInfo. */
void initDistInfo() {
if (!"Linux".equals(detectedKernelName)) {
LOG.appWarning("initDistInfo: detected kernel not linux: " + detectedKernelName);
}
this.kernelName = "Linux";
if (!distributionName.equals(detectedDist)) {
LOG.appError("initDistInfo: dist: " + distributionName + " does not match " + detectedDist);
}
distributionVersionString = getDistVersionString(distributionVersion);
distributionVersion = getDistString("distributiondir");
this.kernelVersion = getKernelDownloadDir(detectedKernelVersion);
String arch0 = getDistString("arch:" + detectedArch);
if (arch0 == null) {
arch0 = detectedArch;
}
this.arch = arch0;
}
/** Returns the detected info to show. */
public String getDetectedInfo() {
return detectedDist + ' ' + detectedDistVersion;
}
/**
* Gets distribution name from distribution version. E.g. suse from sles9.
* This is used only when the distribution is selected in the pulldown menu,
* not when it is detected.
* The conversion rules for distributions are defined in DistResource.java,
* with 'dist:' prefix.
* TODO: remove it?
*/
public String getDistFromDistVersion(final String dV) {
/* remove numbers */
if ("No Match".equals(dV)) {
return null;
}
LOG.debug1("getDistFromDistVersion:" + dV.replaceFirst("\\d.*", ""));
return getDistString("dist:" + dV.replaceFirst("\\d.*", ""));
}
/**
* Returns command from DistResource resource bundle for specific
* distribution and version.
*/
public String getDistCommand(final String text,
final ConvertCmdCallback convertCmdCallback,
final boolean inBash,
final boolean inSudo) {
if (text == null) {
return null;
}
final String[] texts = text.split(";;;");
final List<String> results = new ArrayList<String>();
int i = 0;
for (final String t : texts) {
String distString = getDistString(t);
if (distString == null) {
LOG.appWarning("getDistCommand: unknown command: " + t);
distString = t;
}
if (inBash && i == 0) {
String sudoS = "";
if (inSudo) {
sudoS = DistResource.SUDO;
}
results.add(sudoS + "bash -c \"" + Tools.escapeQuotes(distString, 1) + '"');
} else {
results.add(distString);
}
i++;
}
String ret;
if (results.isEmpty()) {
ret = text;
} else {
ret = Tools.join(";;;", results.toArray(new String[results.size()]));
}
if (convertCmdCallback != null && ret != null) {
ret = convertCmdCallback.convert(ret);
}
return ret;
}
/** Returns string that is specific to a distribution and version. */
public String getDistString(final String text) {
if (distributionName == null) {
distributionName = "";
}
if (distributionVersionString == null) {
distributionVersionString = "";
}
final Locale locale = new Locale(distributionName, distributionVersionString);
LOG.debug2("getDistString: text: " + text + " dist: " + distributionName + " version: " + distributionVersionString);
final ResourceBundle resourceString = ResourceBundle.getBundle("lcmc.configs.DistResource", locale);
String ret;
try {
ret = resourceString.getString(text + '.' + arch);
} catch (final Exception e) {
ret = null;
}
if (ret == null) {
try {
if (ret == null) {
ret = resourceString.getString(text);
}
LOG.debug2("getDistString: ret: " + ret);
return ret;
} catch (final RuntimeException e) {
return null;
}
}
return ret;
}
/**
* Gets compact representation of distribution and version. Distribution
* and version are joined with "_" and all spaces and '.' are replaced by
* "_" as well.
*/
public String getDistVersionString(final String distributionVersion) {
if (distributionName == null) {
distributionName = "";
}
LOG.debug2("getDistVersionString: dist: " + distributionName + ", version: " + distributionVersion);
final Locale locale = new Locale(distributionName, "");
final ResourceBundle resourceCommand = ResourceBundle.getBundle("lcmc.configs.DistResource", locale);
String distVersion = null;
try {
distVersion = resourceCommand.getString("version:" + distributionVersion);
} catch (final Exception e) {
/* with wildcard */
final StringBuilder buf = new StringBuilder(distributionVersion);
for (int i = distributionVersion.length() - 1; i >= 0; i--) {
try {
distVersion = resourceCommand.getString("version:" + buf.toString() + '*');
} catch (final Exception e2) {
distVersion = null;
}
if (distVersion != null) {
break;
}
buf.setLength(i);
}
if (distVersion == null) {
distVersion = distributionVersion;
}
}
LOG.debug2("getDistVersionString: dist version: " + distVersion);
return distVersion;
}
/**
* Converts kernelVersion as parsed from uname to a version that is used
* in the download area on the website.
*/
public String getKernelDownloadDir(final CharSequence kernelVersion) {
final String regexp = getDistString("kerneldir");
if (regexp != null && kernelVersion != null) {
final Pattern p = Pattern.compile(regexp);
final Matcher m = p.matcher(kernelVersion);
if (m.matches()) {
return m.group(1);
}
}
return null;
}
/** Returns string that is specific to a distribution and version. */
@SuppressWarnings("unchecked")
public List<String> getDistStrings(final String text) {
if (distributionName == null) {
distributionName = "";
}
if (distributionVersionString == null) {
distributionVersionString = "";
}
final Locale locale = new Locale(distributionName, distributionVersionString);
LOG.debug2("getDistStrings: text: " + text + " dist: " + distributionName + " version: " + distributionVersionString);
final ResourceBundle resourceString = ResourceBundle.getBundle("lcmc.configs.DistResource", locale);
List<String> ret;
try {
ret = (List<String>) resourceString.getObject(text);
} catch (final Exception e) {
ret = new ArrayList<>();
}
return ret;
}
}