/* * 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 as published by * the Free Software Foundation version 2 of the License. * * 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.plugins.apache.util; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.rhq.core.pluginapi.util.FileUtils; import org.rhq.core.system.ProcessExecution; import org.rhq.core.system.ProcessExecutionResults; import org.rhq.core.system.SystemInfo; import org.rhq.core.system.SystemInfoFactory; /** * TODO * * @author Ian Springer */ public class ApacheBinaryInfo { private static final Log LOG = LogFactory.getLog(ApacheBinaryInfo.class.getName()); private static final Map<String, ApacheBinaryInfo> CACHE = new HashMap<String, ApacheBinaryInfo>(); private static final String APACHE_VERSION = "Apache/"; private static final String SERVER_BUILT = "Server built:"; private static final String MPM_DIR = "-D APACHE_MPM_DIR=\""; private static final String HTTPD_ROOT_DEFINE = "HTTPD_ROOT"; private static final String SERVER_CONFIG_FILE_DEFINE = "SERVER_CONFIG_FILE"; private String version; private String root; private String binaryPath; private String ctl; private String built; private String mpm; private long lastModified = 0; private Set<String> compiledInModules = new HashSet<String>(); private Set<String> compiledInDefines = new HashSet<String>(); protected ApacheBinaryInfo(@NotNull String binaryPath) { this.binaryPath = binaryPath; } @NotNull public static synchronized ApacheBinaryInfo getInfo(String binaryPath, SystemInfo systemInfo) throws Exception { ApacheBinaryInfo info = CACHE.get(binaryPath); long lastModified = new File(binaryPath).lastModified(); if ((info == null) || (lastModified != info.lastModified)) { info = new ApacheBinaryInfo(binaryPath); CACHE.put(binaryPath, info); info.getApacheBinaryInfo(binaryPath, systemInfo); } return info; } private String findVersion(String binaryPath) throws Exception { String line = FileUtils.findString(binaryPath, APACHE_VERSION); if (line == null) { throw new Exception("Unable to find '" + APACHE_VERSION + "' in: " + binaryPath); } int spaceIndex = line.indexOf(" "); if (spaceIndex != -1) { line = line.substring(0, spaceIndex); } int slashIndex = line.lastIndexOf('/'); String version = line.substring(slashIndex + 1); return version; } private String findDefine(String binaryPath, String name) throws Exception { String define = "-D " + name + "=\""; String line = FileUtils.findString(binaryPath, define); if (line == null) { throw new Exception("Unable to find -D " + name + " in: " + binaryPath); } String value = line.substring(define.length(), line.length() - 1); if (value.length() == 0) { LOG.debug("Found -D " + name + " in: " + binaryPath + " but value is empty"); value = null; //e.g. debian's apache2 } return value; } private String findRoot(String binaryPath) throws Exception { String root = findDefine(binaryPath, HTTPD_ROOT_DEFINE); if (root == null) { String file = findDefine(binaryPath, SERVER_CONFIG_FILE_DEFINE); if (file != null) { File conf = new File(file); if (conf.isAbsolute() && conf.exists()) { //e.g. debian is /etc/apache2 root = conf.getParent(); } } } return root; } private void getVersionCommandInfo(String binaryPath, SystemInfo systemInfo) { BufferedReader is = null; try { compiledInDefines.clear(); ProcessExecution processExecution = new ProcessExecution(binaryPath); processExecution.setArguments(new String[] { "-V" }); processExecution.setWaitForCompletion(10000L); processExecution.setCaptureOutput(true); ProcessExecutionResults results = systemInfo.executeProcess(processExecution); if (results.getError() != null) { throw results.getError(); } String line; is = new BufferedReader(new StringReader(results.getCapturedOutput())); while ((line = is.readLine()) != null) { line = line.trim(); if (line.startsWith(SERVER_BUILT)) { line = line.substring(SERVER_BUILT.length()).trim(); this.built = line; } else if (line.startsWith(MPM_DIR)) { line = line.substring(MPM_DIR.length()).trim(); int ix = line.lastIndexOf('"'); if (ix != -1) { line = line.substring(0, ix); } ix = line.lastIndexOf("/"); if (ix != -1) { line = line.substring(ix + 1); } this.mpm = line; } else if (line.startsWith("-D")) { String define = line.substring(3); int equalsIdx = define.indexOf('='); if (equalsIdx >= 0) { define = define.substring(0, equalsIdx); } compiledInDefines.add(define); } } } catch (Throwable t) { String msg = "Error running binary '" + binaryPath + "': " + t.getMessage(); LOG.error(msg, t); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } private void getCompiledInModules(String binaryPath, SystemInfo systemInfo) { BufferedReader is = null; try { compiledInModules.clear(); ProcessExecution processExecution = new ProcessExecution(binaryPath); processExecution.setArguments(new String[] { "-l" }); processExecution.setWaitForCompletion(10000L); processExecution.setCaptureOutput(true); ProcessExecutionResults results = systemInfo.executeProcess(processExecution); if (results.getError() != null) { throw results.getError(); } String line; is = new BufferedReader(new StringReader(results.getCapturedOutput())); boolean firstLine = true; while ((line = is.readLine()) != null) { if (firstLine) { firstLine = false; continue; } compiledInModules.add(line.trim()); } } catch (Throwable t) { String msg = "Error running binary '" + binaryPath + "': " + t.getMessage(); LOG.error(msg, t); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } private void getApacheBinaryInfo(String binaryPath, SystemInfo systemInfo) throws Exception { File binaryFile = new File(binaryPath); if (!binaryFile.exists()) { throw new IOException(binaryFile + " does not exist."); } if (binaryFile.isDirectory()) { throw new IOException(binaryFile + " is a directory."); } this.lastModified = binaryFile.lastModified(); getVersionCommandInfo(binaryPath, systemInfo); getCompiledInModules(binaryPath, systemInfo); File libHttpd = getHttpdSharedLibrary(binaryFile); this.version = findVersion((libHttpd != null) ? libHttpd.getPath() : this.binaryPath); this.root = findRoot(binaryPath); this.ctl = findDefine(binaryPath, SERVER_CONFIG_FILE_DEFINE); } private File getHttpdSharedLibrary(File binaryFile) throws Exception { File libhttpd; File bindir = binaryFile.getParentFile(); if (bindir == null) { throw new Exception(this.binaryPath + " has no parent directory"); } if (isUnix()) { //If libhttpd.so exists in libexec, then we need //to search that file instead of the Apache binary. if ((libhttpd = bindir.getParentFile()) != null) { libhttpd = new File(libhttpd, "libexec/libhttpd.so"); } } else { //on Windows, Apache/version is in libhttpd.dll //the other -D FOO=BAR props are in httpd.exe libhttpd = new File(bindir, "libhttpd.dll"); } if (libhttpd != null && libhttpd.exists()) { return libhttpd; } else { return null; } } public Properties toProperties() { Properties props = new Properties(); props.setProperty("exe", this.binaryPath); if (this.mpm != null) { props.setProperty("mpm", this.mpm); } if (this.built != null) { props.setProperty("built", this.built); } return props; } @Override public boolean equals(Object o) { if (this == o) { return true; } if ((o == null) || (getClass() != o.getClass())) { return false; } ApacheBinaryInfo that = (ApacheBinaryInfo) o; if (!binaryPath.equals(that.binaryPath)) { return false; } return true; } @Override public int hashCode() { return binaryPath.hashCode(); } @Override public String toString() { String info = "version=" + this.version + ", root=" + this.root + ", binary=" + this.binaryPath + ", ctl=" + this.ctl; if (this.mpm != null) { info += ", mpm=" + this.mpm; } if (this.built != null) { info += ", build=" + this.built; } return info; } public String getVersion() { return version; } @Nullable public String getRoot() { return root; } public String getBinaryPath() { return binaryPath; } public String getCtl() { return ctl; } public String getBuilt() { return built; } public String getMpm() { return mpm; } public long getLastModified() { return lastModified; } public static void main(String[] args) throws Exception { String binary = args[0]; System.out.println(getInfo(binary, SystemInfoFactory.createJavaSystemInfo())); } private static boolean isUnix() { return File.separatorChar == '/'; } public Set<String> getCompiledInModules() { return compiledInModules; } public Set<String> getCompiledInDefines() { return compiledInDefines; } }