/* * 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.iis; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xpath.XPathAPI; import org.hyperic.sigar.win32.MetaBase; import org.hyperic.sigar.win32.Win32Exception; import org.hyperic.util.exec.Execute; import org.hyperic.util.exec.ExecuteWatchdog; import org.hyperic.util.exec.PumpStreamHandler; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class IisMetaBase { private static final Log _log = LogFactory.getLog(IisMetaBase.class.getName()); private static final String IIS_MKEY = "/LM/W3SVC"; private static final int MD_SSL_ACCESS_PERM = 6030; private static final int MD_ACCESS_SSL = 0x00000008; private static final String APPCMD = "C:/Windows/System32/inetsrv/appcmd.exe"; String id; String ip; String hostname; String port; String path; boolean requireSSL = false; public String toString() { String s = "id: " + id + " " + ip + ":" + port; if (hostname != null) { s += ", Host: " + hostname; } return s; } public static Map getWebSites() throws Win32Exception { if (new File(APPCMD).exists()) { try { return getWebSitesViaAppCmd(); // IIS7 } catch (Exception e) { _log.error(APPCMD + ": " + e, e); throw new Win32Exception(e.getMessage()); } } else { return getWebSitesViaMetaBase(); } } // public static Map getApplicationPools() throws Win32Exception { // // if (new File(APPCMD).exists()) { // try { // return getApplicationPoolsViaAppCmd(); // } catch (Exception ex) { // _log.error(APPCMD + ": " + ex, ex); // throw new Win32Exception(ex.getMessage()); // } // } else { // return null; // } // } private static boolean parseBinding(IisMetaBase info, String entry) { if (entry == null) { return false; } int ix = entry.indexOf(":"); if (ix == -1) { return false; } // binding format: // "listen ip:port:host header" info.ip = entry.substring(0, ix); entry = entry.substring(ix + 1); ix = entry.indexOf(":"); info.port = entry.substring(0, ix); // if host header is defined, URLMetric // will add Host: header with this value. info.hostname = entry.substring(ix + 1); if ((info.hostname != null) && (info.hostname.length() == 0)) { info.hostname = null; } if ((info.ip == null) || (info.ip.length() == 0) || (info.ip.equals("*"))) { // not bound to a specific ip info.ip = "localhost"; } return true; } // public static Map getApplicationPoolsViaAppCmd() { // // final String[] cmd = { APPCMD, "list", "APPPOOL" }; // // Map apppools = new HashMap(); // // ByteArrayOutputStream output = executeCommandLine(cmd); // // if (output == null) // return apppools; // // String appPools[] = output.toString().split("\\r\\n|\\n|\\r"); // // Pattern pattern = Pattern.compile(".*\"([^\"]+)\".*"); // Pattern infoPattern = Pattern.compile(".*\\(MgdVersion:([^,]+),MgdMode:([^,]+),state:([^\\)]+)\\)"); // // for (int i = 0; i < appPools.length; i++) { // Matcher matcher = pattern.matcher(appPools[i]); // // IisApplicationPool info = new IisApplicationPool(); // // if (matcher.matches()) { // info.name = matcher.group(1); // } // // matcher = infoPattern.matcher(appPools[i]); // // if (matcher.matches()) { // info.dotNetCLRVersion = matcher.group(1); // info.managedPipelineMode = matcher.group(2); // info.status = matcher.group(3); // } // // apppools.put(info.name, info); // } // // return apppools; // } // IIS7 does not use MetaBase private static Map getWebSitesViaAppCmd() throws Exception { final String[] cmd = { APPCMD, "list", "config", "-section:system.applicationHost/sites" }; Map websites = new HashMap(); ByteArrayOutputStream output = executeCommandLine(cmd); if (output == null) return websites; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document config = db.parse(new ByteArrayInputStream(output.toString().getBytes("UTF-8"))); NodeList sites = XPathAPI.selectNodeList(config, "//sites/site"); for (int i = 0; i < sites.getLength(); i++) { Element site = (Element) sites.item(i); String name = site.getAttribute("name"); IisMetaBase info = new IisMetaBase(); info.id = site.getAttribute("id"); String sitePath = "//site[@name=\"" + name + "\"]/"; String bindPath = sitePath + "bindings/binding[1]"; String docPath = sitePath + "application[1]/virtualDirectory[1]/@physicalPath"; Element binding = (Element) XPathAPI.selectSingleNode(site, bindPath); if (binding == null) { _log.debug("No bindings defined for: " + name); continue; } String proto = binding.getAttribute("protocol"); if (proto != null) { if ("https".equals(proto.toString().trim())) { info.requireSSL = true; } } String bindInfo = binding.getAttribute("bindingInformation"); if (!parseBinding(info, bindInfo)) { _log.debug("Failed to parse bindingInformation=" + bindInfo + " for: " + name); continue; } Object docRoot = XPathAPI.eval(site, docPath); if (docRoot != null) { info.path = docRoot.toString(); } _log.debug(name + "=" + info); websites.put(name, info); } return websites; } private static ByteArrayOutputStream executeCommandLine(final String[] cmd) { ByteArrayOutputStream output = new ByteArrayOutputStream(); ExecuteWatchdog wdog = new ExecuteWatchdog(5 * 1000); Execute exec = new Execute(new PumpStreamHandler(output), wdog); exec.setCommandline(cmd); try { int exitStatus = exec.execute(); if (exitStatus != 0 || wdog.killedProcess()) { _log.error(Arrays.asList(cmd) + ": " + output); output.close(); return null; } } catch (Exception e) { _log.error(Arrays.asList(cmd) + ": " + e); try { output.close(); } catch (IOException e1) { _log.error(Arrays.asList(cmd) + ": " + e1); } return null; } return output; } private static Map getWebSitesViaMetaBase() throws Win32Exception { String keys[]; Map websites = new HashMap(); MetaBase mb = new MetaBase(); try { mb.OpenSubKey(IIS_MKEY); keys = mb.getSubKeyNames(); } finally { mb.close(); } for (int i = 0; i < keys.length; i++) { String key = keys[i]; int id; if (!Character.isDigit(key.charAt(0))) { continue; } try { id = Integer.parseInt(key); } catch (NumberFormatException e) { continue; } String subkey = IIS_MKEY + "/" + id; MetaBase srv = null; try { srv = new MetaBase(); srv.OpenSubKey(subkey); String[] bindings = null; IisMetaBase info = new IisMetaBase(); // IIS 6.0+Windows 2003 has Administration website // that requires SSL by default. // Any Web Site can be configured to required ssl. try { int flags = srv.getIntValue(MD_SSL_ACCESS_PERM); info.requireSSL = (flags & MD_ACCESS_SSL) != 0; if (info.requireSSL) { bindings = srv.getMultiStringValue(MetaBase.MD_SECURE_BINDINGS); } } catch (Win32Exception e) { } if (bindings == null) { bindings = srv.getMultiStringValue(MetaBase.MD_SERVER_BINDINGS); } info.id = key; if (bindings.length == 0) { continue; } if (!parseBinding(info, bindings[0])) { continue; } String name = srv.getStringValue(MetaBase.MD_SERVER_COMMENT); websites.put(name, info); // XXX this is bogus, else locks the metabase // because OpenSubKey does not close the key // thats already open. srv.close(); srv = null; srv = new MetaBase(); srv.OpenSubKey(subkey + "/ROOT"); String docroot = srv.getStringValue(3001); info.path = docroot; } catch (Win32Exception e) { } finally { if (srv != null) { srv.close(); } } } return websites; } public static void main(String[] args) throws Exception { Map websites = IisMetaBase.getWebSites(); System.out.println(websites); } }