/* * 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.util.file.match; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.tools.ant.types.selectors.FileSelector; import org.hyperic.sigar.FileSystem; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.SigarException; /** * Implements a powerful file/directory matcher on top of Ant's fileset * classes. */ public class Matcher { /** * Construct an Matcher. */ public Matcher () {} /** * Sets up the appropriate MatcherScanners to use for the search. * @param config The config to use to initialize * @return A List of MatcherScanners to use for the search. */ private List initScanners (MatcherConfig config, FileSystem[] filesystems, MatchResults results) { List dirs = config.getSearchDirs(); String dir; int i, j; MatcherScanner scanner; List scanners = new ArrayList(); List selectorList = config.getMatchSelectors(); MatchSelector[] selectors; MasterMatchSelector masterSelector; FileSelector[] masterSelectorArray; List excludes = config.getExcludePatterns(); Log log = config.getLog(); String[] excludesArray; selectors = new MatchSelector[selectorList.size()]; for ( i=0; i<selectors.length; i++ ) { selectors[i] = (MatchSelector) selectorList.get(i); } masterSelectorArray = new FileSelector[1]; masterSelectorArray[0] = new MasterMatchSelector(selectors, config.getAllowMultipleMatches()); excludesArray = new String[excludes.size()]; for ( i=0; i<excludesArray.length; i++ ) { excludesArray[i] = excludes.get(i).toString(); } for ( i=0; i<dirs.size(); i++ ) { dir = (String) dirs.get(i); if ( shouldSkip(dir, log, results) ) continue; if ( config.getFSTypes() != MatcherConfig.FS_ALL ) { if ( !isFSTypeMatch(dir, config.getFSTypes(), filesystems, log) ) continue; } scanner = new MatcherScanner(); scanner.setLog(log); scanner.setBasedir(new File(dir)); scanner.setSelectors(masterSelectorArray); scanner.setExcludes(excludesArray); scanner.setFollowSymlinks(config.getFollowSymlinks()); scanner.setMatcherInterruptCB(config.getMatcherInterruptCB()); scanner.setMatcherProgressCB(config.getMatcherProgressCB()); scanner.setMaxDepth(config.getMaxDepth()); scanners.add(scanner); } return scanners; } /** * Determine if the dir is on a filesystem that we should * be searching. * @param dir The name of the directory. * @param fstypes The filesystem types we're supposed to search. * @param filesystems The filesystems that sigar detected. * @param log The log to use for errors and warnings. * @return true if the directory should be included in the search, * false otherwise. */ private boolean isFSTypeMatch ( String dir, int fstypes, FileSystem[] filesystems, Log log ) { String longestMatch = ""; int longestMatchIndex = -1; String absPath = (new File(dir)).getAbsolutePath(); String fsMountPoint; if ( filesystems == null ) { if ( log != null ) { log.error("matcher: filesystems array was never initialized."); } throw new IllegalArgumentException("filesystems array was never " + "initialized."); } for ( int i=0; i<filesystems.length; i++ ) { fsMountPoint = filesystems[i].getDirName(); if ( absPath.startsWith(fsMountPoint) && fsMountPoint.length() > longestMatch.length() ) { longestMatch = fsMountPoint; longestMatchIndex = i; } } if ( longestMatchIndex == -1 ) { if ( log != null ) { log.warn("Directory " + dir + " did not match " + "any filesystems, it will not be searched."); } return false; } return isCandidateFS(fstypes, filesystems[longestMatchIndex], log); } /** * Determine if the given filesystem is OK to search, based on * the value of fstype which tells us what filesystem types we * are supposed to search. */ public boolean isCandidateFS ( int fstype, FileSystem fs, Log log ) { switch ( fs.getType() ) { case FileSystem.TYPE_UNKNOWN: if ( log != null ) { log.warn("Encountered UNKNOWN filesystem (device=" + fs.getDevName() + "): " + fs.getDirName()); } return false; case FileSystem.TYPE_NONE: if ( log != null ) { log.warn("Encountered NONE filesystem (device=" + fs.getDevName() + "): " + fs.getDirName()); } return false; case FileSystem.TYPE_LOCAL_DISK: return (fstype == MatcherConfig.FS_ALL || fstype == MatcherConfig.FS_LOCAL); case FileSystem.TYPE_NETWORK: return (fstype == MatcherConfig.FS_ALL || fstype == MatcherConfig.FS_NETWORK); case FileSystem.TYPE_RAM_DISK: case FileSystem.TYPE_CDROM: case FileSystem.TYPE_SWAP: return false; default: if ( log != null ) { log.warn("Encountered filesystem with invalid type (" + fs.getType() + ") (device=" + fs.getDevName() + "): " + fs.getDirName()); } return false; } } private FileSystem[] loadFilesystems (MatcherConfig config) throws SigarException { Log log = config.getLog(); FileSystem[] filesystems; if ( config.getFSTypes() != MatcherConfig.FS_ALL ) { Sigar sigar = new Sigar(); try { filesystems = sigar.getFileSystemList(); return filesystems; } catch ( SigarException se ) { if ( log != null ) { log.error("matcher: error getting " + "available filesystems:" + se, se); } throw se; } finally { sigar.close(); } } return null; } /** * Get the matches for this search. * @return A Map representing the matches. The keys in the Map are * the keys that each MatchSelector in the MatcherConfig was * initialized with in its constructor. The values are Lists, where * each element in the List is a String representing the full path * of the matched path. * @exception MatcherInterruptedException If the search was interrupted * before it could be completed. In this case, you can get the matches * so far by calling getMatchesSoFar on the MatcherInterruptedException * object. * @exception SigarException If an error occurs reading the available * filesystems - this can only happen if the config's getFSTypes returns * a value other than MatcherConfig.FS_ALL. */ public synchronized MatchResults getMatches ( MatcherConfig config ) throws MatcherInterruptedException, SigarException { int i, j; List scanners; MatcherScanner scanner; MatchResults results = new MatchResults(); File f; Log log = config.getLog(); FileSystem[] filesystems = null; filesystems = loadFilesystems(config); scanners = initScanners(config, filesystems, results); for ( i=0; i<scanners.size(); i++ ) { scanner = (MatcherScanner) scanners.get(i); scanner.initMatches(results.matches); try { scanner.doScan(); } catch ( MatcherInterruptedException mie ) { mie.setMatchesSoFar(scanner.getMatches()); if ( log != null ) { log.warn("matcher: search interrupted."); } throw mie; } catch ( Exception e ) { // huh? scanner.addError(new MatcherException("matcher: search error", e)); if ( log != null ) { log.error("matcher: search error: " + e, e); } } results.matches = scanner.getMatches(); if (log != null) { log.debug("results.matches=" + results.matches); } results.errors.addAll(scanner.getErrors()); } return results; } /** * Should we skip a directory completely? * @return true if dir is null, if dir is not a directory, * if we can't read dir, or if dir does not exist. */ private boolean shouldSkip ( String dir, Log log, MatchResults results ) { File dirFile; String msg; // Should never happen if ( dir == null ) { msg = "matcher: ignoring null dir."; if ( log != null ) { log.info(msg); } results.errors.add(new MatcherException(msg)); return true; } dirFile = new File(dir); if ( !dirFile.exists() ) { msg = "matcher: ignoring non-existent dir: " + dirFile.getAbsolutePath(); if ( log != null ) { log.info(msg); } results.errors.add(new MatcherException(msg)); return true; } if ( !dirFile.canRead() ) { msg = "matcher: ignoring unreadable dir: " + dirFile.getAbsolutePath(); if ( log != null ) { log.info(msg); } results.errors.add(new MatcherException(msg)); return true; } if ( !dirFile.isDirectory() ) { msg = "matcher: ignoring non-dir: " + dirFile.getAbsolutePath(); if ( log != null ) { log.info(msg); } results.errors.add(new MatcherException(msg)); return true; } return false; } }