/*
* Copyright (c) 2006-2009 Hyperic, Inc.
* Copyright (c) 2010 VMware, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hyperic.sigar;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.hyperic.jni.ArchLoaderException;
import org.hyperic.jni.ArchNotSupportedException;
import org.hyperic.sigar.ptql.ProcessFinder;
/**
* The Sigar class provides access to the sigar objects containing
* system information. The Sigar object itself maintains internal
* state specific to each platform. It also implements the SigarProxy
* interface which provides caching at the Java level.
*/
public class Sigar implements SigarProxy {
private static String loadError = null;
public static final long FIELD_NOTIMPL = -1;
public static final int PID_PROC_CPU_CACHE = 1;
public static final int PID_PROC_IO_CACHE = 2;
/**
* The Sigar java version.
*/
public static final String VERSION_STRING =
Version.VERSION_STRING;
/**
* The Sigar native version.
*/
public static final String NATIVE_VERSION_STRING;
/**
* The scm (svn) revision from which sigar.jar was built.
*/
public static final String SCM_REVISION =
Version.SCM_REVISION;
/**
* The scm (svn) revision from which the sigar native binary was built.
*/
public static final String NATIVE_SCM_REVISION;
/**
* The date on which sigar.jar was built.
*/
public static final String BUILD_DATE =
Version.BUILD_DATE;
/**
* The date on which the sigar native binary was built.
*/
public static final String NATIVE_BUILD_DATE;
private static boolean enableLogging =
"true".equals(System.getProperty("sigar.nativeLogging"));
private static SigarLoader loader = new SigarLoader(Sigar.class);
private FileSystemMap mounts = null;
private boolean open = false;
int sigarWrapper = 0; //holds the sigar_t *
long longSigarWrapper = 0; //same, but where sizeof(void*) > sizeof(int)
// lastCpu is used to calculate the cpuPerc;
private Cpu lastCpu;
private Cpu[] lastCpuList;
private ProcessFinder processFinder = null;
static {
String nativeVersion = "unknown";
String nativeBuildDate = "unknown";
String nativeScmRevision = "unknown";
try {
loadLibrary();
nativeVersion = getNativeVersion();
nativeBuildDate = getNativeBuildDate();
nativeScmRevision = getNativeScmRevision();
checkVersion(nativeVersion);
} catch (SigarException e) {
loadError = e.getMessage();
try {
SigarLog.debug(loadError, e);
} catch (NoClassDefFoundError ne) {
//no log4j.jar
System.err.println(loadError);
e.printStackTrace();
}
}
NATIVE_VERSION_STRING = nativeVersion;
NATIVE_BUILD_DATE = nativeBuildDate;
NATIVE_SCM_REVISION = nativeScmRevision;
}
private static void checkVersion(String nativeVersionString)
throws SigarException {
StringTokenizer javaVersion =
new StringTokenizer(VERSION_STRING, ".");
StringTokenizer nativeVersion =
new StringTokenizer(nativeVersionString, ".");
String[] desc = { "major", "minor" };
for (int i=0; i<desc.length; i++) {
String jv =
javaVersion.hasMoreTokens() ?
javaVersion.nextToken() : "0";
String nv =
nativeVersion.hasMoreTokens() ?
nativeVersion.nextToken() : "0";
if (!jv.equals(nv)) {
String msg =
desc[i] + " version mismatch: (" +
jv + "!=" + nv + ") " +
"java=" + VERSION_STRING +
", native=" + nativeVersionString;
throw new SigarException(msg);
}
}
}
public static void load() throws SigarException {
if (loadError != null) {
throw new SigarException(loadError);
}
}
private static void loadLibrary() throws SigarException {
if (SigarLoader.IS_WIN32 &&
System.getProperty("os.version").equals("4.0"))
{
try {
//NT 4.0 only
String lib =
loader.findJarPath("pdh.dll") +
File.separator + "pdh.dll";
loader.systemLoad(lib);
} catch (Exception e) {}
}
try {
loader.load();
} catch (ArchNotSupportedException e) {
throw new SigarException(e.getMessage());
} catch (ArchLoaderException e) {
throw new SigarException(e.getMessage());
} catch (UnsatisfiedLinkError e) {
throw new SigarException(e.getMessage());
}
}
public File getNativeLibrary() {
return loader.getNativeLibrary();
}
/**
* Format size in bytes to a human readable string.
*
* @param size The size to format.
* @return The formatted string.
*/
public static native String formatSize(long size);
private static native String getNativeVersion();
private static native String getNativeBuildDate();
private static native String getNativeScmRevision();
/**
* Allocate and initialize the native Sigar object.
*/
public Sigar() {
try {
open();
this.open = true;
} catch (SigarException e) {
if (enableLogging) {
e.printStackTrace();
}
} catch (UnsatisfiedLinkError e) {
if (enableLogging) {
e.printStackTrace();
}
}
if (enableLogging) {
enableLogging(true);
}
}
protected void finalize() {
close();
}
private native void open() throws SigarException;
/**
* Release any native resources associated with this sigar instance.
* The sigar object is no longer usable after it has been closed.
* If the close method is not called directly, the finalize method will
* call it if the Sigar object is garbage collected.
*/
public synchronized void close() {
if (this.open) {
nativeClose();
this.open = false;
}
}
private native int nativeClose();
/**
* Get pid of the current process.
* @exception SigarException on failure.
*/
public native long getPid();
/**
* Get pid for the Windows service with the given name.
* This method is implemented on Windows only as a helper
* for PTQL.
*/
public native long getServicePid(String name) throws SigarException;
/**
* Send a signal to a process.
*
* @param pid The process id.
* @param signum The signal number.
* @exception SigarException on failure.
*/
public native void kill(long pid, int signum) throws SigarException;
/**
* Send a signal to a process.
*
* @param pid The process id.
* @param signame The signal name.
* @exception SigarException on failure.
*/
public void kill(long pid, String signame) throws SigarException {
int signum;
if (Character.isDigit(signame.charAt(0))) {
try {
signum = Integer.parseInt(signame);
} catch (NumberFormatException e) {
signum = -1;
}
}
else {
signum = getSigNum(signame);
}
if (signum < 0) {
throw new SigarException(signame +
": invalid signal specification");
}
kill(pid, signum);
}
public static native int getSigNum(String name);
/**
* Send a signal to a process.
*
* @param pid The process id or query.
* @param signum The signal number.
* @exception SigarException on failure.
*/
public void kill(String pid, int signum) throws SigarException {
kill(convertPid(pid), signum);
}
/**
* Get system memory info.
* @exception SigarException on failure.
*/
public Mem getMem() throws SigarException {
return Mem.fetch(this);
}
/**
* Get system swap info.
* @exception SigarException on failure.
*/
public Swap getSwap() throws SigarException {
return Swap.fetch(this);
}
/**
* Get system cpu info.
* @exception SigarException on failure.
*/
public Cpu getCpu() throws SigarException {
return Cpu.fetch(this);
}
static void pause(int millis) {
try {
Thread.sleep(millis);
} catch(InterruptedException e) { }
}
static void pause() {
pause(500);
}
/**
* Get system CPU info in percentage format. (i.e. fraction of 1)
* @exception SigarException on failure.
*/
public CpuPerc getCpuPerc() throws SigarException {
Cpu oldCpu;
if (this.lastCpu == null){
oldCpu = getCpu();
pause();
}
else {
oldCpu = this.lastCpu;
}
this.lastCpu = getCpu();
return CpuPerc.fetch(this, oldCpu, this.lastCpu);
}
/**
* Get system per-CPU info in percentage format. (i.e. fraction of 1)
* @exception SigarException on failure.
*/
public CpuPerc[] getCpuPercList() throws SigarException {
Cpu[] oldCpuList;
if (this.lastCpuList == null){
oldCpuList = getCpuList();
pause();
}
else {
oldCpuList = this.lastCpuList;
}
this.lastCpuList = getCpuList();
int curLen = this.lastCpuList.length;
int oldLen = oldCpuList.length;
CpuPerc[] perc =
new CpuPerc[curLen < oldLen ? curLen : oldLen];
for (int i=0; i<perc.length; i++) {
perc[i] =
CpuPerc.fetch(this, oldCpuList[i],
this.lastCpuList[i]);
}
return perc;
}
/**
* Get system resource limits.
* @exception SigarException on failure.
*/
public ResourceLimit getResourceLimit() throws SigarException {
return ResourceLimit.fetch(this);
}
/**
* Get system uptime info.
* @exception SigarException on failure.
*/
public Uptime getUptime() throws SigarException {
return Uptime.fetch(this);
}
/**
* Get system load average.
* @exception SigarException on failure.
* @return The system load averages for the past 1, 5, and 15 minutes.
*/
public native double[] getLoadAverage() throws SigarException;
/**
* Get system process list.
* @exception SigarException on failure.
* @return Array of process ids.
*/
public native long[] getProcList() throws SigarException;
/**
* Get system process stats.
* @exception SigarException on failure.
*/
public ProcStat getProcStat() throws SigarException {
return ProcStat.fetch(this);
}
private long convertPid(String pid) throws SigarException {
if (pid.equals("$$")) {
return getPid();
}
else if (Character.isDigit(pid.charAt(0))) {
return Long.parseLong(pid);
}
else {
if (this.processFinder == null) {
this.processFinder = new ProcessFinder(this);
}
return this.processFinder.findSingleProcess(pid);
}
}
/**
* Get process memory info.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcMem getProcMem(long pid) throws SigarException {
return ProcMem.fetch(this, pid);
}
public ProcMem getProcMem(String pid) throws SigarException {
return getProcMem(convertPid(pid));
}
public ProcMem getMultiProcMem(String query) throws SigarException {
return MultiProcMem.get(this, query);
}
/**
* Get process state info.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcState getProcState(long pid) throws SigarException {
return ProcState.fetch(this, pid);
}
public ProcState getProcState(String pid) throws SigarException {
return getProcState(convertPid(pid));
}
/**
* Get process time info.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcTime getProcTime(long pid) throws SigarException {
return ProcTime.fetch(this, pid);
}
public ProcTime getProcTime(String pid) throws SigarException {
return getProcTime(convertPid(pid));
}
/**
* Get process cpu info.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcCpu getProcCpu(long pid) throws SigarException {
return ProcCpu.fetch(this, pid);
}
public ProcCpu getProcCpu(String pid) throws SigarException {
return getProcCpu(convertPid(pid));
}
public MultiProcCpu getMultiProcCpu(String query) throws SigarException {
return MultiProcCpu.get(this, query);
}
/**
* Get process credential info.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcCred getProcCred(long pid) throws SigarException {
return ProcCred.fetch(this, pid);
}
public ProcCred getProcCred(String pid) throws SigarException {
return getProcCred(convertPid(pid));
}
/**
* Get process credential names.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcCredName getProcCredName(long pid) throws SigarException {
return ProcCredName.fetch(this, pid);
}
public ProcCredName getProcCredName(String pid) throws SigarException {
return getProcCredName(convertPid(pid));
}
/**
* Get process file descriptor info.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcFd getProcFd(long pid) throws SigarException {
return ProcFd.fetch(this, pid);
}
public ProcFd getProcFd(String pid) throws SigarException {
return getProcFd(convertPid(pid));
}
/**
* Get process current working directory.
* @param pid The process id.
* @exception SigarException on failure.
*/
public ProcExe getProcExe(long pid) throws SigarException {
return ProcExe.fetch(this, pid);
}
public ProcExe getProcExe(String pid) throws SigarException {
return getProcExe(convertPid(pid));
}
/**
* Get process arguments.
* @param pid The process id.
* @return Array of argument strings.
* @exception SigarException on failure.
*/
public native String[] getProcArgs(long pid) throws SigarException;
public String[] getProcArgs(String pid) throws SigarException {
return getProcArgs(convertPid(pid));
}
/**
* Get process environment.
* @param pid The process id.
* @return Map of environment strings.
* @exception SigarException on failure.
*/
public Map getProcEnv(long pid) throws SigarException {
return ProcEnv.getAll(this, pid);
}
public Map getProcEnv(String pid) throws SigarException {
return getProcEnv(convertPid(pid));
}
/**
* Get process environment variable value.
* This method is intended to avoid the overhead
* of creating a Map with all variables if only
* a single variable is needed.
* @param pid The process id.
* @param key Environment variable name.
* @return Environment variable value.
* @exception SigarException on failure.
*/
public String getProcEnv(long pid, String key) throws SigarException {
return ProcEnv.getValue(this, pid, key);
}
public String getProcEnv(String pid, String key) throws SigarException {
return getProcEnv(convertPid(pid), key);
}
/**
* Get process loaded modules.<p>
* Supported Platforms: Linux, Solaris and Windows.
* @param pid The process id.
* @return List of loaded modules.
* @exception SigarException on failure.
*/
private native List getProcModulesNative(long pid) throws SigarException;
public List getProcModules(long pid) throws SigarException {
return getProcModulesNative(pid);
}
public List getProcModules(String pid) throws SigarException {
return getProcModules(convertPid(pid));
}
/**
* Find the pid of the process which is listening on the given port.<p>
* Supported Platforms: Linux, Windows 2003, Windows XP, AIX.
* @param protocol NetFlags.CONN_TCP or NetFlags.CONN_UDP.
* @param port The port number.
* @return pid of the process.
* @exception SigarException on failure.
*/
public native long getProcPort(int protocol, long port)
throws SigarException;
/**
* @param protocol "tcp" or "udp".
* @param port The port number.
* @return pid of the process.
* @exception SigarException on failure.
*/
public long getProcPort(String protocol, String port)
throws SigarException {
return getProcPort(NetFlags.getConnectionProtocol(protocol),
Integer.parseInt(port));
}
/**
* Get process disk IO info.
* @param pid THe process id.
* @exception SigarException on failure.
*/
public ProcDiskIO getProcDiskIO(long pid) throws SigarException {
try {
return ProcDiskIO.fetch(this, pid);
} catch (UnsatisfiedLinkError linkErrorException) {
// We want to handle exceptions gracefully even if the linked
// shared library is older and isn't compiled with the ProcDiskIO APIs.
// The downside of this is that we throw SigarNotImplemented exception
// also when the shared library can't be loaded.
SigarException sigarException = new SigarNotImplementedException();
sigarException.initCause(linkErrorException);
throw sigarException;
}
}
public ProcDiskIO getProcDiskIO(String pid) throws SigarException {
return getProcDiskIO(convertPid(pid));
}
/**
* Get process cumulative disk IO info.
* @param pid THe process id.
* @exception SigarException on failure.
*/
public ProcCumulativeDiskIO getProcCumulativeDiskIO(long pid) throws SigarException {
try {
return ProcCumulativeDiskIO.fetch(this, pid);
} catch (UnsatisfiedLinkError linkErrorException) {
// We want to handle exceptions gracefully even if the linked
// shared library is older and isn't compiled with the ProcDiskIO APIs.
// The downside of this is that we throw SigarNotImplemented exception
// also when the shared library can't be loaded.
SigarException sigarException = new SigarNotImplementedException();
sigarException.initCause(linkErrorException);
throw sigarException;
}
}
public ProcCumulativeDiskIO getProcCumulativeDiskIO(String pid) throws SigarException {
return getProcCumulativeDiskIO(convertPid(pid));
}
public DumpPidCache dumpPidCache() throws SigarException {
return DumpPidCache.fetch(this);
}
/**
* Get the cumulative cpu time for the calling thread.
*/
public ThreadCpu getThreadCpu() throws SigarException {
return ThreadCpu.fetch(this, 0);
}
private native FileSystem[] getFileSystemListNative() throws SigarException;
/**
* Get list of file systems.
* @exception SigarException on failure.
*/
public FileSystem[] getFileSystemList() throws SigarException {
FileSystem[] fslist = getFileSystemListNative();
if (this.mounts != null) {
this.mounts.init(fslist);
}
return fslist;
}
/**
* Get file system usage.
* @param name Name of the directory on which filesystem is mounted.
* @exception SigarException on failure.
*/
public FileSystemUsage getFileSystemUsage(String name)
throws SigarException {
if (name == null) {
throw new SigarException("name cannot be null");
}
return FileSystemUsage.fetch(this, name);
}
/**
* Get disk usage.
* @param name Name of disk
* @exception SigarException on failure.
*/
public DiskUsage getDiskUsage(String name)
throws SigarException {
if (name == null) {
throw new SigarException("name cannot be null");
}
return DiskUsage.fetch(this, name);
}
/**
* Get file system usage of a mounted directory.
* This method checks that the given directory is mounted.
* Unlike getFileSystemUsage() which only requires that the
* directory exists within a mounted file system.
* This method will also check that NFS servers are reachable via RPC
* before attempting to get the file system stats to prevent application
* hang when an NFS server is down.
* @param name Name of the directory on which filesystem is mounted.
* @exception SigarException If given directory is not mounted.
* @exception NfsUnreachableException If NFS server is unreachable.
* @see org.hyperic.sigar.Sigar#getFileSystemUsage
*/
public FileSystemUsage getMountedFileSystemUsage(String name)
throws SigarException, NfsUnreachableException {
FileSystem fs = getFileSystemMap().getFileSystem(name);
if (fs == null) {
throw new SigarException(name + " is not a mounted filesystem");
}
if (fs instanceof NfsFileSystem) {
NfsFileSystem nfs = (NfsFileSystem)fs;
if (!nfs.ping()) {
throw nfs.getUnreachableException();
}
}
return FileSystemUsage.fetch(this, name);
}
public FileSystemMap getFileSystemMap()
throws SigarException {
if (this.mounts == null) {
this.mounts = new FileSystemMap();
}
getFileSystemList(); //this.mounts.init()
return this.mounts;
}
public FileInfo getFileInfo(String name)
throws SigarException {
return FileInfo.fetchFileInfo(this, name);
}
public FileInfo getLinkInfo(String name)
throws SigarException {
return FileInfo.fetchLinkInfo(this, name);
}
public DirStat getDirStat(String name)
throws SigarException {
return DirStat.fetch(this, name);
}
public DirUsage getDirUsage(String name)
throws SigarException {
return DirUsage.fetch(this, name);
}
/**
* Get list of cpu infomation.
* @exception SigarException on failure.
*/
public native CpuInfo[] getCpuInfoList() throws SigarException;
private native Cpu[] getCpuListNative() throws SigarException;
/**
* Get list of per-cpu metrics.
* @exception SigarException on failure.
*/
public Cpu[] getCpuList() throws SigarException {
return getCpuListNative();
}
/**
* Get list of network routes.
* @exception SigarException on failure.
*/
public native NetRoute[] getNetRouteList() throws SigarException;
/**
* Get list of network connections.
* @exception SigarException on failure.
*/
public native NetConnection[] getNetConnectionList(int flags)
throws SigarException;
/**
* Get the TCP listen address for the given port.
* If there is not a listener on the given port, null will be returned.
*/
public native String getNetListenAddress(long port)
throws SigarException;
public String getNetListenAddress(String port)
throws SigarException {
return getNetListenAddress(Long.parseLong(port));
}
public native String getNetServicesName(int protocol, long port);
public NetStat getNetStat()
throws SigarException {
NetStat netstat = new NetStat();
netstat.stat(this);
return netstat;
}
public NetStat getNetStat(byte[] address, long port)
throws SigarException {
NetStat netstat = new NetStat();
netstat.stat(this, address, port);
return netstat;
}
public native Arp[] getArpList()
throws SigarException;
public native Who[] getWhoList()
throws SigarException;
/**
* TCP-MIB stats
* @exception SigarException on failure.
*/
public Tcp getTcp()
throws SigarException {
return Tcp.fetch(this);
}
public NfsClientV2 getNfsClientV2()
throws SigarException {
return NfsClientV2.fetch(this);
}
public NfsServerV2 getNfsServerV2()
throws SigarException {
return NfsServerV2.fetch(this);
}
public NfsClientV3 getNfsClientV3()
throws SigarException {
return NfsClientV3.fetch(this);
}
public NfsServerV3 getNfsServerV3()
throws SigarException {
return NfsServerV3.fetch(this);
}
/**
* Get general network info.
* @exception SigarException on failure.
*/
public NetInfo getNetInfo()
throws SigarException {
return NetInfo.fetch(this);
}
/**
* Get network interface configuration info.
* @exception SigarException on failure.
*/
public NetInterfaceConfig getNetInterfaceConfig(String name)
throws SigarException {
return NetInterfaceConfig.fetch(this, name);
}
/**
* Get default network interface configuration info.
* Iterates getNetInterfaceList(), returning the first
* available ethernet interface.
* @exception SigarException on failure.
*/
public NetInterfaceConfig getNetInterfaceConfig()
throws SigarException {
String[] interfaces = getNetInterfaceList();
for (int i=0; i<interfaces.length; i++) {
String name = interfaces[i];
NetInterfaceConfig ifconfig;
try {
ifconfig = getNetInterfaceConfig(name);
} catch (SigarException e) {
continue;
}
long flags = ifconfig.getFlags();
if ((flags & NetFlags.IFF_UP) <= 0) {
continue;
}
if ((flags & NetFlags.IFF_POINTOPOINT) > 0) {
continue;
}
if ((flags & NetFlags.IFF_LOOPBACK) > 0) {
continue;
}
return ifconfig;
}
String msg =
"No ethernet interface available";
throw new SigarException(msg);
}
/**
* Get network interface stats.
* @exception SigarException on failure.
*/
public NetInterfaceStat getNetInterfaceStat(String name)
throws SigarException {
return NetInterfaceStat.fetch(this, name);
}
/**
* Get the list of configured network interface names.
* @exception SigarException on failure.
*/
public native String[] getNetInterfaceList() throws SigarException;
/**
* Prompt for a password, disabling terminal echo
* during user input.
* @param prompt Text printed before disabling echo
* @return Text entered by the user.
* @throws IOException If input could not be read.
* @throws SigarNotImplementedException If the native method
* is not implemented on the current platform.
*/
native static String getPasswordNative(String prompt)
throws IOException, SigarNotImplementedException;
/**
* Prompt for a password, disabling terminal echo
* during user input if possible.
* @param prompt Text printed before disabling echo
* @return Text entered by the user.
* @throws IOException If input could not be read.
*/
public static String getPassword(String prompt)
throws IOException
{
try {
return getPasswordNative(prompt);
} catch (IOException e) {
throw e;
} catch (SigarNotImplementedException e) {
//fallthrough
}
//fallback if native .so was not loaded or not supported
System.out.print(prompt);
return (new BufferedReader(new InputStreamReader(System.in))).
readLine();
}
/**
* Reliably retrieve the FQDN for a machine
*
* @return The fully qualified domain name of the machine.
* @exception SigarException on failure.
*/
public native String getFQDN() throws SigarException;
public SigarVersion getSigarVersion() {
return new SigarVersion();
}
/**
* Enabling logging in the native Sigar code.
* This method will hook log4j into the Sigar
* native logging methods. Note that the majority
* of logging in the native code is only at the DEBUG
* level.
*/
public void enableLogging(boolean value) {
if (value) {
SigarLog.enable(this);
}
else {
SigarLog.disable(this);
}
}
}