/*
* RHQ Management Platform
* Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.core.system;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.sigar.DirUsage;
import org.hyperic.sigar.FileSystem;
import org.hyperic.sigar.FileSystemMap;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.NetConnection;
import org.hyperic.sigar.NetFlags;
import org.hyperic.sigar.NetInterfaceStat;
import org.hyperic.sigar.OperatingSystem;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.SigarProxy;
import org.hyperic.sigar.Swap;
import org.rhq.core.system.pquery.ProcessInfoQuery;
/**
* The superclass for all the native {@link SystemInfo} implementations. You are free to subclass this implementation if
* there are additional platform-specific methods that need to be exposed. Most functionality, however, can be exposed
* via this native superclass implementation.
*
* <p>This implementation uses SIGAR. To enable debug logging in SIGAR, set the system property
* <code>sigar.nativeLogging</code> or call {@link Sigar#enableLogging(boolean)}.</p>
*
* @author John Mazzitelli
*/
public class NativeSystemInfo implements SystemInfo {
private final Log log = LogFactory.getLog(NativeSystemInfo.class);
private SigarProxy sigar;
/**
* Always returns <code>true</code> to indicate that the native library is available.
*
* @see SystemInfo#isNative()
*/
public boolean isNative() {
return true;
}
public OperatingSystemType getOperatingSystemType() {
OperatingSystem os = OperatingSystem.getInstance();
if (OperatingSystem.NAME_LINUX.equals(os.getName())) {
return OperatingSystemType.LINUX;
}
if (OperatingSystem.NAME_SOLARIS.equals(os.getName())) {
return OperatingSystemType.SOLARIS;
}
if (OperatingSystem.NAME_WIN32.equals(os.getName())) {
return OperatingSystemType.WINDOWS;
}
if (OperatingSystem.NAME_HPUX.equals(os.getName())) {
return OperatingSystemType.HPUX;
}
if (OperatingSystem.NAME_AIX.equals(os.getName())) {
return OperatingSystemType.AIX;
}
if (OperatingSystem.NAME_MACOSX.equals(os.getName())) {
return OperatingSystemType.OSX;
}
if (OperatingSystem.NAME_FREEBSD.equals(os.getName())) {
return OperatingSystemType.BSD;
}
log.warn("Could not parse operating system name from " + os.getName() + ", returning Java platform");
return OperatingSystemType.JAVA;
}
public String getOperatingSystemName() {
OperatingSystem os = OperatingSystem.getInstance();
// SIGAR returns "Win32" as the OS name for all Windows systems, even 64-bit ones. Work around this by instead
// returning "Windows" for all Windows systems, which is more consistent with the UNIX operating systems anyway.
// (https://jira.hyperic.com/browse/SIGAR-238)
return (OperatingSystem.NAME_WIN32.equals(os.getName()) ? "Windows" : os.getName());
}
public String getOperatingSystemVersion() {
return OperatingSystem.getInstance().getVersion();
}
public String getHostname() throws SystemInfoException {
try {
return sigar.getNetInfo().getHostName();
} catch (Exception e) {
// For some reason, the native layer failed to get the hostname.
// Let's fallback and ask Java for help. But if that fails too,
// let's wrap the native layer's exception, since we'll want to
// see its cause, which will probably have a more descriptive error message.
try {
return InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException uhe) {
throw new SystemInfoException(e);
}
}
}
public List<NetworkAdapterInfo> getAllNetworkAdapters() throws SystemInfoException {
List<NetworkAdapterInfo> adapters = new ArrayList<NetworkAdapterInfo>();
try {
String[] interfaceNames = sigar.getNetInterfaceList();
if (interfaceNames != null) {
for (String interfaceName : interfaceNames) {
if (interfaceName.indexOf(':') != -1) {
continue; //filter out virtual IPs
}
adapters.add(new NetworkAdapterInfo(sigar.getNetInterfaceConfig(interfaceName)));
}
}
} catch (Exception e) {
throw new SystemInfoException(e);
}
return adapters;
}
public NetworkAdapterStats getNetworkAdapterStats(String interfaceName) {
try {
NetInterfaceStat interfaceStat = sigar.getNetInterfaceStat(interfaceName);
return new NetworkAdapterStats(interfaceStat);
} catch (SigarException e) {
throw new SystemInfoException(e);
}
}
public NetworkStats getNetworkStats(String addressName, int port) {
List<NetConnection> matches = getNetworkConnections(addressName, port);
NetworkStats stats = new NetworkStats(matches.toArray(new NetConnection[matches.size()]));
return stats;
}
public List<NetConnection> getNetworkConnections(String addressName, int port) {
try {
int flags = NetFlags.CONN_SERVER | NetFlags.CONN_CLIENT | NetFlags.CONN_TCP;
NetConnection[] conns = sigar.getNetConnectionList(flags);
InetAddress matchAddress = (addressName != null) ? InetAddress.getByName(addressName) : null;
List<NetConnection> list = new ArrayList<NetConnection>();
for (NetConnection conn : conns) {
if (port > 0 && (conn.getLocalPort() != port)) {
continue; // does not match the port we are looking for
}
if (matchAddress != null && !matchAddress.equals(InetAddress.getByName(conn.getLocalAddress()))) {
continue; // does not match the address we are looking for
}
list.add(conn); // matches our criteria, add it to the list to be returned to the caller
}
return list;
} catch (SigarException e) {
throw new SystemInfoException(e);
} catch (UnknownHostException e) {
throw new SystemInfoException(e);
}
}
private List<InetAddress> getInetAddressInList(String address) throws UnknownHostException {
List<InetAddress> inetAddresses = new ArrayList<InetAddress>();
if (address != null) {
inetAddresses.add(InetAddress.getByName(address));
}
return inetAddresses;
}
public List<ServiceInfo> getAllServices() throws SystemInfoException {
throw new UnsupportedOperationException("Cannot get services for this platform");
}
public List<ProcessInfo> getAllProcesses() {
ArrayList<ProcessInfo> processes = new ArrayList<ProcessInfo>();
long[] pids = null;
log.debug("Retrieving PIDs of all running processes...");
long startTime = System.currentTimeMillis();
try {
pids = sigar.getProcList();
long elapsedTime = System.currentTimeMillis() - startTime;
log.debug("Retrieval of " + pids.length + " PIDs took " + elapsedTime + " ms.");
// NOTE: Do not close sigarImpl on success, as the ProcessInfos created below will reuse it.
} catch (Exception e) {
log.warn("Failed to retrieve PIDs of all running processes.", e);
}
if (pids != null) {
for (long pid : pids) {
if (log.isTraceEnabled()) {
log.trace("Loading process info for pid " + pid + "...");
}
ProcessInfo info = new ProcessInfo(pid, sigar);
processes.add(info);
}
}
return processes;
}
public List<ProcessInfo> getProcesses(String piq) {
ProcessInfoQuery piql = new ProcessInfoQuery(getAllProcesses());
return piql.query(piq);
}
public ProcessInfo getThisProcess() {
long self = sigar.getPid();
ProcessInfo info = new ProcessInfo(self);
return info;
}
public ProcessExecutionResults executeProcess(ProcessExecution processExecution) {
// TODO: doesn't look like SIGAR has an API to fork/execute processes? fallback to using the Java way
return SystemInfoFactory.createJavaSystemInfo().executeProcess(processExecution);
}
public int getNumberOfCpus() {
try {
// NOTE: This will return the number of cores, not the number of sockets.
return sigar.getCpuPercList().length;
} catch (Exception e) {
throw new UnsupportedOperationException("Cannot get number of CPUs from native layer", e);
}
}
public Mem getMemoryInfo() {
try {
return sigar.getMem();
} catch (Exception e) {
throw new UnsupportedOperationException("Cannot get memory info from native layer", e);
}
}
public Swap getSwapInfo() {
try {
// Removed this check since http://jira.hyperic.com/browse/SIGAR-112 is fixed.
/*
int enabledCpuCount = sigar.getCpuPercList().length;
int totalCpuCount = sigar.getCpuInfoList().length;
if (enabledCpuCount < totalCpuCount) {
log.info("Aborting swap info collection because one or more CPUs is disabled - " + enabledCpuCount
+ " out of " + totalCpuCount + " CPUs are enabled.");
return null;
}
*/
return sigar.getSwap();
} catch (Exception e) {
throw new UnsupportedOperationException("Cannot get swap info from native layer", e);
}
}
public String readLineFromConsole(boolean noEcho) throws IOException {
String input;
if (noEcho) {
input = Sigar.getPassword("");
} else {
input = new BufferedReader(new InputStreamReader(System.in)).readLine();
}
return input;
}
public void writeLineToConsole(String line) throws IOException {
System.out.print(line); // note: don't use println - let the caller append newline char to 'line' if needed
}
public CpuInformation getCpu(int cpuIndex) {
return new CpuInformation(cpuIndex, sigar);
}
private List<FileSystemInfo> getFileSystems(boolean deferredUsageInfo) {
List<String> mountPoints = new ArrayList<String>();
try {
FileSystemMap map = sigar.getFileSystemMap();
mountPoints.addAll(map.keySet());
} catch (Exception e) {
log.warn("Cannot obtain native file system information", e); // ignore native error otherwise
}
List<FileSystemInfo> infos = new ArrayList<FileSystemInfo>();
for (String mountPoint : mountPoints) {
infos.add(new FileSystemInfo(mountPoint, deferredUsageInfo));
}
return infos;
}
public List<FileSystemInfo> getFileSystems() {
return getFileSystems(false);
}
public List<FileSystemInfo> getFileSystemsDeferredUsageInfo() {
return getFileSystems(true);
}
public FileSystemInfo getFileSystem(String path) {
String mountPoint = null;
try {
FileSystem mountPointForPath = sigar.getFileSystemMap().getMountPoint(path);
if (mountPointForPath != null)
mountPoint = mountPointForPath.getDirName();
} catch (Throwable e) {
log.warn("Cannot obtain native file system information for [" + path + "]", e); // ignore native error otherwise
}
FileSystemInfo fileSystem = new FileSystemInfo(mountPoint);
return fileSystem;
}
@Override
public DirUsage getDirectoryUsage(String path) {
DirUsage dirUsage = null;
try {
dirUsage = sigar.getDirUsage(path);
} catch (SigarException e) {
log.warn("Can not get directory usage for [" + path + "] cause: " + e.getMessage());
return null;
}
return dirUsage;
}
public String getSystemArchitecture() {
OperatingSystem op = OperatingSystem.getInstance();
return op.getArch();
}
/**
* Constructor for {@link NativeSystemInfo} with package scope so only the {@link SystemInfoFactory} can instantiate
* this object.
*/
public NativeSystemInfo() {
this.sigar = SigarAccess.getSigar();
}
}