/*
* 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.postfix;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hyperic.hq.product.AutoServerDetector;
import org.hyperic.hq.product.ServerDetector;
import org.hyperic.hq.product.ServerResource;
import org.hyperic.hq.product.ServiceResource;
import org.hyperic.hq.product.PluginException;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.sigar.SigarException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class PostfixServerDetector
extends ServerDetector
implements AutoServerDetector {
private static final String PTQL_QUERY =
"State.Name.eq=master";
private Log log = LogFactory.getLog("PostfixServerDetector");
static final String SERVER_NAME = "Postfix";
static final String QUEUE = "Queue";
protected static final String PROP_PATH = "path";
protected static final String PROP_QDIR = "queueDir";
protected static final String PROP_QUEUE = "queue";
protected static final String PROP_POSTCONF = "postconf";
protected static final String PROP_CONFDIR = "configDir";
private static final HashMap postconfInfoCache = new HashMap();
private static String postconfBin = null;
private String getPostconfValue(ConfigResponse config, String key) {
String confDir = config.getValue(PROP_CONFDIR);
HashMap info = (HashMap)postconfInfoCache.get(confDir);
if (info == null) {
info = getPostconfInfo(config);
postconfInfoCache.put(confDir, info);
}
return (String)info.get(key);
}
private HashMap getPostconfInfo(ConfigResponse config) {
HashMap info = new HashMap();
Process postconf;
String crPostconfBin = config.getValue(PROP_POSTCONF);
String confDir = config.getValue(PROP_CONFDIR);
// now that PROP_POSTCONF and PROP_CONFDIR gets set before
// this is called, these should hopefully never be true.
if (crPostconfBin == null) {
this.log.warn("postconf not configured, taking a guess...");
crPostconfBin = "/usr/sbin/postconf";
}
if (confDir == null) {
this.log.warn("postfix configuration directory not configured..");
confDir = "/etc/postfix";
}
try {
this.log.debug("executing " + crPostconfBin + " -c " + confDir);
String argv[] = { crPostconfBin, "-c", confDir };
postconf = Runtime.getRuntime().exec(argv);
} catch (IOException e) {
this.log.error("postconf exec failed:" + e);
return info;
}
BufferedReader in = null;
String line;
try {
in = new BufferedReader(
new InputStreamReader(postconf.getInputStream())
);
for (int i = 0; (line = in.readLine()) != null; i++) {
// XXX:String.split() no workie in java 1.3
String conf[] = line.split(" = ");
if (conf.length == 1) {
info.put(conf[0], "");
} else {
info.put(conf[0], conf[1]);
}
}
} catch (IOException e) {
this.log.error("io exception: " + e);
} finally {
if (in != null) {
try { in.close(); } catch (IOException e) {}
}
}
return info;
}
private String whichPostconf(String envPath) {
if (envPath == null) {
return null;
}
String[] path = envPath.split(":");
for (int i=0; i<path.length; i++) {
File postconf =
new File(path[i], "postconf");
if (postconf.exists()) {
log.debug("Using postconf=" + postconf);
return postconf.toString();
}
}
return null;
}
public List getServerResources(ConfigResponse platformConfig)
throws PluginException
{
long[] pids = getPids(PTQL_QUERY);
String user = System.getProperty("user.name");
//XXX use getSigar().getProcCred().getEuid() != 0 ?
if (!"root".equals(user)) {
int num = pids.length;
if (num != 0) {
log.info("Found " + num + " postfix processes" +
" but cannot discover or monitor unless " +
"running as root (user.name=" + user + ")");
}
return null;
}
List servers = new ArrayList();
for (int i=0; i < pids.length; i++) {
Map env;
try {
env = getSigar().getProcEnv(pids[i]);
} catch (SigarException e) {
this.log.debug("must be root to get postfix info...");
return servers;
}
// Config directory is the cwd
String postfixConfigDir = (String)env.get("config_directory");
if (postfixConfigDir == null) {
continue;
}
// try to find postconf in the PATH
// but only if we havent already found one.
if (postconfBin == null) {
postconfBin = whichPostconf((String)env.get("PATH"));
if (postconfBin == null) {
log.warn("Unable to find postconf in PATH");
continue;
}
}
ConfigResponse productConfig = new ConfigResponse();
// set PROP_CONFDIR and PROP_POSTCONF here so
// getPostconfInfo can use it
productConfig.setValue(PROP_CONFDIR, postfixConfigDir);
productConfig.setValue(PROP_POSTCONF, postconfBin);
// get version from postconf output
String version =
getPostconfValue(productConfig, "mail_version");
if (version == null) {
log.warn("cant find mail_version key in postconf");
continue;
}
String qdir = getPostconfValue(productConfig, "queue_directory");
if (qdir == null) {
continue;
}
// create server with postfixConfigDir instead of postfix
// so it will be unique for multiple servers.
ServerResource server = createServerResource(postfixConfigDir);
// set a server specific ptql query for process metrics
File pidFile = new File(qdir, "pid/master.pid");//XXX check exists
String ptqlQuery = "Pid.PidFile.eq=" + pidFile;
productConfig.setValue("process.query", ptqlQuery);
productConfig.setValue(PROP_QDIR, qdir);
server.setProductConfig(productConfig);
server.setMeasurementConfig();
String name =
getPlatformName() + " " + SERVER_NAME + " " + version;
// set server name - different if there are multiple
// servers running
if (pids.length > 1) {
name += " (" + postfixConfigDir + ")";
}
server.setName(name);
servers.add(server);
}
return servers;
}
protected List discoverServices(ConfigResponse config)
throws PluginException
{
ArrayList services = new ArrayList();
String qNames = getPostconfValue(config, "hash_queue_names");
if (qNames == null) {
this.log.error("cant find hash_queue_names key in postconf");
return null;
}
String queues[] = qNames.split(", ");
String qdir = config.getValue(PROP_QDIR);
for (int i = 0; i < queues.length; i++) {
String name = queues[i];
String path = qdir + "/" + name;
ServiceResource service = new ServiceResource();
service.setType(this, QUEUE);
service.setServiceName(name +" "+ QUEUE);
ConfigResponse productConfig = new ConfigResponse();
productConfig.setValue(PROP_QUEUE, name);
productConfig.setValue(PROP_PATH, path);
service.setProductConfig(productConfig);
service.setMeasurementConfig();
//service.setControlConfig();
this.log.debug("adding postfix " + name + " queue (" + path + ")");
services.add(service);
}
return services;
}
}