/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.plugin.apache;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.hyperic.sigar.CpuTimer;
import org.hyperic.util.StringUtil;
import org.hyperic.util.exec.Execute;
import org.hyperic.util.exec.ExecuteWatchdog;
import org.hyperic.util.exec.PumpStreamHandler;
import org.hyperic.util.file.FileUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.product.GenericPlugin;
import org.hyperic.hq.product.PlatformDetector;
public class ApacheBinaryInfo {
private static final Log log =
LogFactory.getLog(ApacheBinaryInfo.class.getName());
private static final String[] CTL_SCRIPTS = {
"apachectl", "apache2ctl"
};
private static HashMap cache = null;
private static final String APACHE_VERSION = "Apache/";
private static final String SERVER_VERSION = "Server version:";
private static final String SERVER_BUILT = "Server built:";
private static final String MPM_DIR = "-D APACHE_MPM_DIR=\"";
public String version = null;
public String root = null;
public String conf;
public String binary;
public String ctl;
public String built;
public String mpm;
public String name;
public long pid = 0;
public String errmsg = "";
private long lastModified = 0;
ApacheBinaryInfo() {}
//clone method..only want to cache the fields
//extracted from the binary itself which are static.
//root, conf, ctl, etc., can change based on process args
private ApacheBinaryInfo(ApacheBinaryInfo info) {
this.version = info.version;
this.root = info.root;
this.conf = info.conf;
this.binary = info.binary;
this.ctl = info.ctl;
this.built = info.built;
this.mpm = info.mpm;
this.name = info.name;
this.errmsg = info.errmsg;
this.lastModified = info.lastModified;
}
public static synchronized ApacheBinaryInfo getInfo(String binary) {
ApacheBinaryInfo info = new ApacheBinaryInfo();
if (binary.startsWith("\"")) {
binary = binary.substring(1, binary.indexOf("\"", 1));
}
info.binary = binary;
info.ctl = binary;
try {
info.getApacheBinaryInfo(binary);
} catch (IOException e) {
log.debug(e, e);
}
return info;
}
public static ApacheBinaryInfo getInfo(String binary,
String wantedVersion) {
ApacheBinaryInfo info = getInfo(binary);
if (info.version == null) {
return null;
}
if (info.version.startsWith(wantedVersion)) {
return info;
}
return null;
}
//XXX very minimal parsing of httpd.conf (see HHQ-2594)
public static Map parseConfig(String file) {
Map values = new HashMap();
String line;
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
while ((line = reader.readLine()) != null) {
if (line.length() == 0) {
continue;
}
char chr = line.charAt(0);
if ((chr == '#') || (chr == '<') ||
Character.isWhitespace(chr))
{
continue; //only looking at top-level
}
int ix = line.indexOf('#');
if (ix != -1) {
line = line.substring(0, ix);
}
line = line.trim();
String[] ent = StringUtil.explodeQuoted(line);
if (ent.length == 2) {
values.put(ent[0], ent[1]);
}
}
} catch (IOException e) {
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {}
}
}
return values;
}
public static String getVersion(String binary,
String wantedVersion) {
ApacheBinaryInfo info = getInfo(binary);
if (info == null) {
return null;
}
if (info.version.startsWith(wantedVersion)) {
return info.version;
}
return null;
}
public static String getRootDir(String binary,
String wantedVersion) {
ApacheBinaryInfo info = getInfo(binary);
if (info == null) {
return null;
}
if (info.version.startsWith(wantedVersion)) {
return info.root;
}
return null;
}
private boolean findVersion(String binary) throws IOException {
if (this.version != null) {
return true;
}
this.errmsg = "";
String line = FileUtil.findString(binary, APACHE_VERSION);
if (line == null) {
this.errmsg =
"Unable to find '" + APACHE_VERSION + "' in: " +
binary;
return false;
}
int ix = line.indexOf(" ");
if (ix != -1) {
line = line.substring(0, ix);
}
ix = line.lastIndexOf('/');
this.version = line.substring(ix+1);
return true;
}
private String findDefine(String binary, String name)
throws IOException {
this.errmsg = "";
String define = "-D " + name + "=\"";
String line = FileUtil.findString(binary, define);
if (line == null) {
this.errmsg =
"Unable to find -D " + name + " in: " +
binary;
return null;
}
String value =
line.substring(define.length(),
line.length()-1);
if (value.length() == 0) {
this.errmsg =
"Found -D " + name + " in: " +
binary + " but value is empty";
value = null; //e.g. debian's apache2
}
return value;
}
private boolean findRoot(String binary) throws IOException {
if ((this.root = findDefine(binary, "HTTPD_ROOT")) == null) {
String file = findDefine(binary, "SERVER_CONFIG_FILE");
if (file != null) {
File conf = new File(file);
if (conf.isAbsolute() && conf.exists()) {
//e.g. debian is /etc/apache2
this.root = conf.getParent();
}
}
}
if (this.root == null) {
return false;
}
else {
return true;
}
}
public File serverRootRelative(String name) {
File file = new File(name);
if (!file.isAbsolute() && (this.root != null)) {
return new File(this.root, name);
}
else {
return file;
}
}
private void getVersionCmdInfo(String binary) {
ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
ExecuteWatchdog watchdog =
new ExecuteWatchdog(1000);
Execute ex =
new Execute(new PumpStreamHandler(stdOut, stdErr),
watchdog);
if (!PlatformDetector.isWin32()) {
File lib = new File(new File(binary).getParentFile().getParentFile(), "lib");
String[] env = {"LD_LIBRARY_PATH=" + lib.getAbsolutePath()};
ex.setEnvironment(env);
}
ex.setCommandline(new String[] { binary, "-V" });
BufferedReader is = null;
try {
String line;
ex.execute();
is = new BufferedReader(new StringReader(stdOut.toString()));
while ((line = is.readLine()) != null) {
line = line.trim();
if (line.startsWith(SERVER_VERSION)) {
line = line.substring(SERVER_VERSION.length()).trim();
int ix = line.indexOf('/');
if (ix != -1) {
line = line.substring(ix+1);
ix = line.indexOf(' ');
if (ix != -1) {
line = line.substring(0, ix);
}
this.version = line;
}
}
else 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;
}
}
} catch (Exception e) {
String msg =
"Error running binary '" + binary + "': " +
e.getMessage();
log.debug(msg, e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {}
}
}
log.debug("[getVersionCmdInfo] this="+this);
}
private void getApacheBinaryInfo(String binary)
throws IOException {
File binaryFile = new File(binary);
if (!binaryFile.exists()) {
this.errmsg = "'" + binaryFile + "' not found";
return;
}
if (binaryFile.isDirectory()) {
this.errmsg = "'" + binaryFile + "' is a directory";
return;
}
getVersionCmdInfo(binary);
boolean isDLL = false;
File libhttpd = null;
File bindir = binaryFile.getParentFile();
if (bindir == null) {
throw new IOException(binary + " has no parent directory");
}
if (ApacheServerDetector.isWin32()) {
//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.exists()) {
isDLL = true;
}
}
else {
//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");
if (libhttpd.exists()) {
binary = libhttpd.getAbsolutePath();
}
}
}
for (int i=0; i<CTL_SCRIPTS.length; i++) {
File script = new File(bindir, CTL_SCRIPTS[i]);
if (script.exists()) {
this.ctl = script.toString();
}
}
if (!(findVersion(binary) ||
(isDLL && findVersion(libhttpd.getAbsolutePath()))))
{
return;
}
if (!findRoot(binary)) {
return;
}
}
public Properties toProperties() {
Properties props = new Properties();
props.setProperty("exe", this.binary);
if (this.mpm != null) {
props.setProperty("mpm", this.mpm);
}
if (this.built != null) {
props.setProperty("built", this.built);
}
return props;
}
public String toString() {
String info =
"version=" + this.version +
", root=" + this.root +
", binary=" + this.binary +
", ctl=" + this.ctl;
if (this.mpm != null) {
info += ", mpm=" + this.mpm;
}
if (this.built != null) {
info += ", build=" + this.built;
}
if (this.errmsg.length() > 0) {
info += ", errmsg=" + this.errmsg;
}
return info;
}
public static void main(String[] args) {
CpuTimer cpu = new CpuTimer();
cpu.start();
String binary = args[0];
System.out.println(getInfo(binary));
cpu.stop();
cpu.list(System.out);
}
}