/*
* 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.autoinventory.scanimpl;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.util.PluginLoader;
import org.hyperic.util.StringUtil;
import org.hyperic.util.config.ConfigOption;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.config.EnumerationConfigOption;
import org.hyperic.util.config.DirArrayConfigOption;
import org.hyperic.util.config.IntegerConfigOption;
import org.hyperic.util.config.BooleanConfigOption;
import org.hyperic.util.file.match.Matcher;
import org.hyperic.util.file.match.MatcherConfig;
import org.hyperic.util.file.match.MatchSelector;
import org.hyperic.util.file.match.MatchResults;
import org.hyperic.util.file.match.MatcherInterruptCallback;
import org.hyperic.util.file.match.MatcherProgressCallback;
import org.hyperic.util.file.match.MatcherInterruptedException;
import org.hyperic.sigar.SigarException;
import org.hyperic.hq.product.PlatformDetector;
import org.hyperic.hq.product.PluginException;
import org.hyperic.hq.product.ServerDetector;
import org.hyperic.hq.product.FileServerDetector;
import org.hyperic.hq.autoinventory.AutoinventoryException;
import org.hyperic.hq.autoinventory.ServerSignature;
/**
* This implementation of ScanMethod knows how to scan the filesystem.
* It can be configured to scan the whole filesystem, or to only
* scan certain directories while ignoring others. It can also ignore
* entire filesystems of a particular type, for example network mounted
* filesystems or CD-ROM drives (ie iso9660 filesystems).
*/
public class FileScan
extends ScanMethodBase
implements MatcherInterruptCallback, MatcherProgressCallback {
private List _scanDirs;
private List _excludeDirs;
private String _fsTypes;
private int _depth;
private boolean _followSymlinks;
private boolean _isWindows;
private Log _log = LogFactory.getLog(FileScan.class.getName());
private transient long _statusUpdateCounter = 0;
public static final String FS_TYPE_ALL = "All disks";
public static final String FS_TYPE_LOCAL = "Local disks";
public static final String FS_TYPE_NETWORK = "Network-mounted disks";
public static final String[] FS_TYPES
= { FS_TYPE_ALL, FS_TYPE_LOCAL, FS_TYPE_NETWORK };
public FileScan () {
// we'll give the filesystem a reliability factor of 5
_authorityLevel = 5;
_isWindows = PlatformDetector.IS_WIN32;
}
public FileScan (boolean isWindows) {
// we'll give the filesystem a reliability factor of 5
_authorityLevel = 5;
_isWindows = isWindows;
}
public String getName () { return "FileScan"; }
public String getDisplayName () { return "File Scan"; }
public String getDescription () { return "Scan the filesystem"; }
protected ConfigOption[] getOptionsArray () {
ConfigOption[] opts = new ConfigOption[5];
int i=0;
opts[i++] = new DirArrayConfigOption("scanDirs",
"Directories to scan"
+ " (use | as a delimiter)",
getDefaultScanDirs());
opts[i++] = new DirArrayConfigOption("excludeDirs",
"Directories to exclude from scan"
+ " (use | as a delimiter)",
getDefaultExcludeDirs());
opts[i++] = new EnumerationConfigOption("fsTypes",
"Filesystem types to scan",
FS_TYPES[0],
FS_TYPES);
opts[i++] = new IntegerConfigOption("depth",
"How deep (in directory levels) to scan."
+ " Use -1 to indicate unlimited depth",
new Integer(6));
opts[i++] = new BooleanConfigOption("followSymlinks",
"Should symlinks be followed?",
false);
return opts;
}
public void scan(ConfigResponse platformConfig, ServerDetector[] serverDetectors)
throws AutoinventoryException {
boolean hasFileScan = false;
for (int i=0; i<serverDetectors.length; i++) {
if (serverDetectors[i] instanceof FileServerDetector) {
hasFileScan = true;
break;
}
}
if (!hasFileScan) {
_log.debug("Skipping FileScan");
_state.setScanStatus(this, "scan completed");
return;
}
try {
_state.setScanStatus(this, "scan started");
// Get the list of directories to scan
_scanDirs = StringUtil.explode(_config.getValue("scanDirs"),
DirArrayConfigOption.DELIM_STR);
// Get the list of directories to exclude
_excludeDirs = StringUtil.explode(_config.getValue("excludeDirs"),
DirArrayConfigOption.DELIM_STR);
_fsTypes = _config.getValue("fsTypes");
int fsTypeID = convertFSType(_fsTypes);
_depth = Integer.parseInt(_config.getValue("depth"));
_followSymlinks
= Boolean.valueOf(_config.getValue("followSymlinks")).booleanValue();
if ( _scanner.getIsInterrupted() ) {
return;
}
Matcher m = new Matcher();
MatcherConfig mconfig = new MatcherConfig();
Iterator matchedServerTypes;
int i;
MatchResults matchResults;
List stMatches;
String matchPath;
ServerDetector serverDetector;
ServerDetector prevDetector;
List detectedServers;
MatchSelector matchSelector;
// This map helps us keep track of whether or not
// a previous server detector has detected a server
// in a given directory.
Map pathsToDetectors = new HashMap();
for ( i=0; i<_scanDirs.size(); i++ ) {
mconfig.addSearchDir((String) _scanDirs.get(i));
}
mconfig.setExcludePatterns(_excludeDirs);
mconfig.setFSTypes(fsTypeID);
mconfig.setFollowSymlinks(_followSymlinks);
mconfig.setLog(_log);
mconfig.setMaxDepth(_depth);
mconfig.setMatcherInterruptCB(this);
mconfig.setMatcherProgressCB(this);
mconfig.setAllowMultipleMatches(true);
// For each server detector, add a matcherselector
for ( i=0; i<serverDetectors.length; i++ ) {
matchSelector = getMatchSelector(serverDetectors[i]);
mconfig.addMatchSelector(matchSelector);
}
try {
_statusUpdateCounter = 0;
matchResults = m.getMatches(mconfig);
_state.addScanExceptions(this, matchResults.getErrorArray());
} catch (SigarException se) {
throw new AutoinventoryException("Fatal Exception running "
+ "FileScan: " + se, se);
} catch (MatcherInterruptedException mie) {
// Don't do anything, the scan is aborted
_log.info("Scan interrupted.");
// but if we wanted to, the exception is kind enough to let
// us know what the matches were before the interruption...
// allMatches = mie.getMatchesSoFar();
return;
}
matchedServerTypes = matchResults.matches.keySet().iterator();
while ( matchedServerTypes.hasNext() ) {
serverDetector
= (ServerDetector) matchedServerTypes.next();
stMatches = (List) matchResults.matches.get(serverDetector);
for ( i=0; i<stMatches.size(); i++ ) {
matchPath = (String) stMatches.get(i);
prevDetector
= (ServerDetector) pathsToDetectors.get(matchPath);
if ( prevDetector != null ) {
throw new AutoinventoryException
("Multiple servers ("
+ prevDetector.getServerSignature().getServerTypeName()
+ " and "
+ serverDetector.getServerSignature().getServerTypeName()
+ ") can detect for path: " + matchPath);
}
PluginLoader.setClassLoader(serverDetector);
try {
detectedServers = ((FileServerDetector)serverDetector).
getServerResources(platformConfig, matchPath);
} finally {
PluginLoader.resetClassLoader(serverDetector);
}
if ( detectedServers != null &&
detectedServers.size() > 0 ) {
// Record this match so if future server detectors
// claim a match and detect servers here, it will
// be an error condition (see exception thrown above).
pathsToDetectors.put(serverDetector, matchPath);
// Add servers to state
// We had a match, save the path and server value.
_log.info("DETECTED SERVERS="
+ StringUtil.listToString(detectedServers));
_state.addServers(this, detectedServers);
}
}
}
} catch (PluginException e) {
_log.error("PluginException in FileScan: " + e, e);
}
catch ( Exception e ) {
_log.error("Big-time error in FileScan: " + e, e);
throw new AutoinventoryException(e);
} finally {
_state.setScanStatus(this, "scan completed");
}
}
/** @see org.hyperic.util.file.match.MatcherInterruptCallback#getIsInterrupted */
public boolean getIsInterrupted () {
return Thread.currentThread().isInterrupted();
}
/**
* Convert our concept of which filesystems to scan
* into one of the FS_XXX constants defined in MatcherConfig
*/
private int convertFSType ( String aiType ) {
if ( aiType.equals(FS_TYPE_ALL) ) {
return MatcherConfig.FS_ALL;
}
if ( aiType.equals(FS_TYPE_LOCAL) ) {
return MatcherConfig.FS_LOCAL;
}
if ( aiType.equals(FS_TYPE_NETWORK) ) {
return MatcherConfig.FS_NETWORK;
}
throw new IllegalArgumentException("Unrecognized filesystem argument: "
+ aiType);
}
private static final String[] WIN32_DEFAULT_DIRS = { "C:\\" };
private static final String[] WIN32_DEFAULT_EXCLUDE_DIRS
= { "\\WINNT", "\\TEMP", "\\TMP",
"\\Documents and Settings", "\\Recycled" };
private static final String[] UNIX_DEFAULT_DIRS = { "/usr", "/opt" };
private static final String[] UNIX_DEFAULT_EXCLUDE_DIRS
= { "/usr/doc", "/usr/dict", "/usr/lib", "/usr/libexec",
"/usr/man", "/usr/tmp", "/usr/include", "/usr/share",
"/usr/src",
"/usr/local/include", "/usr/local/share", "/usr/local/src" };
private String getDefaultScanDirs () {
String dirs = "";
int i;
if ( _isWindows ) {
// XXX We should use sigar here, because IMHO the "right"
// default value should be "all local disks", but we have no
// way to know what is local without something like sigar.
for ( i=0; i<WIN32_DEFAULT_DIRS.length; i++ ) {
if ( dirs.length() > 0 ) dirs += DirArrayConfigOption.DELIM_STR;
dirs += WIN32_DEFAULT_DIRS[i];
}
} else {
// XXX We could also use sigar here to look for other
// local filesystems, like /local0 /u1 etc.
for ( i=0; i<UNIX_DEFAULT_DIRS.length; i++ ) {
if ( dirs.length() > 0 ) dirs += DirArrayConfigOption.DELIM_STR;
dirs += UNIX_DEFAULT_DIRS[i];
}
}
return dirs;
}
private String getDefaultExcludeDirs () {
String dirs = "";
int i;
if ( _isWindows ) {
for ( i=0; i<WIN32_DEFAULT_EXCLUDE_DIRS.length; i++ ) {
if ( dirs.length() > 0 ) dirs += DirArrayConfigOption.DELIM_STR;
dirs += WIN32_DEFAULT_EXCLUDE_DIRS[i];
}
} else {
for ( i=0; i<UNIX_DEFAULT_EXCLUDE_DIRS.length; i++ ) {
if ( dirs.length() > 0 ) dirs += DirArrayConfigOption.DELIM_STR;
dirs += UNIX_DEFAULT_EXCLUDE_DIRS[i];
}
}
return dirs;
}
private MatchSelector getMatchSelector ( ServerDetector sd ) {
MatchSelector ms = getMatchSelector(sd.getServerSignature());
ms.setKey(sd);
return ms;
}
private MatchSelector getMatchSelector ( ServerSignature sig ) {
MatchSelector ms = new MatchSelector(sig);
ms.addMatchPatterns (sig.getFileMatchPatterns());
ms.addExcludePatterns(sig.getFileExcludePatterns());
// System.err.println("FileScan.getMatchSelector(" + sig + ") returning: " + ms);
return ms;
}
/**
* @see org.hyperic.util.file.match.MatcherProgressCallback#notifyScanDir
*/
public void notifyScanDir (File dir) {
if ( (_statusUpdateCounter % 10) == 0 ) {
_state.setScanStatus(this, "scanning: " + dir.getAbsolutePath());
}
_statusUpdateCounter++;
}
}