/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.systeminfo.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.hyperic.sigar.FileSystem; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.SigarException; import org.hyperic.sigar.SigarProxy; import org.hyperic.sigar.SigarProxyCache; import org.hyperic.sigar.ptql.ProcessFinder; import org.openhab.binding.systeminfo.SysteminfoBindingProvider; import org.openhab.core.binding.AbstractActiveBinding; import org.openhab.core.items.Item; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.StringType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Binding for system and process information gathering. * * @author Pauli Anttila * @since 1.3.0 */ public class SysteminfoBinding extends AbstractActiveBinding<SysteminfoBindingProvider>implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(SysteminfoBinding.class); /** the interval to find new refresh candidates (defaults to 1000 milliseconds) */ private int granularity = 1000; /** the unit to measure keyfacts (defaults to 'M') */ private char units = 'M'; private Map<String, Long> lastUpdateMap = new HashMap<String, Long>(); private static Sigar sigarImpl; private static SigarProxy sigar; @Override public void activate() { } @Override public void deactivate() { sigar = null; sigarImpl = null; } /** * @{inheritDoc */ @Override protected long getRefreshInterval() { return granularity; } /** * @{inheritDoc */ @Override protected String getName() { return "Systeminfo Refresh Service"; } /** * @{inheritDoc */ @Override protected void execute() { for (SysteminfoBindingProvider provider : providers) { for (String itemName : provider.getItemNames()) { int refreshInterval = provider.getRefreshInterval(itemName); Long lastUpdateTimeStamp = lastUpdateMap.get(itemName); if (lastUpdateTimeStamp == null) { lastUpdateTimeStamp = 0L; } long age = System.currentTimeMillis() - lastUpdateTimeStamp; boolean needsUpdate = age >= refreshInterval; if (needsUpdate) { logger.debug("item '{}' is about to be refreshed now", itemName); SysteminfoCommandType commmandType = provider.getCommandType(itemName); Class<? extends Item> itemType = provider.getItemType(itemName); String target = provider.getTarget(itemName); State state = getData(commmandType, itemType, target); if (state != null) { eventPublisher.postUpdate(itemName, state); } else { logger.error("No response received from command '{}'", commmandType); } lastUpdateMap.put(itemName, System.currentTimeMillis()); } } } } private State getData(SysteminfoCommandType commandType, Class<? extends Item> itemType, String target) { State state = UnDefType.UNDEF; long pid; try { switch (commandType) { case LOAD_AVERAGE_1MIN: state = new DecimalType(sigar.getLoadAverage()[0]); break; case LOAD_AVERAGE_5MIN: state = new DecimalType(sigar.getLoadAverage()[1]); break; case LOAD_AVERAGE_15MIN: state = new DecimalType(sigar.getLoadAverage()[2]); break; case CPU_COMBINED: state = new DecimalType(sigar.getCpuPerc().getCombined() * 100); break; case CPU_USER: state = new DecimalType(sigar.getCpuPerc().getUser() * 100); break; case CPU_SYSTEM: state = new DecimalType(sigar.getCpuPerc().getSys() * 100); break; case CPU_NICE: state = new DecimalType(sigar.getCpuPerc().getNice() * 100); break; case CPU_WAIT: state = new DecimalType(sigar.getCpuPerc().getWait() * 100); break; case UPTIME: state = new DecimalType(sigar.getUptime().getUptime()); break; case UPTIME_FORMATTED: state = new StringType(getElapsedTime((long) sigar.getUptime().getUptime())); break; case MEM_FREE_PERCENT: state = new DecimalType(sigar.getMem().getFreePercent()); break; case MEM_USED_PERCENT: state = new DecimalType(sigar.getMem().getUsedPercent()); break; case MEM_FREE: state = new DecimalType(formatBytes(sigar.getMem().getFree(), units)); break; case MEM_USED: state = new DecimalType(formatBytes(sigar.getMem().getUsed(), units)); break; case MEM_ACTUAL_FREE: state = new DecimalType(formatBytes(sigar.getMem().getActualFree(), units)); break; case MEM_ACTUAL_USED: state = new DecimalType(formatBytes(sigar.getMem().getActualUsed(), units)); break; case MEM_TOTAL: state = new DecimalType(formatBytes(sigar.getMem().getTotal(), units)); break; case SWAP_FREE: state = new DecimalType(formatBytes(sigar.getSwap().getFree(), units)); break; case SWAP_TOTAL: state = new DecimalType(formatBytes(sigar.getSwap().getTotal(), units)); break; case SWAP_USED: state = new DecimalType(formatBytes(sigar.getSwap().getUsed(), units)); break; case SWAP_PAGE_IN: state = new DecimalType(formatBytes(sigar.getSwap().getPageIn(), units)); break; case SWAP_PAGE_OUT: state = new DecimalType(formatBytes(sigar.getSwap().getPageOut(), units)); break; case NET_RX_BYTES: state = new DecimalType(formatBytes(sigar.getNetInterfaceStat(target).getRxBytes(), units)); break; case NET_TX_BYTES: state = new DecimalType(formatBytes(sigar.getNetInterfaceStat(target).getTxBytes(), units)); break; case DISK_READS: state = new DecimalType(sigar.getDiskUsage(target).getReads()); break; case DISK_WRITES: state = new DecimalType(sigar.getDiskUsage(target).getWrites()); break; case DISK_READ_BYTES: state = new DecimalType(formatBytes(sigar.getDiskUsage(target).getReadBytes(), units)); break; case DISK_WRITE_BYTES: state = new DecimalType(formatBytes(sigar.getDiskUsage(target).getWriteBytes(), units)); break; case FS_USED: state = new DecimalType(formatBytes(sigar.getFileSystemUsage(target).getUsed() * 1024, units)); break; case FS_FREE: state = new DecimalType(formatBytes(sigar.getFileSystemUsage(target).getFree() * 1024, units)); break; case FS_TOTAL: state = new DecimalType(formatBytes(sigar.getFileSystemUsage(target).getTotal() * 1024, units)); break; case FS_USE_PERCENT: state = new DecimalType(sigar.getFileSystemUsage(target).getUsePercent() * 100); break; case FS_FILES: state = new DecimalType(sigar.getFileSystemUsage(target).getFiles()); break; case FS_FREE_FILES: state = new DecimalType(sigar.getFileSystemUsage(target).getFreeFiles()); break; case DIR_USAGE: state = new DecimalType(formatBytes(sigar.getDirUsage(target).getDiskUsage(), units)); break; case DIR_FILES: state = new DecimalType(sigar.getDirUsage(target).getFiles()); break; case PROCESS_REAL_MEM: pid = getPid(target); state = new DecimalType(formatBytes(sigar.getProcMem(pid).getResident(), units)); break; case PROCESS_VIRTUAL_MEM: pid = getPid(target); state = new DecimalType(formatBytes(sigar.getProcMem(pid).getSize(), units)); break; case PROCESS_CPU_PERCENT: pid = getPid(target); state = new DecimalType(sigar.getProcCpu(pid).getPercent() * 100); break; case PROCESS_CPU_SYSTEM: pid = getPid(target); state = new DecimalType(sigar.getProcCpu(pid).getSys()); break; case PROCESS_CPU_USER: pid = getPid(target); state = new DecimalType(sigar.getProcCpu(pid).getUser()); break; case PROCESS_CPU_TOTAL: pid = getPid(target); state = new DecimalType(sigar.getProcCpu(pid).getTotal()); break; case PROCESS_UPTIME: pid = getPid(target); state = new DecimalType(getProcessUptime(pid)); break; case PROCESS_UPTIME_FORMATTED: pid = getPid(target); state = new StringType(getElapsedTime(getProcessUptime(pid))); break; default: break; } } catch (SigarException e) { logger.error("Error occured while reading KPI's", e); } return state; } private long getProcessUptime(long pid) throws SigarException { long processStartTime = sigar.getProcTime(pid).getStartTime(); long currentTime = System.currentTimeMillis(); return (currentTime - processStartTime) / 1000; } private long getPid(String processName) throws SigarException { long pid; ProcessFinder processFinder = new ProcessFinder(sigarImpl); String query; if (processName.equals("$$")) { pid = sigar.getPid(); logger.debug("Return own pid {}", pid); return pid; } else if (processName.startsWith("*")) { query = "State.Name.sw=" + processName.replace("*", ""); } else if (processName.endsWith("*")) { query = "State.Name.ew=" + processName.replace("*", ""); } else if (processName.startsWith("=")) { query = "State.Name.eq=" + processName.replace("=", ""); } else if (processName.startsWith("#")) { query = processName.replace("#", ""); } else { query = "State.Name.ct=" + processName; } logger.debug("Query pid by '{}'", query); pid = processFinder.findSingleProcess(query); logger.debug("Return pid {}", pid); return pid; } private static String getElapsedTime(long sec) { final int SECOND = 1; final int MINUTE = 60 * SECOND; final int HOUR = 60 * MINUTE; final int DAY = 24 * HOUR; StringBuffer text = new StringBuffer(""); if (sec > DAY) { if (sec < (2 * DAY)) { text.append(sec / DAY).append(" day "); } else { text.append(sec / DAY).append(" days "); } sec %= DAY; } text.append(sec / HOUR).append(":"); sec %= HOUR; text.append(String.format("%02d", sec / MINUTE)); return text.toString(); } private double formatBytes(double value, char units) { double retval = 0; switch (units) { case 'K': retval = value / 1024; break; case 'M': retval = value / (1024 * 1024); break; case 'G': retval = value / (1024 * 1024 * 1024); break; case 'B': default: retval = value; break; } return retval; } protected void addBindingProvider(SysteminfoBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(SysteminfoBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } /** * {@inheritDoc} */ @Override public void updated(Dictionary<String, ?> config) throws ConfigurationException { String variant = null; if (config != null) { String granularityString = (String) config.get("granularity"); if (StringUtils.isNotBlank(granularityString)) { granularity = Integer.parseInt(granularityString); } logger.debug("Granularity: {} ms", granularity); variant = (String) config.get("variant"); logger.debug("Variant: {}", variant); String tmp = (String) config.get("units"); if (StringUtils.isNotBlank(tmp)) { if (tmp.length() != 1) { throw new ConfigurationException("units", "Illegal units length"); } if (!tmp.matches("[BKMGT]")) { throw new ConfigurationException("units", "Illegal units"); } units = tmp.charAt(0); } logger.debug("Using units: {}", units); } logger.debug("About to initialize system monitor..."); try { initializeSystemMonitor(variant); } catch (Throwable t) { logger.error("Error initializing system monitor", t); } logger.debug("Initialized system monitor."); setProperlyConfigured(true); } private void initializeSystemMonitor(String variant) { if (sigarImpl == null) { Sigar.variant = variant; sigarImpl = new Sigar(); } if (sigar == null) { sigar = SigarProxyCache.newInstance(sigarImpl, 1000); } logger.info("Using Sigar version {}", Sigar.VERSION_STRING); logger.info("Using native version {}", Sigar.NATIVE_VERSION_STRING); try { String[] interfaces = sigar.getNetInterfaceList(); logger.debug("valid net interfaces: {}", Arrays.toString(interfaces)); FileSystem[] filesystems = sigar.getFileSystemList(); logger.debug("file systems: {}", Arrays.toString(filesystems)); List<String> disks = new ArrayList<String>(); for (int i = 0; i < filesystems.length; i++) { FileSystem fs = filesystems[i]; if (fs.getType() == FileSystem.TYPE_LOCAL_DISK) { disks.add(fs.getDevName()); } } logger.debug("valid disk names: {}", Arrays.toString(disks.toArray())); } catch (SigarException e) { logger.error("System monitor error:", e); } } }