/*
* 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.netservices;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.hyperic.hq.product.AutoServerDetector;
import org.hyperic.hq.product.LogTrackPlugin;
import org.hyperic.hq.product.MeasurementPlugin;
import org.hyperic.hq.product.PluginException;
import org.hyperic.hq.product.ProductPlugin;
import org.hyperic.hq.product.ProductPluginManager;
import org.hyperic.hq.product.ServerDetector;
import org.hyperic.hq.product.ServerResource;
import org.hyperic.hq.product.ServiceResource;
import org.hyperic.hq.product.TypeInfo;
import org.hyperic.sigar.FileSystem;
import org.hyperic.sigar.NetConnection;
import org.hyperic.sigar.NetFlags;
import org.hyperic.sigar.NetServices;
import org.hyperic.sigar.ProcUtil;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.util.config.ConfigResponse;
public class NetServicesDetector
extends ServerDetector
implements AutoServerDetector {
private static final String RPC = "RPC";
private ServerResource getServer(ConfigResponse config) {
ServerResource server = createServerResource("/");
String fqdn = config.getValue(ProductPlugin.PROP_PLATFORM_FQDN);
String type = getTypeInfo().getName();
server.setName(fqdn + " " + type);
server.setIdentifier(server.getName());
server.setProductConfig();
server.setMeasurementConfig();
//server.setControlConfig();
getLog().debug("Created server=" + server.getName());
return server;
}
public List getServerResources(ConfigResponse config) {
List servers = new ArrayList();
servers.add(getServer(config));
return servers;
}
private ServiceResource createService(String type,
String address,
String port,
boolean isSSL) {
ConfigResponse config = new ConfigResponse();
config.setValue(NetServicesCollector.PROP_HOSTNAME,
address);
config.setValue(NetServicesCollector.PROP_PORT,
port);
if (isSSL) {
config.setValue(NetServicesCollector.PROP_SSL,
"true");
}
ServiceResource service = new ServiceResource();
service.setType(type);
String name = getPlatformName() + " " + type;
if (isSSL) {
name += " (SSL)";
}
service.setName(name);
setProductConfig(service, config); //merge defaults
service.setMeasurementConfig(new ConfigResponse(),
LogTrackPlugin.LOGLEVEL_WARN,
false);
return service;
}
private String getDescription(Sigar sigar, long port) {
long pid = -1;
try {
pid = sigar.getProcPort(NetFlags.CONN_TCP, port);
if (pid == 0) {
//XXX sigar bug
return null;
}
} catch (SigarException e) {
return null;
}
String description = null;
if (pid != -1) {
try {
description =
ProcUtil.getDescription(sigar, pid);
int len = description.length()+4;
int max = 200 - len; //appdef limit is 200
if (description.length() > max) {
int offset = description.length() - max;
description =
"..." + description.substring(offset);
}
} catch (SigarException e) {
return null;
}
}
if ((description == null) ||
(description.length() == 0))
{
return null;
}
else {
return description;
}
}
//XXX lame.
private boolean isIPv4(String address) {
return new StringTokenizer(address, ".", true).countTokens() == 7;
}
private void discoverNFS(List services) {
if (isWin32()) {
return;
}
FileSystem[] fslist;
try {
fslist = getSigar().getFileSystemList();
} catch (SigarException e) {
return;
}
for (int i=0; i<fslist.length; i++) {
FileSystem fs = fslist[i];
if (fs.getType() != FileSystem.TYPE_NETWORK) {
continue;
}
String type = fs.getSysTypeName();
if (!type.equals(RPCCollector.PROGRAM_NFS)) {
continue;
}
String dev = fs.getDevName();
int ix = dev.indexOf(':');
if (ix == -1) {
continue;
}
String host = dev.substring(0, ix);
ServiceResource service = new ServiceResource();
ConfigResponse config = new ConfigResponse();
config.setValue(NetServicesCollector.PROP_HOSTNAME,
host);
config.setValue("program", type);
config.setValue("version", RPCCollector.RPC_VERSION);
service.setProductConfig(config);
service.setMeasurementConfig();
service.setType(RPC);
String name = getPlatformName() + " NFS mount " + dev;
service.setName(name);
service.setDescription("Local mount point: " + fs.getDirName());
services.add(service);
}
}
private String getDiscover(String type) {
String key = "netservices.discover";
if (type != null) {
key += "." + type;
}
return getManager().getProperty(key);
}
protected List discoverServices(ConfigResponse serverConfig)
throws PluginException {
ArrayList services = new ArrayList();
if (!"false".equals(getDiscover(RPC))) {
discoverNFS(services);
}
if ("true".equals(getDiscover("http"))) {
services.add(createService("HTTP", "localhost", "80", false));
}
//not enabled by default, but helpful for testing.
if (!"true".equals(getDiscover(null))) {
return services;
}
String ip =
serverConfig.getValue(ProductPlugin.PROP_PLATFORM_IP);
ProductPluginManager ppm =
(ProductPluginManager)getManager().getParent();
ProductPlugin pPlugin =
ppm.getProductPlugin("netservices");
Sigar sigar = new Sigar();
NetConnection[] connections;
int flags = NetFlags.CONN_SERVER | NetFlags.CONN_TCP;
try {
connections = sigar.getNetConnectionList(flags);
} catch (SigarException e) {
return null;
}
HashMap ports = new HashMap();
ServiceResource service;
String description;
for (int i=0; i<connections.length; i++) {
NetConnection conn = connections[i];
if (conn.getState() != NetFlags.TCP_LISTEN) {
continue;
}
String address = conn.getLocalAddress();
if (address.equals(NetFlags.ANY_ADDR_V6) ||
!isIPv4(address))
{
continue;
}
if (NetFlags.isAnyAddress(address)) {
address = "*";
}
ports.put(String.valueOf(conn.getLocalPort()), address);
}
TypeInfo[] types = pPlugin.getTypes();
for (int i=0; i<types.length; i++) {
if (types[i].getType() != TypeInfo.TYPE_SERVICE) {
continue;
}
String type = types[i].getName();
MeasurementPlugin plugin =
ppm.getMeasurementPlugin(type);
String[] props = {
NetServicesCollector.PROP_PORT,
NetServicesCollector.PROP_SSLPORT
};
//loop twice, 1 == default port, 2 == default ssl port
for (int j=0; j<=1; j++) {
String port = plugin.getTypeProperty(props[j]);
if (port == null) {
continue;
}
boolean isSSL = j == 1;
String address = (String)ports.remove(port);
if (address == null) {
continue;
}
if (address.equals("*")) {
address = ip;
}
service = createService(type, address, port, isSSL);
description =
getDescription(sigar, Long.parseLong(port));
if (description != null) {
service.setDescription(description);
}
services.add(service);
}
}
//if we can get a decent description, add TCP Socket
//services for the remaining listening ports
for (Iterator it=ports.entrySet().iterator();
it.hasNext();)
{
Map.Entry entry = (Map.Entry)it.next();
String portstr = (String)entry.getKey();
String address = (String)entry.getValue();
long port = Long.parseLong(portstr);
StringBuffer name = new StringBuffer();
name.append(getPlatformName()).append(" LISTEN ").
append(address).append(':').append(port);
String pname = NetServices.getTcpName(port);
if (pname != null) {
name.append(" (").append(pname).append(")");
}
description = getDescription(sigar, port);
if ((pname == null) && (description == null)) {
continue;
}
if (address.equals("*")) {
address = ip;
}
service =
createService("TCP Socket", address, portstr, false);
service.setName(name.toString());
service.setDescription(description);
services.add(service);
}
return services;
}
}