diff --git a/README b/README new file mode 100644 index 00000000..94e22452 --- /dev/null +++ b/README @@ -0,0 +1,50 @@ +sigar - System Info Gather And Reporter + +goal of this api is provide a portable interface for gathering system +information such as: + +- system memory (total, used, etc.) + +- system cpu (total, sys, idle, etc.) + +- system swap (total, free, etc.) + +- process memory usage (size, vsize, etc.) + +- process cpu usage (user, sys, etc.) + +- process credentials (uid, gid, ppid, etc.) + +- process times (start time, user cpu, system cpu, etc.) + +- process state (name, state, etc.) + +- process list (list of active process pids) + +- process arguments + +- process environment + +- filesystem list (list of mounted filesystems: fs type, fs name, etc.) + +- filesystem usage (total blocks, blocks free, files, etc.) + +- load average + +- uptime + +- netload (packets in, packets out, bytes in, bytes out, etc.) + +the api used by applications will be the same for all platforms, +underneath the platform vendor api(s) are used to fill in the portable +sigar structures: + +- linux => /proc filesystem, native system calls + +- solaris => kstat api + /proc filesystem + +- hpux => pstat api + +- win32 => registry performance "api", sdk calls + +- aix => /dev/kmem, native system calls diff --git a/bindings/dotnet/default.build b/bindings/dotnet/default.build new file mode 100644 index 00000000..e1f15460 --- /dev/null +++ b/bindings/dotnet/default.build @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bindings/dotnet/examples/CpuInfo.cs b/bindings/dotnet/examples/CpuInfo.cs new file mode 100644 index 00000000..b7c7d2e8 --- /dev/null +++ b/bindings/dotnet/examples/CpuInfo.cs @@ -0,0 +1,21 @@ +using System; +using Hyperic.Sigar; + +public class CpuInfo { + + public static void Main() { + Sigar sigar = new Sigar(); + + Hyperic.Sigar.CpuInfo[] infos = + sigar.CpuInfos(); + + System.Console.WriteLine(infos.Length + " total CPUs.."); + + foreach (Hyperic.Sigar.CpuInfo info in infos) { + System.Console.WriteLine("Vendor........" + info.Vendor); + System.Console.WriteLine("Model........." + info.Model); + System.Console.WriteLine("Mhz..........." + info.Mhz); + System.Console.WriteLine(""); + } + } +} diff --git a/bindings/dotnet/examples/Df.cs b/bindings/dotnet/examples/Df.cs new file mode 100644 index 00000000..605890dd --- /dev/null +++ b/bindings/dotnet/examples/Df.cs @@ -0,0 +1,42 @@ +using System; +using Hyperic.Sigar; + +public class Df { + + public static void Main() { + Sigar sigar = new Sigar(); + + foreach (FileSystem fs in sigar.FileSystemList()) { + FileSystemUsage usage; + ulong used, avail, total, pct; + + try { + usage = sigar.FileSystemUsage(fs.DirName); + + used = usage.Total - usage.Free; + avail = usage.Avail; + total = usage.Total; + pct = (ulong)(usage.UsePercent * 100); + } catch (ApplicationException) { + used = avail = total = pct = 0; + continue; + } + + string usePct; + if (pct == 0) { + usePct = "-"; + } + else { + usePct = pct + "%"; + } + + System.Console.WriteLine(fs.DevName + "\t" + + total + "\t" + + used + "\t" + + avail + "\t" + + usePct + "\t" + + fs.DirName + "\t" + + fs.SysTypeName + "/" + fs.TypeName); + } + } +} diff --git a/bindings/dotnet/examples/Free.cs b/bindings/dotnet/examples/Free.cs new file mode 100644 index 00000000..1c4879e7 --- /dev/null +++ b/bindings/dotnet/examples/Free.cs @@ -0,0 +1,26 @@ +using System; +using Hyperic.Sigar; + +public class Free { + + public static void Main() { + Sigar sigar = new Sigar(); + + Mem mem = sigar.Mem(); + Swap swap = sigar.Swap(); + + System.Console.WriteLine("\tTotal\tUsed\tFree"); + + System.Console.WriteLine("Mem:\t" + + mem.Total / 1024 + "\t" + + mem.Used / 1024 + "\t" + + mem.Free / 1024); + + System.Console.WriteLine("Swap:\t" + + swap.Total / 1024 + "\t" + + swap.Used / 1024 + "\t" + + swap.Free / 1024); + + System.Console.WriteLine("RAM:\t" + mem.Ram + "MB"); + } +} diff --git a/bindings/dotnet/src/Sigar.cs b/bindings/dotnet/src/Sigar.cs new file mode 100644 index 00000000..b09f417c --- /dev/null +++ b/bindings/dotnet/src/Sigar.cs @@ -0,0 +1,271 @@ +using System; +using System.IO; +using System.Collections; +using System.Runtime.InteropServices; + +namespace Hyperic.Sigar { + public class Sigar { + public const int OK = 0; + + internal const int FS_NAME_LEN = 64; + + internal const string LIBSIGAR = "sigar-x86-winnt.dll"; + + internal HandleRef sigar; + + [DllImport(LIBSIGAR)] + private static extern IntPtr sigar_new(); + + [DllImport(LIBSIGAR)] + private static extern int sigar_close(IntPtr sigar); + + public Sigar() { + IntPtr handle = sigar_new(); + this.sigar = new HandleRef(this, handle); + } + + public Mem Mem() { + return Hyperic.Sigar.Mem.NativeGet(this); + } + + public Swap Swap() { + return Hyperic.Sigar.Swap.NativeGet(this); + } + + public CpuInfo[] CpuInfos() { + return Hyperic.Sigar.CpuInfos.NativeGet(this); + } + + public FileSystem[] FileSystemList() { + return Hyperic.Sigar.FileSystemList.NativeGet(this); + } + + public FileSystemUsage FileSystemUsage(string dirname) { + return Hyperic.Sigar.FileSystemUsage.NativeGet(this, dirname); + } + + ~Sigar() { + sigar_close(this.sigar.Handle); + } + + internal static IntPtr incrementIntPtr(IntPtr ptr, int size) { + Int32 x = (Int32)ptr; + x += size; + return (IntPtr)x; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct Mem { + public readonly ulong Ram; + public readonly ulong Total; + public readonly ulong Used; + public readonly ulong Free; + public readonly ulong Shared; + private readonly ulong NA_buffer; + private readonly ulong NA_cached; + private readonly ulong NA_user; + + [DllImport(Sigar.LIBSIGAR)] + private static extern int sigar_mem_get(IntPtr sigar, IntPtr mem); + + internal static Mem NativeGet(Sigar sigar) { + Type type = typeof(Mem); + //sigar_mem_t *ptr = malloc(sizeof(*ptr)) + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(type)); + + int status = sigar_mem_get(sigar.sigar.Handle, ptr); + + if (status != Sigar.OK) { + Marshal.FreeHGlobal(ptr); + throw new ApplicationException("mem_get"); + } + + //memcpy(ptr, this, sizeof(this)) + Mem mem = (Mem)Marshal.PtrToStructure(ptr, type); + Marshal.FreeHGlobal(ptr); + return mem; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct Swap { + public readonly ulong Total; + public readonly ulong Used; + public readonly ulong Free; + + [DllImport(Sigar.LIBSIGAR)] + private static extern int sigar_swap_get(IntPtr sigar, IntPtr swap); + + internal static Swap NativeGet(Sigar sigar) { + Type type = typeof(Swap); + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(type)); + + int status = sigar_swap_get(sigar.sigar.Handle, ptr); + + if (status != Sigar.OK) { + Marshal.FreeHGlobal(ptr); + throw new ApplicationException("swap_get"); + } + + Swap swap = (Swap)Marshal.PtrToStructure(ptr, type); + Marshal.FreeHGlobal(ptr); + + return swap; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct CpuInfo { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] + public readonly string Vendor; //char[128] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] + public readonly string Model; //char[128] + public readonly int Mhz; + private readonly ulong CacheSize; //XXX not implemented + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CpuInfos { + private readonly uint Number; //sizeof(unsigned long) == 4 + private readonly uint size; + private readonly IntPtr data; + + [DllImport(Sigar.LIBSIGAR)] + private static extern int sigar_cpu_infos_get(IntPtr sigar, + IntPtr cpu_infos); + + [DllImport(Sigar.LIBSIGAR)] + private static extern int sigar_cpu_infos_destroy(IntPtr sigar, + IntPtr cpu_infos); + + internal static CpuInfo[] NativeGet(Sigar sigar) { + Type type = typeof(CpuInfos); + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(type)); + + int status = sigar_cpu_infos_get(sigar.sigar.Handle, ptr); + + if (status != Sigar.OK) { + Marshal.FreeHGlobal(ptr); + throw new ApplicationException("cpu_infos_get"); + } + + CpuInfos infosPtr = (CpuInfos)Marshal.PtrToStructure(ptr, type); + + CpuInfo[] infos = new CpuInfo[infosPtr.Number]; + + IntPtr eptr = infosPtr.data; + int size = Marshal.SizeOf(infos[0]); + + for (int i=0; i + + + + + + + + + + diff --git a/bindings/java/.cvsignore b/bindings/java/.cvsignore new file mode 100644 index 00000000..60ebd439 --- /dev/null +++ b/bindings/java/.cvsignore @@ -0,0 +1,3 @@ +sigar-bin +build +testresults diff --git a/bindings/java/.project b/bindings/java/.project new file mode 100644 index 00000000..08f4876d --- /dev/null +++ b/bindings/java/.project @@ -0,0 +1,17 @@ + + + sigar + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/bindings/java/build.xml b/bindings/java/build.xml new file mode 100644 index 00000000..22da9e9f --- /dev/null +++ b/bindings/java/build.xml @@ -0,0 +1,205 @@ + +]> + + + + + + + + + + + + + + + &jni-build; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright © 2004 Hyperic, LLC. All Rights Reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bindings/java/lib/bcel-5.1.jar b/bindings/java/lib/bcel-5.1.jar new file mode 100644 index 00000000..524e375c Binary files /dev/null and b/bindings/java/lib/bcel-5.1.jar differ diff --git a/bindings/java/lib/junit.jar b/bindings/java/lib/junit.jar new file mode 100644 index 00000000..674d71e8 Binary files /dev/null and b/bindings/java/lib/junit.jar differ diff --git a/bindings/java/lib/log4j.jar b/bindings/java/lib/log4j.jar new file mode 100644 index 00000000..c7111d8b Binary files /dev/null and b/bindings/java/lib/log4j.jar differ diff --git a/bindings/java/src/jni/generate.pl b/bindings/java/src/jni/generate.pl new file mode 100644 index 00000000..0755faa8 --- /dev/null +++ b/bindings/java/src/jni/generate.pl @@ -0,0 +1,1255 @@ +use strict; +use File::Path; + +my %platforms = ( + A => "AIX", + D => "Darwin", + F => "FreeBSD", + H => "HPUX", + L => "Linux", + S => "Solaris", + W => "Win32", +); + +sub supported_platforms { + my $p = shift; + return 'Undocumented' unless $p; + if ($p eq '*') { + return 'All'; + } + + my @platforms; + for (split //, $p) { + push @platforms, $platforms{$_}; + } + + return join ", ", @platforms; +} + +#this script generates jni code and java classes for the following table + +my %classes = ( + Mem => [ + { + name => 'total', type => 'Long', + desc => 'Total system memory', + plat => '*', + cmd => { + AIX => 'lsattr -El sys0 -a realmem', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => '', + Win32 => 'taskman', + }, + }, + { + name => 'ram', type => 'Long', + desc => 'System Random Access Memory (in MB)', + plat => '*', + cmd => { + AIX => 'lsattr -El sys0 -a realmem', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'cat /proc/mtrr | head -1', + Solaris => '', + Win32 => '', + }, + }, + { + name => 'used', type => 'Long', + desc => 'Total used system memory', + plat => '*', + cmd => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => '', + Win32 => 'taskman', + }, + }, + { + name => 'free', type => 'Long', + desc => 'Total free system memory', + plat => '*', + cmd => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => '', + Win32 => 'taskman', + }, + }, + { + name => 'shared', type => 'Long', + desc => 'Total shared system memory', + plat => 'LSW', + cmd => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => '', + Win32 => '', + }, + }, + ], + Swap => [ + { + name => 'total', type => 'Long', + desc => 'Total system swap', + plat => '*', + cmd => { + AIX => 'lsps -s', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => 'swap -s', + Win32 => '', + }, + }, + { + name => 'used', type => 'Long', + desc => 'Total used system swap', + plat => '*', + cmd => { + AIX => 'lsps -s', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => 'swap -s', + Win32 => '', + }, + }, + { + name => 'free', type => 'Long', + desc => 'Total free system swap', + plat => '*', + cmd => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'free', + Solaris => 'swap -s', + Win32 => '', + }, + }, + ], + Cpu => [ + { + name => 'user', type => 'Long', + desc => 'Total system cpu user time', + plat => '*' + }, + { + name => 'sys', type => 'Long', + desc => 'Total system cpu kernel time', + plat => '*' + }, + { + name => 'nice', type => 'Long', + desc => 'Total system cpu nice time', + plat => 'DFHL' + }, + { + name => 'idle', type => 'Long', + desc => 'Total system cpu idle time', + plat => '*' + }, + { + name => 'total', type => 'Long', + desc => 'Total system cpu time', + plat => '*' + }, + ], + CpuInfo => [ + { + name => 'vendor', type => 'String', + desc => 'CPU vendor id', + plat => 'ALSW' + }, + { + name => 'model', type => 'String', + desc => 'CPU model', + plat => 'ALSW' + }, + { + name => 'mhz', type => 'Int', + desc => 'CPU speed', + plat => 'AHLSW' + }, + { + name => 'cache_size', type => 'Long', + desc => 'CPU cache size', + plat => 'AL' + }, + ], + Uptime => [ + { + name => 'uptime', type => 'Double', + desc => 'Time since machine started in seconds', + plat => '*' + }, + { + name => 'idletime', type => 'Double', + desc => 'Time machine spent idle since start', + plat => 'L' + }, + ], + ProcMem => [ + { + name => 'size', type => 'Long', + desc => 'Total process memory', + plat => 'AHLW' + }, + { + name => 'vsize', type => 'Long', + desc => 'Total process virtual memory', + plat => 'AHLW' + }, + { + name => 'resident', type => 'Long', + desc => 'Total process non-swapped memory', + plat => 'L' + }, + { + name => 'share', type => 'Long', + desc => 'Total process shared memory', + plat => 'AHL' + }, + { + name => 'rss', type => 'Long', + desc => 'Process resident set size', + plat => 'AHL' + }, + ], + ProcCred => [ + { + name => 'uid', type => 'Long', + desc => 'Process user id', + plat => 'ADFHLS' + }, + { + name => 'gid', type => 'Long', + desc => 'Process group id', + plat => 'DFHLS' + }, + { + name => 'euid', type => 'Long', + desc => 'Process effective user id', + plat => 'DFHLS' + }, + { + name => 'egid', type => 'Long', + desc => 'Process effective group id', + plat => 'DFHLS' + }, + ], + ProcCredName => [ + { + name => 'user', type => 'String', + desc => 'Process owner user name', + }, + { + name => 'group', type => 'String', + desc => 'Process owner group name', + }, + ], + ProcTime => [ + { + name => 'start_time', type => 'Long', + desc => 'Time process was started in seconds', + plat => 'ADHLSW' + }, + { + name => 'utime', type => 'Long', + desc => 'Process cpu user time', + plat => 'AHLSW' + }, + { + name => 'stime', type => 'Long', + desc => 'Process cpu kernel time', + plat => 'AHLSW' + }, + ], + ProcState => [ + { + name => 'state', type => 'Char', + desc => 'Process state (Running, Zombie, etc.)', + plat => '*' + }, + { + name => 'name', type => 'String', + desc => 'Name of the process program', + plat => '*' + }, + { + name => 'ppid', type => 'Long', + desc => 'Process parent process id', + plat => 'ADFHLS' + }, + { + name => 'tty', type => 'Int', + desc => 'Device number of rocess controling terminal', + plat => 'HLS' + }, + { + name => 'nice', type => 'Int', + desc => 'Nice value of process', + plat => 'ADHLS' + }, + { + name => 'priority', type => 'Int', + desc => 'Kernel scheduling priority of process', + plat => 'DFHLS' + }, + ], + ProcFd => [ + { + name => 'total', type => 'Long', + desc => 'Total number of open file descriptors', + plat => 'AHLSW' + }, + ], + ProcStat => [ + { + name => 'total', type => 'Long', + desc => 'Total number of processes', + plat => '*' + }, + ], + ProcExe => [ + { + name => 'name', type => 'String', + desc => 'Name of process executable', + plat => 'L', + cmd => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'ls -l /proc/$$/exe', + Solaris => '', + Win32 => '', + }, + }, + { + name => 'cwd', type => 'String', + desc => 'Name of process current working directory', + plat => 'L', + cmd => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'ls -l /proc/$$/cwd', + Solaris => '', + Win32 => '', + }, + }, + ], + FileSystem => [ + { + name => 'dir_name', type => 'String', + desc => 'Directory name', + plat => '*' + }, + { + name => 'dev_name', type => 'String', + desc => 'Device name', + plat => '*' + }, + { + name => 'type_name', type => 'String', + desc => 'File system generic type name', + plat => '*' + }, + { + name => 'sys_type_name', type => 'String', + desc => 'File system os specific type name', + plat => '*' + }, + { + name => 'type', type => 'Int', + desc => 'File system type', + plat => '*' + }, + { + name => 'flags', type => 'Long', + desc => 'File system flags', + plat => '*' + }, + ], + FileSystemUsage => [ + { + name => 'total', type => 'Long', + desc => 'Total bytes of filesystem', + plat => '*' + }, + { + name => 'free', type => 'Long', + desc => 'Total free bytes on filesytem', + plat => '*' + }, + { + name => 'avail', type => 'Long', + desc => 'Total free bytes on filesytem available to caller', + plat => '*' + }, + { + name => 'files', type => 'Long', + desc => 'Total number of file nodes on the filesystem', + plat => 'ADFHLS' + }, + { + name => 'free_files', type => 'Long', + desc => 'Number of free file nodes on the filesystem', + plat => 'ADFHLS' + }, + { + name => 'use_percent', type => 'Double', + desc => 'Percent of disk used', + plat => '*' + }, + ], + FileAttrs => [ + { + name => 'permissions', type => 'Long', + }, + { + name => 'type', type => 'Int', + }, + { + name => 'uid', type => 'Long', + }, + { + name => 'gid', type => 'Long', + }, + { + name => 'inode', type => 'Long', + }, + { + name => 'device', type => 'Long', + }, + { + name => 'nlink', type => 'Long', + }, + { + name => 'size', type => 'Long', + }, + { + name => 'atime', type => 'Long', + }, + { + name => 'ctime', type => 'Long', + }, + { + name => 'mtime', type => 'Long', + }, + ], + DirStat => [ + { + name => 'total', type => 'Long', + }, + { + name => 'files', type => 'Long', + }, + { + name => 'subdirs', type => 'Long', + }, + { + name => 'symlinks', type => 'Long', + }, + { + name => 'chrdevs', type => 'Long', + }, + { + name => 'blkdevs', type => 'Long', + }, + { + name => 'sockets', type => 'Long', + }, + ], + NetRoute => [ + { + name => 'destination', type => 'NetAddr', + desc => '', + plat => 'HLW' + }, + { + name => 'gateway', type => 'NetAddr', + desc => '', + plat => 'HLW' + }, + { + name => 'flags', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'refcnt', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'use', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'metric', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'mask', type => 'NetAddr', + desc => '', + plat => 'HL' + }, + { + name => 'mtu', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'window', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'irtt', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'ifname', type => 'String', + desc => '', + plat => 'L' + }, + ], + NetInterfaceConfig => [ + { + name => 'name', type => 'String', + desc => '', + plat => '*' + }, + { + name => 'hwaddr', type => 'String', + desc => '', + plat => '*' + }, + { + name => 'address', type => 'NetAddr', + desc => '', + plat => '*' + }, + { + name => 'destination', type => 'NetAddr', + desc => '', + plat => '*' + }, + { + name => 'broadcast', type => 'NetAddr', + desc => '', + plat => '*' + }, + { + name => 'netmask', type => 'NetAddr', + desc => '', + plat => '*' + }, + { + name => 'flags', type => 'Long', + desc => '', + plat => '*' + }, + { + name => 'mtu', type => 'Long', + desc => '', + plat => 'DFL' + }, + { + name => 'metric', type => 'Long', + desc => '', + plat => 'DFL' + }, + ], + NetInterfaceStat => [ + { + name => 'rx_bytes', type => 'Long', + desc => '', + plat => '*' + }, + { + name => 'rx_packets', type => 'Long', + desc => '', + plat => '*' + }, + { + name => 'rx_errors', type => 'Long', + desc => '', + plat => '*' + }, + { + name => 'rx_dropped', type => 'Long', + desc => '', + plat => '' + }, + { + name => 'rx_overruns', type => 'Long', + desc => '', + plat => '' + }, + { + name => 'rx_frame', type => 'Long', + desc => '', + plat => '' + }, + { + name => 'tx_bytes', type => 'Long', + desc => '', + plat => '*' + }, + { + name => 'tx_packets', type => 'Long', + desc => '', + plat => '*' + }, + { + name => 'tx_errors', type => 'Long', + desc => '*', + plat => '' + }, + { + name => 'tx_dropped', type => 'Long', + desc => '', + plat => '' + }, + { + name => 'tx_overruns', type => 'Long', + desc => '', + plat => '' + }, + { + name => 'tx_collisions', type => 'Long', + desc => '', + plat => '' + }, + { + name => 'tx_carrier', type => 'Long', + desc => '', + plat => '' + }, + ], + NetConnection => [ + { + name => 'local_port', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'local_address', type => 'String', + desc => '', + plat => 'L' + }, + { + name => 'remote_port', type => 'Long', + desc => '', + plat => 'L' + }, + { + name => 'remote_address', type => 'String', + desc => '', + plat => 'L' + }, + { + name => 'type', type => 'Int', + desc => '', + plat => 'L' + }, + ], + +); + +my %cmds = ( + Mem => { + AIX => 'top', + Darwin => 'top', + FreeBSD => 'top', + HPUX => 'top', + Linux => 'top', + Solaris => 'top', + Win32 => 'taskman', + }, + Swap => { + AIX => 'top', + Darwin => 'top', + FreeBSD => 'top', + HPUX => 'top', + Linux => 'top', + Solaris => 'top', + Win32 => 'taskman', + }, + Cpu => { + AIX => 'top', + Darwin => 'top', + FreeBSD => 'top', + HPUX => 'top', + Linux => 'top', + Solaris => 'top', + Win32 => 'taskman', + }, + CpuInfo => { + AIX => 'lsattr -El proc0', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'cat /proc/cpuinfo', + Solaris => 'psrinfo -v', + Win32 => '', + }, + Uptime => { + AIX => 'uptime', + Darwin => 'uptime', + FreeBSD => 'uptime', + HPUX => 'uptime', + Linux => 'uptime', + Solaris => 'uptime', + Win32 => '', + }, + ProcMem => { + AIX => 'top, ps', + Darwin => 'top, ps', + FreeBSD => 'top, ps', + HPUX => 'top, ps', + Linux => 'top, ps', + Solaris => 'top, ps', + Win32 => 'taskman', + }, + ProcCred => { + AIX => 'top, ps', + Darwin => 'top, ps', + FreeBSD => 'top, ps', + HPUX => 'top, ps', + Linux => 'top, ps', + Solaris => 'top, ps', + Win32 => 'taskman', + }, + ProcTime => { + AIX => 'top, ps', + Darwin => 'top, ps', + FreeBSD => 'top, ps', + HPUX => 'top, ps', + Linux => 'top, ps', + Solaris => 'top, ps', + Win32 => 'taskman', + }, + ProcState => { + AIX => 'top, ps', + Darwin => 'top, ps', + FreeBSD => 'top, ps', + HPUX => 'top, ps', + Linux => 'top, ps', + Solaris => 'top, ps', + Win32 => 'taskman', + }, + ProcFd => { + AIX => 'lsof', + Darwin => 'lsof', + FreeBSD => 'lsof', + HPUX => 'lsof', + Linux => 'lsof', + Solaris => 'lsof', + Win32 => '', + }, + ProcStat => { + AIX => 'top, ps', + Darwin => 'top, ps', + FreeBSD => 'top, ps', + HPUX => 'top, ps', + Linux => 'top, ps', + Solaris => 'top, ps', + Win32 => 'taskman', + }, + FileSystemUsage => { + AIX => 'df', + Darwin => 'df', + FreeBSD => 'df', + HPUX => 'df', + Linux => 'df', + Solaris => 'df', + Win32 => '', + }, + NetRoute => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'route -n', + Solaris => '', + Win32 => '', + }, + NetInterfaceConfig => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'ifconfig', + Solaris => 'ifconfig -a', + Win32 => '', + }, + NetInterfaceStat => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '/usr/sbin/lanadmin -g mibstats 0, netstat -i', + Linux => 'ifconfig', + Solaris => '', + Win32 => '', + }, + NetConnection => { + AIX => '', + Darwin => '', + FreeBSD => '', + HPUX => '', + Linux => 'netstat', + Solaris => '', + Win32 => '', + }, +); + +my %jfields = ( + Long => "J", + Double => "D", + Int => "I", + Char => "C", + String => "Ljava/lang/String;", +); + +my %pfields = ( + Long => "UV", + Double => "double", + Int => "IV", + Char => "char", + String => "char *", +); + +$jfields{'NetAddr'} = $jfields{'String'}; + +$pfields{'NetAddr'} = 'UV'; #XXX in java is a String + +my %jinit = ( + String => 'null', +); + +my %jtype = ( + String => 'String', +); + +#alias +for my $j (\%jfields, \%jinit, \%jtype) { + $j->{'NetAddr'} = $j->{'String'}; +} + +my %func_alias = ( +); + +my $cfile = 'javasigar_generated.c'; +my $hfile = 'javasigar_generated.h'; +my $pfile = 'Sigar_generated.xs'; + +if ((stat $0)[9] < (stat "src/jni/$cfile")[9]) { + print "$cfile unchanged\n"; + exit; +} + +print "generating $cfile\n"; + +my $build_src = $ARGV[0] or die "usage: $0 build_directory"; + +if (! -d $build_src) { + die "$build_src: $!"; +} + +chdir $build_src; + +my $jsrc = 'net/hyperic/sigar'; +mkpath([$jsrc], 0, 0755) unless -d $jsrc; + +open CFH, ">$cfile" or die "open $cfile: $!"; +open HFH, ">$hfile" or die "open $hfile: $!"; +open PFH, ">$pfile" or die "open $pfile: $!"; + +my $datestamp = scalar localtime; + +my $warning = < <<'EOF', + public static final int TYPE_UNKNOWN = 0; + public static final int TYPE_NONE = 1; + public static final int TYPE_LOCAL_DISK = 2; + public static final int TYPE_NETWORK = 3; + public static final int TYPE_RAM_DISK = 4; + public static final int TYPE_CDROM = 5; + public static final int TYPE_SWAP = 6; +EOF + NetConnection => <<'EOF', + public native String getTypeString(); +EOF + Mem => <<'EOF', + public String toString() { + return + "Mem: " + + (this.total / 1024) + "K av, " + + (this.used / 1024) + "K used, " + + (this.free / 1024) + "K free, " + + (this.shared / 1024) + "K shrd"; + } +EOF + Swap => <<'EOF', + public String toString() { + return + "Swap: " + + (this.total / 1024) + "K av, " + + (this.used / 1024) + "K used, " + + (this.free / 1024) + "K free"; + } +EOF + ProcState => <<'EOF', + public static final char SLEEP = 'S'; + public static final char RUN = 'R'; + public static final char STOP = 'T'; + public static final char ZOMBIE = 'Z'; + public static final char IDLE = 'D'; +EOF +); + +my %has_name_arg = map { $_, 1 } qw(FileSystemUsage FileAttrs DirStat + NetInterfaceConfig NetInterfaceStat); +my %proc_no_arg = map { $_, 1 } qw(stat); +my %get_not_impl = map { $_, 1 } qw(net_route net_connection + cpu_info file_system); #list funcs only + +my %field_cache; +my $i = 0; +while (my($class, $fields) = each %classes) { + next if $field_cache{$class}++; + print HFH "#define JSIGAR_FIELDS_\U$class $i\n"; + $i++; + my $n = 0; + for my $field (@$fields) { + my $name = $field->{name}; + print HFH "# define JSIGAR_FIELDS_\U${class}_${name} $n\n"; + $n++; + } + print HFH "# define JSIGAR_FIELDS_\U${class}_MAX $n\n"; +} +print HFH "#define JSIGAR_FIELDS_MAX $i\n"; + +while (my($name, $fields) = each %classes) { + my $java_class = "net.hyperic.sigar.$name"; + (my $jni_prefix = "Java.$java_class") =~ s/\./_/g; + my $class = $name; + my $cname; + my $args_proto = ""; + my($arg, $arg_type); + my $args = ""; + my $is_proc = 0; + my $decl_string = ""; + my $get_string = ""; + my $release_string = ""; + + my $jname = lcfirst $name; + + #example: FileSystemUsage -> file_system_usage + ($cname = $name) =~ s/([a-z])([A-Z])/$1_$2/g; + $cname = lc $cname; + + if ($cname =~ /^proc_(\w+)/) { + unless ($proc_no_arg{$1}) { + $args_proto = ", jlong pid"; + $arg_type = 'sigar_pid_t'; + $arg = 'pid'; + $args = " $arg, "; + $is_proc = 1; + } + } + elsif ($has_name_arg{$name}) { + #hallo freulien + $args_proto = ", jstring jname"; + $arg_type = 'const char *'; + $decl_string = "const char *name;"; + $get_string = "name = JENV->GetStringUTFChars(env, jname, 0);"; + $release_string = "JENV->ReleaseStringUTFChars(env, jname, name);"; + $arg = 'name'; + $args = " $arg, "; + } + + my $sigar_prefix = join '_', 'sigar', $cname; + + my $sigar_function = join '_', $sigar_prefix, 'get'; + $sigar_function = $func_alias{$sigar_function} || $sigar_function; + my $sigar_type = join '_', $sigar_prefix, 't'; + + my $nativefunc = join '_', $jni_prefix, 'nativeGet'; + + my $proto = join "\n", + "JNIEXPORT void JNICALL $nativefunc", + "(JNIEnv *env, jobject obj, jobject sigar_obj$args_proto)"; + + my $jfile = "$name.java"; + open JFH, ">$jsrc/$jfile" or die "open $jfile: $!"; + print JFH $warning; + + my $impl = ! $get_not_impl{$cname}; + + print CFH <GetObjectClass(env, obj); + $decl_string + dSIGAR_VOID; + + $get_string + + status = $sigar_function(sigar,${args}&s); + + $release_string + + if (status != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return; + } + +EOF + + my $jargs_proto = 'Sigar sigar'; + my $jargs = 'sigar'; + + if ($is_proc) { + $jargs_proto .= ', long pid'; + $jargs .= ", pid"; + } + elsif ($has_name_arg{$name}) { + $jargs_proto .= ', String name'; + $jargs .= ", name"; + } + + my $cache_field_ids = 1; + + print JFH <classref = ", + " (jclass)JENV->NewGlobalRef(env, cls);", + " $field_class->ids = ", + " malloc($field_class_max *", + " sizeof(*$field_class->ids));"); + + my $perl_class = "Sigar::$class"; + if (0) { + #insert into Sigar.xs + (my $perl_typedef = $perl_class) =~ s/:/_/g; + print "typedef $sigar_type * $perl_typedef;\n"; + } + elsif (0) { + #insert into typemap + print "$perl_class T_PTROBJ\n"; + } + + print PFH "\nMODULE = Sigar PACKAGE = Sigar PREFIX = sigar_\n\n"; + + my $xs_args = 'sigar'; + if ($arg) { + $xs_args .= ", $arg"; + } + + print PFH <{type}; + my $name = $field->{name}; + my $desc = $field->{desc} || $name; + (my $jname = $name) =~ s/_(\w)/\u$1/g; + my $sig = qq("$jfields{$type}"); + my $set = "JENV->Set${type}Field"; + + print PFH <$name; + + OUTPUT: + RETVAL + +EOF + + my $field_ix = $field_class_ix . "_\U$name"; + my $get_id = qq|JENV->GetFieldID(env, cls, "$jname", $sig)|; + my $id_cache = "$field_class->ids[$field_ix]"; + + my $id_lookup = $cache_field_ids ? + $id_cache : $get_id; + + push @init_fields, + " $id_cache = ", + " $get_id;"; + + push @macro, + qq| $set(env, obj, $id_lookup, s.$name);|; + + my $init = $jinit{$type} || '0'; + my $jtype = $jtype{$type} || lcfirst($type); + my $platforms = supported_platforms($field->{plat}); + + print JFH " $jtype $jname = $init;\n\n"; + push @copy, " copy.$jname = this.$jname;\n"; + + #documentation + print JFH " /**\n"; + print JFH " * Get the $desc.

\n"; + print JFH " * Supported Platforms: $platforms.\n"; + print JFH " *

\n"; + if (my $cmd = ($field->{cmd} || $cmds{$class})) { + print JFH " * System equivalent commands:

    \n"; + for my $p (sort keys %$cmd) { + print JFH " *
  • $p: $cmd->{$p}
    \n"; + } + print JFH " *
\n"; + } + print JFH " * \@return $desc\n"; + print JFH " */\n"; + + print JFH " public $jtype get\u$jname() { return $jname; }\n"; + } + + print JFH "\n void copyTo($name copy) {\n", @copy, " }\n"; + + if (my $code = $extra_code{$name}) { + print JFH $code; + } + + push @init_fields, " }"; + + if ($cache_field_ids) { + print HFH join(' \\' . "\n", @init_fields), "\n\n"; + print CFH "\n\n $init_define(cls);\n\n" if $impl; + } + else { + print HFH "#define $init_define(cls)\n"; + } + + print HFH join(' \\' . "\n", @macro), "\n\n"; + print CFH "\n\n $define(cls, obj, s);" if $impl; + + print CFH "\n}\n" if $impl; + print JFH "\n}\n"; + + close JFH; +} + +close CFH; diff --git a/bindings/java/src/jni/javasigar.c b/bindings/java/src/jni/javasigar.c new file mode 100644 index 00000000..2eee33ed --- /dev/null +++ b/bindings/java/src/jni/javasigar.c @@ -0,0 +1,974 @@ +#include +#include "sigar.h" +#include "sigar_fileinfo.h" +#include "sigar_log.h" + +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif + +#include "javasigar_generated.h" + +#define JENV (*env) + +#define SIGAR_PACKAGE "net/hyperic/sigar/" + +#define SIGAR_JNI(m) JNICALL Java_net_hyperic_sigar_##m + +#define SIGAR_FIND_CLASS(name) \ + JENV->FindClass(env, SIGAR_PACKAGE name) + +typedef struct { + jclass classref; + jfieldID *ids; +} jsigar_field_cache_t; + +typedef struct { + JNIEnv *env; + jobject logger; + sigar_t *sigar; + jsigar_field_cache_t *fields[JSIGAR_FIELDS_MAX]; + int open_status; +} jni_sigar_t; + +#define dSIGAR_GET \ + jni_sigar_t *jsigar = sigar_get_pointer(env, sigar_obj); \ + sigar_t *sigar + +#define dSIGAR_VOID \ + dSIGAR_GET; \ + if (!jsigar) return; \ + sigar = jsigar->sigar + +#define dSIGAR(val) \ + dSIGAR_GET; \ + if (!jsigar) return val; \ + sigar = jsigar->sigar + +static void sigar_throw_exception(JNIEnv *env, char *msg) +{ + jclass errorClass = SIGAR_FIND_CLASS("SigarException"); + + JENV->ThrowNew(env, errorClass, msg); +} + +static void sigar_throw_notimpl(JNIEnv *env, char *msg) +{ + jclass errorClass = SIGAR_FIND_CLASS("SigarNotImplementedException"); + + JENV->ThrowNew(env, errorClass, msg); +} + +static void sigar_throw_error(JNIEnv *env, sigar_t *sigar, int err) +{ + jclass errorClass; + + switch (err) { +#ifndef WIN32 + case ENOENT: + errorClass = SIGAR_FIND_CLASS("SigarFileNotFoundException"); + break; +#else + /*XXX*/ +#endif + case SIGAR_ENOTIMPL: + errorClass = SIGAR_FIND_CLASS("SigarNotImplementedException"); + break; + default: + errorClass = SIGAR_FIND_CLASS("SigarException"); + break; + } + + JENV->ThrowNew(env, errorClass, + sigar_strerror(sigar, err)); +} + +static jni_sigar_t *sigar_get_pointer(JNIEnv *env, jobject obj) { + jfieldID pointer_field; + jni_sigar_t *jsigar; + jclass cls; + + cls = JENV->GetObjectClass(env, obj); + + pointer_field = JENV->GetFieldID(env, cls, "sigarWrapper", "I"); + jsigar = (jni_sigar_t *) JENV->GetIntField(env, obj, pointer_field); + + if (!jsigar) { + sigar_throw_exception(env, "sigar has been closed"); + return NULL; + } + + if (jsigar->open_status != SIGAR_OK) { + sigar_throw_error(env, jsigar->sigar, + jsigar->open_status); + return NULL; + } + + return jsigar; +} + +static void sigar_set_pointer(JNIEnv *env, jobject obj, const void *ptr) { + jfieldID pointer_field; + int pointer_int; + jclass cls; + + cls = JENV->GetObjectClass(env, obj); + + pointer_field = JENV->GetFieldID(env, cls, "sigarWrapper", "I"); + pointer_int = (int)ptr; + + JENV->SetIntField(env, obj, pointer_field, pointer_int); +} + +JNIEXPORT jstring SIGAR_JNI(Sigar_formatSize) +(JNIEnv *env, jclass cls, jlong size) +{ + char buf[56]; + sigar_format_size(size, buf); + return JENV->NewStringUTF(env, buf); +} + +JNIEXPORT void SIGAR_JNI(Sigar_open) +(JNIEnv *env, jobject obj) +{ + jni_sigar_t *jsigar = malloc(sizeof(*jsigar)); + + memset(jsigar, '\0', sizeof(*jsigar)); + + sigar_set_pointer(env, obj, jsigar); + + /* this method is called by the constructor. + * if != SIGAR_OK save status and throw exception + * when methods are invoked (see sigar_get_pointer). + */ + if ((jsigar->open_status = sigar_open(&jsigar->sigar)) != SIGAR_OK) { + sigar_throw_error(env, jsigar->sigar, jsigar->open_status); + return; + } +} + +JNIEXPORT jint SIGAR_JNI(Sigar_nativeClose) +(JNIEnv *env, jobject sigar_obj) +{ + jint status; + int i; + dSIGAR(0); + + /* only place it is possible this would be something other than + * SIGAR_OK is on win32 if RegCloseKey fails, which i don't think + * is possible either. + */ + status = sigar_close(sigar); + + if (jsigar->logger != NULL) { + JENV->DeleteGlobalRef(env, jsigar->logger); + } + + for (i=0; ifields[i]) { + JENV->DeleteGlobalRef(env, + jsigar->fields[i]->classref); + free(jsigar->fields[i]->ids); + free(jsigar->fields[i]); + } + } + + free(jsigar); + sigar_set_pointer(env, sigar_obj, NULL); + + return status; +} + +JNIEXPORT jlong SIGAR_JNI(Sigar_getPid) +(JNIEnv *env, jobject sigar_obj) +{ + dSIGAR(0); + + return sigar_pid_get(sigar); +} + +JNIEXPORT void SIGAR_JNI(Sigar_kill) +(JNIEnv *env, jobject sigar_obj, jlong pid, jint signum) +{ + int status; + dSIGAR_VOID; + + if ((status = sigar_proc_kill(pid, signum)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + } +} + +#define SetStringField(env, obj, fieldID, val) \ + SetObjectField(env, obj, fieldID, JENV->NewStringUTF(env, val)) + +static jstring jinet_ntoa(JNIEnv *env, sigar_t *sigar, sigar_uint64_t val) { + char addr_str[SIGAR_INET_ADDR_LEN]; + sigar_inet_ntoa(sigar, val, addr_str); + return JENV->NewStringUTF(env, addr_str); +} + +#define SetNetAddrField(env, obj, fieldID, val) \ + SetObjectField(env, obj, fieldID, jinet_ntoa(env, sigar, val)) + +#include "javasigar_generated.c" + +#define SIGAR_CLASS_SIG(name) \ + "L" SIGAR_PACKAGE name ";" + +#define SIGAR_ALLOC_OBJECT(name) \ + JENV->AllocObject(env, SIGAR_FIND_CLASS(name)) + +enum { + FS_FIELD_DIRNAME, + FS_FIELD_DEVNAME, + FS_FIELD_SYS_TYPENAME, + FS_FIELD_TYPE, + FS_FIELD_TYPENAME, + FS_FIELD_MAX +}; + +#define STRING_SIG "Ljava/lang/String;" + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getFileSystemList) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + unsigned int i; + sigar_file_system_list_t fslist; + jobjectArray fsarray; + jfieldID ids[FS_FIELD_MAX]; + jclass cls = SIGAR_FIND_CLASS("FileSystem"); + dSIGAR(NULL); + + if ((status = sigar_file_system_list_get(sigar, &fslist)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + ids[FS_FIELD_DIRNAME] = + JENV->GetFieldID(env, cls, "dirName", STRING_SIG); + + ids[FS_FIELD_DEVNAME] = + JENV->GetFieldID(env, cls, "devName", STRING_SIG); + + ids[FS_FIELD_TYPENAME] = + JENV->GetFieldID(env, cls, "typeName", STRING_SIG); + + ids[FS_FIELD_SYS_TYPENAME] = + JENV->GetFieldID(env, cls, "sysTypeName", STRING_SIG); + + ids[FS_FIELD_TYPE] = + JENV->GetFieldID(env, cls, "type", "I"); + + fsarray = JENV->NewObjectArray(env, fslist.number, cls, 0); + + for (i=0; iAllocObject(env, cls); + + JENV->SetStringField(env, fsobj, + ids[FS_FIELD_DIRNAME], + fs->dir_name); + + JENV->SetStringField(env, fsobj, + ids[FS_FIELD_DEVNAME], + fs->dev_name); + + JENV->SetStringField(env, fsobj, + ids[FS_FIELD_SYS_TYPENAME], + fs->sys_type_name); + + JENV->SetStringField(env, fsobj, + ids[FS_FIELD_TYPENAME], + fs->type_name); + + JENV->SetIntField(env, fsobj, + ids[FS_FIELD_TYPE], + fs->type); + + JENV->SetObjectArrayElement(env, fsarray, i, fsobj); + } + + sigar_file_system_list_destroy(sigar, &fslist); + + return fsarray; +} + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getCpuInfoList) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + unsigned int i; + sigar_cpu_infos_t cpu_infos; + jobjectArray cpuarray; + jclass cls = SIGAR_FIND_CLASS("CpuInfo"); + dSIGAR(NULL); + + if ((status = sigar_cpu_infos_get(sigar, &cpu_infos)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + JAVA_SIGAR_INIT_FIELDS_CPUINFO(cls); + + cpuarray = JENV->NewObjectArray(env, cpu_infos.number, cls, 0); + + for (i=0; iAllocObject(env, cls); + JAVA_SIGAR_SET_FIELDS_CPUINFO(cls, info_obj, + cpu_infos.data[i]); + JENV->SetObjectArrayElement(env, cpuarray, i, info_obj); + } + + sigar_cpu_infos_destroy(sigar, &cpu_infos); + + return cpuarray; +} + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getCpuListNative) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + unsigned int i; + sigar_cpu_list_t cpulist; + jobjectArray cpuarray; + jclass cls = SIGAR_FIND_CLASS("Cpu"); + dSIGAR(NULL); + + if ((status = sigar_cpu_list_get(sigar, &cpulist)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + JAVA_SIGAR_INIT_FIELDS_CPU(cls); + + cpuarray = JENV->NewObjectArray(env, cpulist.number, cls, 0); + + for (i=0; iAllocObject(env, cls); + JAVA_SIGAR_SET_FIELDS_CPU(cls, info_obj, + cpulist.data[i]); + JENV->SetObjectArrayElement(env, cpuarray, i, info_obj); + } + + sigar_cpu_list_destroy(sigar, &cpulist); + + return cpuarray; +} + +JNIEXPORT jlongArray SIGAR_JNI(Sigar_getProcList) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + jlongArray procarray; + sigar_proc_list_t proclist; + jlong *pids = NULL; + dSIGAR(NULL); + + if ((status = sigar_proc_list_get(sigar, &proclist)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + procarray = JENV->NewLongArray(env, proclist.number); + + if (sizeof(jlong) == sizeof(sigar_pid_t)) { + pids = (jlong *)proclist.data; + } + else { + unsigned int i; + pids = (jlong *)malloc(sizeof(jlong) * proclist.number); + + for (i=0; iSetLongArrayRegion(env, procarray, 0, + proclist.number, pids); + + if (pids != (jlong *)proclist.data) { + free(pids); + } + + sigar_proc_list_destroy(sigar, &proclist); + + return procarray; +} + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getProcArgs) +(JNIEnv *env, jobject sigar_obj, jlong pid) +{ + int status; + unsigned int i; + sigar_proc_args_t procargs; + jobjectArray argsarray; + jclass stringclass = JENV->FindClass(env, "java/lang/String"); + dSIGAR(NULL); + + if ((status = sigar_proc_args_get(sigar, pid, &procargs)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + argsarray = JENV->NewObjectArray(env, procargs.number, stringclass, 0); + + for (i=0; iNewStringUTF(env, procargs.data[i]); + JENV->SetObjectArrayElement(env, argsarray, i, s); + } + + sigar_proc_args_destroy(sigar, &procargs); + + return argsarray; +} + +typedef struct { + JNIEnv *env; + jobject map; + jmethodID id; +} jni_env_put_t; + +static int jni_env_getall(void *data, + const char *key, int klen, + char *val, int vlen) +{ + jni_env_put_t *put = (jni_env_put_t *)data; + JNIEnv *env = put->env; + + JENV->CallObjectMethod(env, put->map, put->id, + JENV->NewStringUTF(env, key), + JENV->NewStringUTF(env, val)); + + return SIGAR_OK; +} + +JNIEXPORT jobject SIGAR_JNI(ProcEnv_getAll) +(JNIEnv *env, jobject cls, jobject sigar_obj, jlong pid) +{ + int status; + sigar_proc_env_t procenv; + jobject hashmap; + jni_env_put_t put; + jclass mapclass = + JENV->FindClass(env, "java/util/HashMap"); + jmethodID mapid = + JENV->GetMethodID(env, mapclass, "", "()V"); + jmethodID putid = + JENV->GetMethodID(env, mapclass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)" + "Ljava/lang/Object;"); + dSIGAR(NULL); + + hashmap = JENV->NewObject(env, mapclass, mapid); + + put.env = env; + put.id = putid; + put.map = hashmap; + + procenv.type = SIGAR_PROC_ENV_ALL; + procenv.env_getter = jni_env_getall; + procenv.data = &put; + + if ((status = sigar_proc_env_get(sigar, pid, &procenv)) != SIGAR_OK) { + JENV->DeleteLocalRef(env, hashmap); + sigar_throw_error(env, sigar, status); + return NULL; + } + + return hashmap; +} + +typedef struct { + JNIEnv *env; + const char *key; + int klen; + jstring val; +} jni_env_get_t; + +static int jni_env_getvalue(void *data, + const char *key, int klen, + char *val, int vlen) +{ + jni_env_get_t *get = (jni_env_get_t *)data; + JNIEnv *env = get->env; + + if ((get->klen == klen) && + (strcmp(get->key, key) == 0)) + { + get->val = JENV->NewStringUTF(env, val); + return !SIGAR_OK; /* foundit; stop iterating */ + } + + return SIGAR_OK; +} + +JNIEXPORT jstring SIGAR_JNI(ProcEnv_getValue) +(JNIEnv *env, jobject cls, jobject sigar_obj, jlong pid, jstring key) +{ + int status; + sigar_proc_env_t procenv; + jni_env_get_t get; + dSIGAR(NULL); + + get.env = env; + get.key = JENV->GetStringUTFChars(env, key, 0); + get.klen = JENV->GetStringUTFLength(env, key); + get.val = NULL; + + procenv.type = SIGAR_PROC_ENV_KEY; + procenv.key = get.key; + procenv.klen = get.klen; + procenv.env_getter = jni_env_getvalue; + procenv.data = &get; + + if ((status = sigar_proc_env_get(sigar, pid, &procenv)) != SIGAR_OK) { + JENV->ReleaseStringUTFChars(env, key, get.key); + sigar_throw_error(env, sigar, status); + return NULL; + } + + JENV->ReleaseStringUTFChars(env, key, get.key); + + return get.val; +} + +JNIEXPORT jdoubleArray SIGAR_JNI(Sigar_getLoadAverage) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + jlongArray avgarray; + sigar_loadavg_t loadavg; + dSIGAR(NULL); + + if ((status = sigar_loadavg_get(sigar, &loadavg)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + avgarray = JENV->NewDoubleArray(env, 3); + + JENV->SetDoubleArrayRegion(env, avgarray, 0, + 3, loadavg.loadavg); + + return avgarray; +} + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getNetRouteList) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + unsigned int i; + jarray routearray; + jclass cls = SIGAR_FIND_CLASS("NetRoute"); + sigar_net_route_list_t routelist; + dSIGAR(NULL); + + if ((status = sigar_net_route_list_get(sigar, &routelist)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + JAVA_SIGAR_INIT_FIELDS_NETROUTE(cls); + + routearray = JENV->NewObjectArray(env, routelist.number, cls, 0); + + for (i=0; iAllocObject(env, cls); + JAVA_SIGAR_SET_FIELDS_NETROUTE(cls, obj, routelist.data[i]); + JENV->SetObjectArrayElement(env, routearray, i, obj); + } + + sigar_net_route_list_destroy(sigar, &routelist); + + return routearray; +} + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getNetConnectionList) +(JNIEnv *env, jobject sigar_obj, jint flags) +{ + int status; + unsigned int i; + jarray connarray; + jclass cls = SIGAR_FIND_CLASS("NetConnection"); + sigar_net_connection_list_t connlist; + dSIGAR(NULL); + + status = sigar_net_connection_list_get(sigar, &connlist, flags); + + if (status != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + JAVA_SIGAR_INIT_FIELDS_NETCONNECTION(cls); + + connarray = JENV->NewObjectArray(env, connlist.number, cls, 0); + + for (i=0; iAllocObject(env, cls); + JAVA_SIGAR_SET_FIELDS_NETCONNECTION(cls, obj, connlist.data[i]); + JENV->SetObjectArrayElement(env, connarray, i, obj); + } + + sigar_net_connection_list_destroy(sigar, &connlist); + + return connarray; +} + +JNIEXPORT jstring SIGAR_JNI(NetConnection_getTypeString) +(JNIEnv *env, jobject obj) +{ + jclass cls = JENV->GetObjectClass(env, obj); + jfieldID field = JENV->GetFieldID(env, cls, "type", "I"); + jint type = JENV->GetIntField(env, obj, field); + return JENV->NewStringUTF(env, + sigar_net_connection_type_get(type)); +} + +/* XXX perhaps it would be better to duplicate these strings + * in java land as static final so we dont create a new String + * everytime. + */ +JNIEXPORT jstring SIGAR_JNI(FileInfo_getTypeString) +(JNIEnv *env, jclass cls, jint type) +{ + return JENV->NewStringUTF(env, + sigar_file_attrs_type_string_get(type)); +} + +JNIEXPORT jstring SIGAR_JNI(FileInfo_getPermissionsString) +(JNIEnv *env, jclass cls, jlong perms) +{ + char str[24]; + return JENV->NewStringUTF(env, + sigar_file_attrs_permissions_string_get(perms, + str)); +} + +JNIEXPORT jint SIGAR_JNI(FileInfo_getMode) +(JNIEnv *env, jclass cls, jlong perms) +{ + return sigar_file_attrs_mode_get(perms); +} + + +/* + * copy of the generated FileAttrs_nativeGet function + * but we call the lstat wrapper instead. + */ +JNIEXPORT void SIGAR_JNI(FileInfo_nativeGetLink) +(JNIEnv *env, jobject obj, jobject sigar_obj, jstring name) +{ + sigar_file_attrs_t s; + int status; + jclass cls = JENV->GetObjectClass(env, obj); + const char *utf; + dSIGAR_VOID; + + utf = JENV->GetStringUTFChars(env, name, 0); + + status = sigar_link_attrs_get(sigar, utf, &s); + + JENV->ReleaseStringUTFChars(env, name, utf); + + if (status != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return; + } + + JAVA_SIGAR_INIT_FIELDS_FILEATTRS(cls); + + JAVA_SIGAR_SET_FIELDS_FILEATTRS(cls, obj, s); +} + +JNIEXPORT jlong SIGAR_JNI(Sigar_getProcPort) +(JNIEnv *env, jobject sigar_obj, jlong port) +{ + int status; + sigar_pid_t pid; + dSIGAR(0); + +#if defined(__linux__) || defined(WIN32) + /* just thinking about implementing this on other platforms hurts */ + + if ((status = sigar_proc_port_get(sigar, (unsigned long)port, &pid)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + } +#else + status = SIGAR_ENOTIMPL; + pid = -1; + sigar_throw_error(env, sigar, status); +#endif + + return pid; +} + +JNIEXPORT jobjectArray SIGAR_JNI(Sigar_getNetInterfaceList) +(JNIEnv *env, jobject sigar_obj) +{ + int status; + unsigned int i; + sigar_net_interface_list_t iflist; + jobjectArray ifarray; + jclass stringclass = JENV->FindClass(env, "java/lang/String"); + dSIGAR(NULL); + + if ((status = sigar_net_interface_list_get(sigar, &iflist)) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + ifarray = JENV->NewObjectArray(env, iflist.number, stringclass, 0); + + for (i=0; iNewStringUTF(env, iflist.data[i]); + JENV->SetObjectArrayElement(env, ifarray, i, s); + } + + sigar_net_interface_list_destroy(sigar, &iflist); + + return ifarray; +} + +JNIEXPORT jstring SIGAR_JNI(Sigar_getPasswordNative) +(JNIEnv *env, jclass classinstance, jstring prompt) +{ + const char *prompt_str; + char *password; + + if (getenv("NO_NATIVE_GETPASS")) { + sigar_throw_notimpl(env, "disabled with $NO_NATIVE_GETPASS"); + return NULL; + } + + prompt_str = JENV->GetStringUTFChars(env, prompt, 0); + + password = sigar_password_get(prompt_str); + + JENV->ReleaseStringUTFChars(env, prompt, prompt_str); + + return JENV->NewStringUTF(env, password); +} + +JNIEXPORT jstring SIGAR_JNI(Sigar_getFQDN) +(JNIEnv *env, jobject sigar_obj) +{ + char fqdn[SIGAR_FQDN_LEN]; + int status; + dSIGAR(NULL); + + if ((status = sigar_fqdn_get(sigar, fqdn, sizeof(fqdn))) != SIGAR_OK) { + sigar_throw_error(env, sigar, status); + return NULL; + } + + return JENV->NewStringUTF(env, fqdn); +} + +#include "sigar_getline.h" + +JNIEXPORT jstring SIGAR_JNI(util_Getline_getline) +(JNIEnv *env, jobject sigar_obj, jstring prompt) +{ + const char *prompt_str; + char *line; + jboolean is_copy; + + prompt_str = JENV->GetStringUTFChars(env, prompt, &is_copy); + + line = sigar_getline((char *)prompt_str); + + if (is_copy) { + JENV->ReleaseStringUTFChars(env, prompt, prompt_str); + } + + if ((line == NULL) || + sigar_getline_eof()) + { + jclass eof_ex = JENV->FindClass(env, "java/io/EOFException"); + JENV->ThrowNew(env, eof_ex, ""); + return NULL; + } + + return JENV->NewStringUTF(env, line); +} + +JNIEXPORT void SIGAR_JNI(util_Getline_histadd) +(JNIEnv *env, jobject sigar_obj, jstring hist) +{ + const char *hist_str; + jboolean is_copy; + + hist_str = JENV->GetStringUTFChars(env, hist, &is_copy); + + sigar_getline_histadd((char *)hist_str); + + if (is_copy) { + JENV->ReleaseStringUTFChars(env, hist, hist_str); + } +} + +JNIEXPORT void SIGAR_JNI(util_Getline_histinit) +(JNIEnv *env, jobject sigar_obj, jstring hist) +{ + const char *hist_str; + jboolean is_copy; + + hist_str = JENV->GetStringUTFChars(env, hist, &is_copy); + + sigar_getline_histinit((char *)hist_str); + + if (is_copy) { + JENV->ReleaseStringUTFChars(env, hist, hist_str); + } +} + +static struct { + JNIEnv *env; + jobject obj; + jmethodID id; + jclass clazz; +} jsigar_completer; + +static int jsigar_getline_completer(char *buffer, int offset, int *pos) +{ + JNIEnv *env = jsigar_completer.env; + jstring jbuffer; + jstring completion; + const char *line; + int len, cur; + jboolean is_copy; + + jbuffer = JENV->NewStringUTF(env, buffer); + + completion = + JENV->CallObjectMethod(env, jsigar_completer.obj, + jsigar_completer.id, jbuffer); + + if (JENV->ExceptionOccurred(env)) { + JENV->ExceptionDescribe(env); + return 0; + } + + if (!completion) { + return 0; + } + + line = JENV->GetStringUTFChars(env, completion, &is_copy); + len = JENV->GetStringUTFLength(env, completion); + + cur = *pos; + + if (len != cur) { + strcpy(buffer, line); + *pos = len; + } + + if (is_copy) { + JENV->ReleaseStringUTFChars(env, completion, line); + } + + return cur; +} + +JNIEXPORT void SIGAR_JNI(util_Getline_setCompleter) +(JNIEnv *env, jclass classinstance, jobject completer) +{ + if (completer == NULL) { + sigar_getline_completer_set(NULL); + return; + } + + jsigar_completer.env = env; + jsigar_completer.obj = completer; + jsigar_completer.clazz = JENV->GetObjectClass(env, completer); + jsigar_completer.id = + JENV->GetMethodID(env, jsigar_completer.clazz, + "complete", + "(Ljava/lang/String;)Ljava/lang/String;"); + + sigar_getline_completer_set(jsigar_getline_completer); +} + +JNIEXPORT void SIGAR_JNI(util_Getline_redraw) +(JNIEnv *env, jobject obj) +{ + sigar_getline_redraw(); +} + +JNIEXPORT void SIGAR_JNI(util_Getline_reset) +(JNIEnv *env, jobject obj) +{ + sigar_getline_reset(); +} + +static const char *log_methods[] = { + "fatal", /* SIGAR_LOG_FATAL */ + "error", /* SIGAR_LOG_ERROR */ + "warn", /* SIGAR_LOG_WARN */ + "info", /* SIGAR_LOG_INFO */ + "debug", /* SIGAR_LOG_DEBUG */ + /* XXX trace is only in commons-logging??? */ + "debug", /* SIGAR_LOG_TRACE */ +}; + +static void jsigar_log_impl(sigar_t *sigar, void *data, + int level, char *message) +{ + jni_sigar_t *jsigar = (jni_sigar_t *)data; + JNIEnv *env = jsigar->env; + jobject logger = jsigar->logger; + jobject message_obj; + + /* XXX should cache method id lookups */ + jmethodID id = + JENV->GetMethodID(env, JENV->GetObjectClass(env, logger), + log_methods[level], + "(Ljava/lang/Object;)V"); + + if (JENV->ExceptionOccurred(env)) { + JENV->ExceptionDescribe(env); + return; + } + + message_obj = (jobject)JENV->NewStringUTF(env, message); + + JENV->CallObjectMethod(env, logger, id, message_obj); +} + +JNIEXPORT void SIGAR_JNI(SigarLog_setLogger) +(JNIEnv *env, jclass classinstance, jobject sigar_obj, jobject logger) +{ + dSIGAR_VOID; + + jsigar->env = env; + + if (jsigar->logger != NULL) { + JENV->DeleteGlobalRef(env, jsigar->logger); + jsigar->logger = NULL; + } + + if (logger) { + jsigar->logger = JENV->NewGlobalRef(env, logger); + + sigar_log_impl_set(sigar, jsigar, jsigar_log_impl); + } + else { + sigar_log_impl_set(sigar, NULL, NULL); + } +} + +JNIEXPORT void SIGAR_JNI(SigarLog_setLevel) +(JNIEnv *env, jclass classinstance, jobject sigar_obj, jint level) +{ + dSIGAR_VOID; + + sigar_log_level_set(sigar, level); +} diff --git a/bindings/java/src/net/hyperic/sigar/CpuPerc.java b/bindings/java/src/net/hyperic/sigar/CpuPerc.java new file mode 100644 index 00000000..1af417b7 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/CpuPerc.java @@ -0,0 +1,89 @@ +package net.hyperic.sigar; + +/** + * CPU percentage usage + */ +public class CpuPerc { + private double user; + private double sys; + private double nice; + private double idle; + + CpuPerc(){ } + + static CpuPerc calculate(Cpu oldCpu, Cpu curCpu) { + double diffUser, diffSys, diffNice, diffIdle, diffTotal; + + diffUser = curCpu.getUser() - oldCpu.getUser(); + diffSys = curCpu.getSys() - oldCpu.getSys(); + diffNice = curCpu.getNice() - oldCpu.getNice(); + diffIdle = curCpu.getIdle() - oldCpu.getIdle(); + + // Sanity check -- sometimes these values waiver in between + // whole numbers when Cpu is checked very rapidly + diffUser = diffUser < 0 ? 0 : diffUser; + diffSys = diffSys < 0 ? 0 : diffSys; + diffNice = diffNice < 0 ? 0 : diffNice; + diffIdle = diffIdle < 0 ? 0 : diffIdle; + + diffTotal = diffUser + diffSys + diffNice + diffIdle; + + CpuPerc perc = new CpuPerc(); + perc.setUser(diffUser / diffTotal); + perc.setSys(diffSys / diffTotal); + perc.setNice(diffNice / diffTotal); + perc.setIdle(diffIdle / diffTotal); + return perc; + } + + public double getUser(){ + return this.user; + } + + void setUser(double user){ + this.user = user; + } + + public double getSys(){ + return this.sys; + } + + void setSys(double sys){ + this.sys = sys; + } + + public double getNice(){ + return this.nice; + } + + void setNice(double nice){ + this.nice = nice; + } + + public double getIdle(){ + return this.idle; + } + + void setIdle(double idle){ + this.idle = idle; + } + + public static String format(double val) { + String p = String.valueOf(val * 100.0); + //cant wait for sprintf. + int ix = p.indexOf(".") + 1; + String percent = + p.substring(0, ix) + + p.substring(ix, ix+1); + return percent + "%"; + } + + public String toString() { + return + "CPU states: " + + format(this.user) + " user, " + + format(this.sys) + " system, " + + format(this.nice) + " nice, " + + format(this.idle) + " idle"; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/CurrentProcessSummary.java b/bindings/java/src/net/hyperic/sigar/CurrentProcessSummary.java new file mode 100644 index 00000000..deebd448 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/CurrentProcessSummary.java @@ -0,0 +1,73 @@ +package net.hyperic.sigar; + +public class CurrentProcessSummary { + private int total=0, sleeping=0, running=0, zombie=0, stopped=0; + + private CurrentProcessSummary() { } + + public static CurrentProcessSummary get(SigarProxy sigar) + throws SigarException { + + long[] pids = sigar.getProcList(); + CurrentProcessSummary summary = + new CurrentProcessSummary(); + + for (int i=0; iMODE_UREAD|MODE_UWRITE|MODE_GREAD|MODE_WREAD + * converts to 644. + * @return The file permissions mode. + */ + public int getMode() { + return FileInfo.getMode(this.permissions); + } + + public void enableDirStat(boolean value) { + this.dirStatEnabled = value; + if (value) { + if (this.type != TYPE_DIR) { + throw new IllegalArgumentException(this.name + " is not a directory"); + } + + try { + if (this.stat == null) { + this.stat = this.sigar.getDirStat(this.name); + } + else { + this.stat.nativeGet(this.sigar, this.name); + } + } catch (SigarException e) { + //ok for now + } + } + } + + public String diff() { + if (this.oldInfo == null) { + return ""; + } + return diff(this.oldInfo); + } + + public String diff(DirStat stat) { + DirStat thisStat = this.stat; + StringBuffer sb = new StringBuffer(); + + if (thisStat.files != stat.files) { + sb.append("Files........"). + append(stat.files). + append(DIFF_SEP). + append(thisStat.files). + append("\n"); + } + + if (thisStat.subdirs != stat.subdirs) { + sb.append("Subdirs......"). + append(stat.subdirs). + append(DIFF_SEP). + append(thisStat.subdirs). + append("\n"); + } + + if (thisStat.symlinks != stat.symlinks) { + sb.append("Symlinks....."). + append(stat.symlinks). + append(DIFF_SEP). + append(thisStat.symlinks). + append("\n"); + } + + if (thisStat.chrdevs != stat.chrdevs) { + sb.append("Chrdevs......"). + append(stat.chrdevs). + append(DIFF_SEP). + append(thisStat.chrdevs). + append("\n"); + } + + if (thisStat.blkdevs != stat.blkdevs) { + sb.append("Blkdevs......"). + append(stat.blkdevs). + append(DIFF_SEP). + append(thisStat.blkdevs). + append("\n"); + } + + if (thisStat.sockets != stat.sockets) { + sb.append("Sockets......"). + append(stat.sockets). + append(DIFF_SEP). + append(thisStat.sockets). + append("\n"); + } + + if (thisStat.total != stat.total) { + sb.append("Total........"). + append(stat.total). + append(DIFF_SEP). + append(thisStat.total). + append("\n"); + } + + return sb.toString(); + } + + public String diff(FileInfo info) { + StringBuffer sb = new StringBuffer(); + boolean changed = false; + + if (this.ctime != info.ctime) { + sb.append("Ctime........"). + append(new Date(info.ctime)). + append(DIFF_SEP). + append(new Date(info.ctime)). + append("\n"); + changed = true; + } + + if (this.mtime != info.mtime) { + sb.append("Mtime........"). + append(new Date(info.mtime)). + append(DIFF_SEP). + append(new Date(this.mtime)). + append("\n"); + } + else if (!changed) { + //no point in checking the rest if all times are the same. + //or should we include atime in the diff? + return ""; + } + + if (this.atime != info.atime) { + sb.append("Atime........"). + append(new Date(info.atime)). + append(DIFF_SEP). + append(new Date(this.atime)). + append("\n"); + } + + if (this.permissions != info.permissions) { + sb.append("Permissions.."). + append(getPermissionsString(info.permissions)). + append(DIFF_SEP). + append(getPermissionsString(this.permissions)). + append("\n"); + } + + if (this.type != info.type) { + sb.append("Type........."). + append(getTypeString(info.type)). + append(DIFF_SEP). + append(getTypeString(this.type)). + append("\n"); + } + + if (this.uid != info.uid) { + sb.append("Uid.........."). + append(info.uid). + append(DIFF_SEP). + append(this.uid). + append("\n"); + } + + if (this.gid != info.gid) { + sb.append("Gid.........."). + append(info.gid). + append(DIFF_SEP). + append(this.gid). + append("\n"); + } + + if (this.inode != info.inode) { + sb.append("Inode........"). + append(info.inode). + append(DIFF_SEP). + append(this.inode). + append("\n"); + } + + if (this.device != info.device) { + sb.append("Device......."). + append(info.device). + append(DIFF_SEP). + append(this.device). + append("\n"); + } + + if (this.nlink != info.nlink) { + sb.append("Nlink........"). + append(info.nlink). + append(DIFF_SEP). + append(this.nlink). + append("\n"); + } + + if (this.size != info.size) { + sb.append("Size........."). + append(info.size). + append(DIFF_SEP). + append(this.size). + append("\n"); + } + + if (this.dirStatEnabled) { + sb.append(diff(info.stat)); + } + + return sb.toString(); + } + + public FileInfo getPreviousInfo() { + return this.oldInfo; + } + + public boolean modified() + throws SigarException, + SigarFileNotFoundException { + + if (this.oldInfo == null) { + this.oldInfo = new FileInfo(); + if (this.dirStatEnabled) { + this.oldInfo.stat = new DirStat(); + } + } + copyTo(this.oldInfo); + if (this.dirStatEnabled) { + this.stat.copyTo(this.oldInfo.stat); + } + + stat(); + + return this.mtime != oldInfo.mtime; + } + + public boolean changed() + throws SigarException, + SigarFileNotFoundException { + + return modified() || (this.ctime != oldInfo.ctime); + } + + public void stat() + throws SigarException, + SigarFileNotFoundException { + + long mtime = this.mtime; + + if (this.lstat) { + this.nativeGetLink(this.sigar, this.name); + } + else { + this.nativeGet(this.sigar, this.name); + } + + if (this.dirStatEnabled && + (mtime != this.mtime)) //no need to fetch stat if unmodified. + { + this.stat.nativeGet(this.sigar, this.name); + } + } + + private static FileInfo fetchInfo(Sigar sigar, String name, + boolean followSymlinks) + throws SigarException { + + FileInfo info = new FileInfo(); + + if (followSymlinks) { + info.nativeGet(sigar, name); + info.lstat = false; + } + else { + info.nativeGetLink(sigar, name); + info.lstat = true; + } + + info.sigar = sigar; + info.name = name; + return info; + } + + static FileInfo fetchFileInfo(Sigar sigar, String name) + throws SigarException { + + return fetchInfo(sigar, name, true); + } + + static FileInfo fetchLinkInfo(Sigar sigar, String name) + throws SigarException { + + return fetchInfo(sigar, name, false); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/FileSystemMap.java b/bindings/java/src/net/hyperic/sigar/FileSystemMap.java new file mode 100644 index 00000000..82fbd22b --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/FileSystemMap.java @@ -0,0 +1,76 @@ +package net.hyperic.sigar; + +import java.util.Map; +import java.util.HashMap; + +import java.io.IOException; +import java.io.File; + +/** + * Helper class to build a map of mounted file systems. + */ +public class FileSystemMap extends HashMap { + + /** + * FileSystemMap is read-only, this method is unsupported. + * @see #init + */ + public Object put(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + /** + * Populate the map. FileSystem.getDirName is used as the map key. + */ + public void init(FileSystem[] fslist) { + super.clear(); + + for (int i=0; i 0) + retval += "UP "; + if ((flags & IFF_BROADCAST) > 0) + retval += "BROADCAST "; + if ((flags & IFF_DEBUG) > 0) + retval += "DEBUG "; + if ((flags & IFF_LOOPBACK) > 0) + retval += "LOOPBACK "; + if ((flags & IFF_POINTOPOINT) > 0) + retval += "POINTOPOINT "; + if ((flags & IFF_NOTRAILERS) > 0) + retval += "NOTRAILERS "; + if ((flags & IFF_RUNNING) > 0) + retval += "RUNNING "; + if ((flags & IFF_NOARP) > 0) + retval += "NOARP "; + if ((flags & IFF_PROMISC) > 0) + retval += "PROMISC "; + if ((flags & IFF_ALLMULTI) > 0) + retval += "ALLMULTI "; + if ((flags & IFF_MULTICAST) > 0) + retval += "MULTICAST "; + + return retval; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ProcCpu.java b/bindings/java/src/net/hyperic/sigar/ProcCpu.java new file mode 100644 index 00000000..4e6a42e4 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ProcCpu.java @@ -0,0 +1,92 @@ +package net.hyperic.sigar; + +import java.util.HashMap; +import java.util.Map; + +/** + * Extend ProcTime to provide process cpu percentage metric. + */ +public class ProcCpu extends ProcTime { + + private static ProcCpu key = new ProcCpu(); + private static Map ptable = new HashMap(); + + private long lastTime = 0; + private long pid; + private long time = 0; + private double percent = 0.0; + + private void getValues(Sigar sigar, long pid) + throws SigarException { + this.nativeGet(sigar, pid); + this.time = this.utime + this.stime; + } + + static synchronized ProcCpu get(Sigar sigar, long pid) + throws SigarException { + + ProcCpu cpu; + + key.pid = pid; + cpu = (ProcCpu)ptable.get(key); + + if (cpu == null) { + cpu = new ProcCpu(); + cpu.pid = pid; + ptable.put(cpu, cpu); + } + + long timeNow = System.currentTimeMillis() / 1000; //seconds + double diff = timeNow - cpu.lastTime; + if (diff == 0) { + return cpu; //we were just called within < 1 second ago. + } + + cpu.lastTime = timeNow; + + long otime = cpu.time; + + cpu.getValues(sigar, pid); + + if (otime == 0) { + //XXX could/should pause first time called. + return cpu; + } + + cpu.percent = ((cpu.time - otime) / diff); + if (cpu.percent >= 1.0) { + cpu.percent = 0.99; + } + + return cpu; + } + + /** + * @return Process CPU usage percentage. + */ + public double getPercent() { + return this.percent; + } + + /** + * @return Sum of Utime and Stime. + */ + public long getTotal() { + return this.time; + } + + /** + * @return Pid of the process. + */ + public int hashCode() { + return (int)this.pid; + } + + public boolean equals(Object cpu) { + if (!(cpu instanceof ProcCpu)) { + return false; + } + + return ((ProcCpu)cpu).pid == this.pid; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ProcEnv.java b/bindings/java/src/net/hyperic/sigar/ProcEnv.java new file mode 100644 index 00000000..11cef8cb --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ProcEnv.java @@ -0,0 +1,32 @@ +package net.hyperic.sigar; + +import java.util.Map; + +/** + * Lookup environment for a process. + */ +class ProcEnv { + + private ProcEnv () { } + + /** + * @param sigar The Sigar object. + * @param pid Process id. + * @return Map of environment. + * @exception SigarException on failure. + * @see net.hyperic.sigar.Sigar#getProcEnv + */ + public static native Map getAll(Sigar sigar, long pid) + throws SigarException; + + /** + * @param sigar The Sigar object. + * @param pid Process id. + * @param key Environment variable name. + * @return Environment variable value. + * @exception SigarException on failure. + * @see net.hyperic.sigar.Sigar#getProcEnv + */ + public static native String getValue(Sigar sigar, long pid, String key) + throws SigarException; +} diff --git a/bindings/java/src/net/hyperic/sigar/Sigar.java b/bindings/java/src/net/hyperic/sigar/Sigar.java new file mode 100644 index 00000000..46c907f4 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/Sigar.java @@ -0,0 +1,565 @@ +package net.hyperic.sigar; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import java.util.Map; + +import net.hyperic.jni.ArchLoaderException; +import net.hyperic.jni.ArchNotSupportedException; + +/** + * Entry point for the Sigar - System Information GAtheRer + */ +public class Sigar implements SigarProxy { + + public static final String VERSION = "1.1.0"; + + private static SigarLoader loader = new SigarLoader(Sigar.class); + private FileSystemMap mounts = null; + + int sigarWrapper = 0; //holds the sigar_t * + + // lastCpu is used to calculate the cpuPerc; + private Cpu lastCpu; + private Cpu[] lastCpuList; + private static SigarProxy instance = null; + + static { + try { + load(); + } catch (SigarException e) { + //will find out later when invoking methods. + } + } + + private static void load() throws SigarException { + 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()); + } + } + + /** + * 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); + + /** + * Constructor + */ + public Sigar() { + try { + open(); + } catch (SigarException e) { + //XXX log? + } catch (UnsatisfiedLinkError e) { + //XXX log? + } + } + + /** + * Convenience method to keep a sigar instance alive. + * This instance is not thread safe. + */ + //XXX we could make it thread safe by returning a SigarProxy + //impl which synchronizes all method calls. + public static synchronized SigarProxy getInstance() { + if (instance == null) { + instance = new Sigar(); + } + return instance; + } + + 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. + */ + public void close() { + if (this.sigarWrapper != 0) { + nativeClose(); + } + } + + private native int nativeClose(); + + /** + * Get pid of the current process. + * @exception SigarException on failure. + */ + public native long getPid(); + + /** + * Send 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; + + /** + * 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 (this.lastCpu = Cpu.fetch(this)); + } + + private static void pause(int millis) { + try { + Thread.sleep(millis); + } catch(InterruptedException e) { } + } + + private 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, curCpu; + + if (this.lastCpu == null){ + oldCpu = this.getCpu(); + pause(); + } + else { + oldCpu = this.lastCpu; + } + + curCpu = this.getCpu(); + return CpuPerc.calculate(oldCpu, curCpu); + } + + /** + * Get system per-CPU info in percentage format. (i.e. fraction of 1) + * @exception SigarException on failure. + */ + public CpuPerc[] getCpuPercList() throws SigarException { + Cpu[] oldCpuList, curCpuList; + + if (this.lastCpuList == null){ + oldCpuList = getCpuList(); + pause(); + } + else { + oldCpuList = this.lastCpuList; + } + + curCpuList = getCpuList(); + + int curLen = curCpuList.length, oldLen = oldCpuList.length; + + CpuPerc[] perc = new CpuPerc[curLen < oldLen ? curLen : oldLen]; + + for (int i=0; i cacheVal.expire) { + if (this.debugEnabled) { + debug("expiring " + method.getName() + + " from cache" + argDebug); + } + + cacheVal.value = null; + } + } + + if (cacheVal.value == null) { + try { + retval = method.invoke(this.sigar, args); + } catch (InvocationTargetException e) { + Throwable t = + ((InvocationTargetException)e). + getTargetException(); + + String msg; + + if (t instanceof SigarException) { + msg = ""; + } + else { + msg = t.getClass().getName() + ": "; + } + + msg += t.getMessage(); + + if (argKey != null) { + msg += ": " + getDebugArgs(args, argKey); + } + + if (t instanceof SigarNotImplementedException) { + throw new SigarNotImplementedException(msg); + } + throw new SigarException(msg); + } catch (Exception e) { + String msg = + e.getClass().getName() + ": " + + e.getMessage(); + + if (argKey != null) { + msg += ": " + getDebugArgs(args, argKey); + } + + throw new SigarException(msg); + } + + cacheVal.value = retval; + cacheVal.timestamp = timeNow; + + if (args == null) { + this.cache.put(method, cacheVal); + } + else { + argMap.put(argKey, cacheVal); + this.cache.put(method, argMap); + } + } + else { + retval = cacheVal.value; + } + + return retval; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/SynchronizedSigarProxyCache.java b/bindings/java/src/net/hyperic/sigar/SynchronizedSigarProxyCache.java new file mode 100644 index 00000000..204f29be --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/SynchronizedSigarProxyCache.java @@ -0,0 +1,33 @@ +package net.hyperic.sigar; + +import java.lang.reflect.Method; + +public class SynchronizedSigarProxyCache + extends SigarProxyCache { + + private static Object lock = new Object(); + private static SigarProxy instance = null; + + public static SigarProxy getInstance(Sigar sigar) + throws SigarException { + + synchronized (lock) { + if (instance == null) { + instance = SigarProxyCache.newInstance(sigar); + } + } + + return instance; + } + + public SynchronizedSigarProxyCache(Sigar sigar, int expire) { + super(sigar, expire); + } + + //all Objects returned are read-only. + public synchronized Object invoke(Object proxy, Method method, Object[] args) + throws SigarException, SigarNotImplementedException { + + return super.invoke(proxy, method, args); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/cmd/CpuInfo.java b/bindings/java/src/net/hyperic/sigar/cmd/CpuInfo.java new file mode 100644 index 00000000..2a411e61 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/cmd/CpuInfo.java @@ -0,0 +1,45 @@ +package net.hyperic.sigar.cmd; + +import net.hyperic.sigar.CpuPerc; +import net.hyperic.sigar.SigarException; + +public class CpuInfo extends SigarCommandBase { + + public CpuInfo(Shell shell) { + super(shell); + } + + public CpuInfo() { + super(); + } + + public String getUsageShort() { + return "Display cpu information"; + } + + public void output(String[] args) throws SigarException { + net.hyperic.sigar.CpuInfo[] infos = + this.sigar.getCpuInfoList(); + + CpuPerc[] cpus = this.sigar.getCpuPercList(); + + this.out.println(infos.length + " total CPUs.."); + + for (int i=0; i 0 ? + "Local Loopback" : "Ethernet"; + + String hwaddr = ""; + if (!NetFlags.NULL_HWADDR.equals(ifconfig.getHwaddr())) { + hwaddr = " HWaddr " + ifconfig.getHwaddr(); + } + + println(ifconfig.getName() + "\t" + + "Link encap:" + encap + + hwaddr); + + String ptp = ""; + if ((flags & NetFlags.IFF_POINTOPOINT) > 0) { + ptp = " P-t-P:" + ifconfig.getDestination(); + } + + String bcast = ""; + if ((flags & NetFlags.IFF_BROADCAST) > 0) { + bcast = " Bcast:" + ifconfig.getBroadcast(); + } + + println("\t" + + "inet addr:" + ifconfig.getAddress() + + ptp + //unlikely + bcast + + " Mask:" + ifconfig.getNetmask()); + + println("\t" + + NetFlags.getIfFlagsString(flags) + + " MTU:" + ifconfig.getMtu() + + " Metric:" + ifconfig.getMetric()); + try { + NetInterfaceStat ifstat = + this.sigar.getNetInterfaceStat(name); + + println("\t" + + "RX packets:" + ifstat.getRxPackets() + + " errors:" + ifstat.getRxErrors() + + " dropped:" + ifstat.getRxDropped() + + " overruns:" + ifstat.getRxOverruns() + + " frame:" + ifstat.getRxFrame()); + + println("\t" + + "TX packets:" + ifstat.getTxPackets() + + " errors:" + ifstat.getTxErrors() + + " dropped:" + ifstat.getTxDropped() + + " overruns:" + ifstat.getTxOverruns() + + " carrier:" + ifstat.getTxCarrier()); + println("\t" + "collisions:" + + ifstat.getTxCollisions()); + + long rxBytes = ifstat.getRxBytes(); + long txBytes = ifstat.getTxBytes(); + + println("\t" + + "RX bytes:" + rxBytes + + " (" + Sigar.formatSize(rxBytes) + ")" + + " " + + "TX bytes:" + txBytes + + " (" + Sigar.formatSize(txBytes) + ")"); + } catch (SigarException e) { + } + + println(""); + } + + public static void main(String[] args) throws Exception { + new Ifconfig().processCommand(args); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/cmd/Kill.java b/bindings/java/src/net/hyperic/sigar/cmd/Kill.java new file mode 100644 index 00000000..69c0d8ad --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/cmd/Kill.java @@ -0,0 +1,60 @@ +package net.hyperic.sigar.cmd; + +import net.hyperic.sigar.SigarException; + +public class Kill extends SigarCommandBase { + + public Kill(Shell shell) { + super(shell); + } + + public Kill() { + super(); + } + + protected boolean validateArgs(String[] args) { + return args.length == 1 || args.length == 2; + } + + public String getSyntaxArgs() { + return "[signal] "; + } + + public String getUsageShort() { + return "Send signal to a process"; + } + + public boolean isPidCompleter() { + return true; + } + + public void output(String[] args) throws SigarException { + int signum = 15; //SIGTERM + long[] pids; + String query; + + if (args.length == 2) { + query = args[1]; + try { + signum = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + //XXX convert SIGFOO to number + throw new SigarException(e.getMessage()); + } + } + else { + query = args[0]; + } + + pids = this.shell.findPids(new String[] { query }); + + for (int i=0; i 0) { + flags = getFlags(args, flags); + } + + NetConnection[] connections = this.sigar.getNetConnectionList(flags); + println("Proto\tLocal Address\tForeign Address"); + + for (int i=0; i"; + } + + public String getUsageShort() { + return "Run process table query"; + } + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + if (args.length > 0) { + if (getSubHandler(args[0]) != null) { + super.processCommand(args); + return; + } + } + + if (args.length != 1) { + throw new ShellCommandUsageException(getSyntax()); + } + + long[] pids; + try { + pids = this.shell.findPids(args); + } catch (NumberFormatException e) { + throw new ShellCommandUsageException(getSyntax()); + } catch (SigarException e) { + throw new ShellCommandExecException(e.getMessage()); + } + + for (int i=0; i= 1) && + Character.isDigit(line.charAt(0))) + { + return line; + } + + return + ((GetlineCompleter)this.shell.getHandler("ptql")).complete(line); + } + + public String complete(String line) { + if (isPidCompleter()) { + return completePid(line); + } + GetlineCompleter c = getCompleter(); + if (c != null) { + return c.complete(line); + } + + this.completer.setCollection(getCompletions()); + return this.completer.complete(line); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/cmd/Tail.java b/bindings/java/src/net/hyperic/sigar/cmd/Tail.java new file mode 100644 index 00000000..9d96ae30 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/cmd/Tail.java @@ -0,0 +1,62 @@ +package net.hyperic.sigar.cmd; + +import java.io.IOException; +import java.io.BufferedReader; +import java.io.Reader; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.FileInfo; +import net.hyperic.sigar.FileTail; +import net.hyperic.sigar.FileWatcherThread; + +public class Tail { + + public static void main(String[] args) throws SigarException { + final String pattern; + + Sigar sigar = new Sigar(); + + FileWatcherThread watcherThread = + FileWatcherThread.getInstance(); + + watcherThread.doStart(); + + watcherThread.setInterval(1000); + + FileTail watcher = + new FileTail(sigar) { + public void tail(FileInfo info, Reader reader) { + String line; + BufferedReader buffer = + new BufferedReader(reader); + + if (getFiles().size() > 1) { + System.out.println("==> " + + info.getName() + + " <=="); + } + + try { + while ((line = buffer.readLine()) != null) { + System.out.println(line); + } + } catch (IOException e) { + System.out.println(e); + } + } + }; + + for (int i=0; i + * % java -jar sigar-bin/lib/sigar.jar Top State.Name.eq=java + */ +public class Top { + private static final int SLEEP_TIME = 1000 * 5; + + private static final String HEADER = + "PID\tUSER\tSTIME\tSIZE\tRSS\tSHARE\tSTATE\tTIME\t%CPU\tCOMMAND"; + + private static final String CLEAR_SCREEN = "\033[2J"; + + public static void main(String[] args) throws Exception { + Sigar sigarImpl = new Sigar(); + + SigarProxy sigar = + SigarProxyCache.newInstance(sigarImpl, SLEEP_TIME); + + while (true) { + System.out.print(CLEAR_SCREEN); + + System.out.println(Uptime.getInfo(sigar)); + + System.out.println(CurrentProcessSummary.get(sigar)); + + System.out.println(sigar.getCpuPerc()); + + System.out.println(sigar.getMem()); + + System.out.println(sigar.getSwap()); + + System.out.println(); + + System.out.println(HEADER); + + long[] pids = Shell.getPids(sigar, args); + + for (int i=0; i 1) ? "days" : "day") + ", "; + } + + minutes = (int)uptime / 60; + hours = minutes / 60; + hours %= 24; + minutes %= 60; + + if (hours != 0) { + retval += hours + ":" + minutes; + } + else { + retval += minutes + " min"; + } + + return retval; + } + + private static String getCurrentTime() { + return new SimpleDateFormat("h:mm a").format(new Date()); + } + + //pretty close to uptime command, but we don't output number of users yet + public static void main(String[] args) throws Exception { + new Uptime().processCommand(args); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/cmd/Version.java b/bindings/java/src/net/hyperic/sigar/cmd/Version.java new file mode 100644 index 00000000..6aeccf6b --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/cmd/Version.java @@ -0,0 +1,49 @@ +package net.hyperic.sigar.cmd; + +import java.io.PrintStream; + +import net.hyperic.sigar.Sigar; + +public class Version extends SigarCommandBase { + private static String[] SYS_PROPS = { + "os.name", + "os.version", + "os.arch", + "java.vm.version", + "java.vm.vendor", + "sun.arch.data.model", + "sun.cpu.endian", + "sun.os.patch.level", + }; + + public Version(Shell shell) { + super(shell); + } + + public Version() { + super(); + } + + public String getUsageShort() { + return "Display sigar version"; + } + + public static void printInfo(PrintStream os) { + os.println("Sigar version=" + Sigar.VERSION); + os.println(""); + + for (int i=0; i " + + new File(file).getCanonicalPath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + System.out.println(link.getTypeChar() + + info.getPermissionsString() + "\t" + + info.getUid() + "\t" + info.getGid() + "\t" + + info.getSize() + "\t" + + new Date(info.getMtime()) + "\t" + + file); + + if (info.getType() == FileInfo.TYPE_DIR) { + info.enableDirStat(true); + + DirStat stats = sigar.getDirStat(file); + System.out.println(" Files......." + stats.getFiles()); + System.out.println(" Subdirs....." + stats.getSubdirs()); + System.out.println(" Symlinks...." + stats.getSymlinks()); + System.out.println(" Chrdevs....." + stats.getChrdevs()); + System.out.println(" Blkdevs....." + stats.getBlkdevs()); + System.out.println(" Sockets....." + stats.getSockets()); + System.out.println(" Total......." + stats.getTotal()); + } + } + + private static void add(Sigar sigar, + FileWatcher watcher, + String file, + boolean recurse) + throws SigarException { + + FileInfo info = watcher.add(file); + printHeader(sigar, info); + + if (!recurse) { + return; + } + + if (info.getType() == FileInfo.TYPE_DIR) { + File[] dirs = + new File(info.getName()).listFiles(new FileFilter() { + public boolean accept(File file) { + return file.isDirectory() && file.canRead(); + } + }); + + for (int i=0; i 0) { + //skip domain name + name = name.substring(ix + 1); + } + + StringTokenizer st = new StringTokenizer(name, ","); + + while (st.hasMoreTokens()) { + String attr = st.nextToken(); + ix = attr.indexOf('='); + + String key = attr.substring(0, ix); + String val = attr.substring(key.length()+1); + + if (key.equals(PROP_TYPE)) { + invoker.setType(val); + } + else if (key.equals(PROP_ARG)) { + //need to decode value, e.g. Arg=C%3D\ => Arg=C:\ + invoker.setArg(decode(val)); + } + } + + cache.put(name, invoker); + + return invoker; + } + + //like URLDecoder but we only look for escaped ObjectName + //delimiters ':', '=' and ',' + public static String decode(String val) { + StringBuffer buf = new StringBuffer(val.length()); + boolean changed = false; + int len = val.length(); + int i = 0; + + while (i < len) { + char c = val.charAt(i); + + if (c == '%') { + char d; + if (i+2 > len) { + break; + } + String s = val.substring(i+1, i+3); + + if (s.equals("3A")) { + d = ':'; + } + else if (s.equals("3D")) { + d = '='; + } + else if (s.equals("2C")) { + d = ','; + } + else { + buf.append(c); + i++; + continue; + } + + changed = true; + buf.append(d); + i += 3; + } + else { + buf.append(c); + i++; + } + } + + return changed ? buf.toString() : val; + } + + private void setArg(String val) { + this.arg = val; + } + + /** + * The value of the parsed Arg property. + */ + public String getArg() { + return this.arg; + } + + /** + * Returns a JMX style object name with given property values. + */ + public static String getObjectName(String type, String arg) { + String s = DOMAIN_NAME + ":"; + + s += PROP_TYPE + "=" + type; + + if (arg != null) { + s += "," + PROP_ARG + "=" + arg; + } + + return s; + } + + /** + * Returns a JMX style object that represents this instance. + */ + public String getObjectName() { + return getObjectName(getType(), getArg()); + } + + public String toString() { + return getObjectName(); + } + + /** + * Invoke an attribute method for the given object. + * Example: + * SigarInvokerJMX invoker = new SigarInvokerJMX(proxy, "Type=Mem"); + * Object val = invoker.invoke("Free"); + * + * @param attr The attribute name (e.g. Total) + * @exception SigarException If invocation fails. + */ + public Object invoke(String attr) + throws SigarException { + + return super.invoke(getArg(), attr); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/jmx/SigarProcess.java b/bindings/java/src/net/hyperic/sigar/jmx/SigarProcess.java new file mode 100644 index 00000000..3a44d52f --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/jmx/SigarProcess.java @@ -0,0 +1,71 @@ +package net.hyperic.sigar.jmx; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarProxy; +import net.hyperic.sigar.SigarProxyCache; + +/** + * Implement the SigarProcessMBean to provide current process info + * via JMX. + */ + +public class SigarProcess implements SigarProcessMBean { + + private Sigar sigar; + private SigarProxy sigarProxy; + + private static final String procMemName = + SigarInvokerJMX.getObjectName("ProcMem", "$$"); + + private static final String procTimeName = + SigarInvokerJMX.getObjectName("ProcTime", "$$"); + + private SigarInvokerJMX procMem, procTime; + + public SigarProcess() { + this(null); + } + + public SigarProcess(String path) { + sigar = new Sigar(); + sigarProxy = SigarProxyCache.newInstance(sigar); + + procMem = SigarInvokerJMX.getInstance(sigarProxy, + procMemName); + + procTime = SigarInvokerJMX.getInstance(sigarProxy, + procTimeName); + } + + public void close() { + } + + private Long getLongValue(SigarInvokerJMX invoker, String attr) { + try { + return (Long)invoker.invoke(attr); + } catch (SigarException e) { + return new Long(-1); + } + } + + public Long getMemSize() { + return getLongValue(procMem, "Size"); + } + + public Long getMemVsize() { + return getLongValue(procMem, "Vsize"); + } + + public Long getMemShare() { + return getLongValue(procMem, "Share"); + } + + public Long getTimeUser() { + return getLongValue(procTime, "Utime"); + } + + public Long getTimeSys() { + return getLongValue(procTime, "Stime"); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/jmx/SigarProcessMBean.java b/bindings/java/src/net/hyperic/sigar/jmx/SigarProcessMBean.java new file mode 100644 index 00000000..313a1d89 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/jmx/SigarProcessMBean.java @@ -0,0 +1,19 @@ +package net.hyperic.sigar.jmx; + +/* + * yeah, yeah, we could generate this class via xdoclet, + * whoopdi-friggin-do... by hand is much less pain. + */ + +public interface SigarProcessMBean { + + public Long getMemSize(); + + public Long getMemVsize(); + + public Long getMemShare(); + + public Long getTimeUser(); + + public Long getTimeSys(); +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/DefaultPagerProcessor.java b/bindings/java/src/net/hyperic/sigar/pager/DefaultPagerProcessor.java new file mode 100644 index 00000000..730e23fa --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/DefaultPagerProcessor.java @@ -0,0 +1,22 @@ +package net.hyperic.sigar.pager; + +/** + * The default page processor does not process any elements + * that you're paging. This is useful if you're only looking + * to page through an existing collection, and you don't need + * to perform any transformations on the elements that are + * found to belong in the resultant page. + */ +public class DefaultPagerProcessor implements PagerProcessor { + + /** + * Default processor does not process anything, it just + * returns what was passed in. + * @param o The object to process. + * @return The same (completely unmodified) object that was passed in. + * @see PagerProcessor#processElement + */ + public Object processElement(Object o) { + return o; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/ListPageFetcher.java b/bindings/java/src/net/hyperic/sigar/pager/ListPageFetcher.java new file mode 100644 index 00000000..cc4c7165 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/ListPageFetcher.java @@ -0,0 +1,108 @@ +package net.hyperic.sigar.pager; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.ListIterator; + +/** + * A PageFetcher which works with a pre-fetched list as the + * data backing the fetcher. + */ + +public class ListPageFetcher extends PageFetcher { + private List data; + private int sortOrder; + + public ListPageFetcher(List data) { + super(); + this.data = data; + this.sortOrder = PageControl.SORT_UNSORTED; + } + + public PageList getPage(PageControl control) { + PageList res = new PageList(); + int startIdx, curIdx, endIdx; + + if (this.data.size() == 0) { + return new PageList(); + } + + this.ensureSortOrder(control); + res.setTotalSize(this.data.size()); + + startIdx = clamp(control.getPageEntityIndex(), 0, + this.data.size() - 1); + curIdx = startIdx; + + if (control.getPagesize() == PageControl.SIZE_UNLIMITED) { + endIdx = this.data.size(); + } + else { + endIdx = clamp(startIdx + control.getPagesize(), startIdx, + this.data.size()); + } + + for (ListIterator i=this.data.listIterator(startIdx); + i.hasNext() && curIdx < endIdx; + curIdx++) + { + res.add(i.next()); + } + + return res; + } + + private class DescSorter implements Comparator { + public int compare(Object o1, Object o2){ + return -((Comparable)o1).compareTo((Comparable)o2); + } + + public boolean equals(Object other){ + return false; + } + } + + private void ensureSortOrder(PageControl control) { + if (control.getSortorder() == this.sortOrder) { + return; + } + + this.sortOrder = control.getSortorder(); + if (this.sortOrder == PageControl.SORT_UNSORTED) { + return; + } + else if(this.sortOrder == PageControl.SORT_ASC) { + Collections.sort(data); + } + else if(this.sortOrder == PageControl.SORT_DESC) { + Collections.sort(data, new DescSorter()); + } + else { + throw new IllegalStateException("Unknown control sorting type: " + + this.sortOrder); + } + } + + /** + * Clamp a value to a range. If the passed value is + * less than the minimum, return the minimum. If it + * is greater than the maximum, assign the maximum. + * else return the passed value. + */ + private static int clamp(int val, int min, int max) { + return (int)clamp((long)val, (long)min, (long)max); + } + + private static long clamp(long val, long min, long max) { + if (val < min) { + return min; + } + + if (val > max) { + return max; + } + + return val; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PageControl.java b/bindings/java/src/net/hyperic/sigar/pager/PageControl.java new file mode 100644 index 00000000..6761decd --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PageControl.java @@ -0,0 +1,165 @@ +package net.hyperic.sigar.pager; + +import java.io.Serializable; + +/** + * A utility class to wrap up all the paging/sorting options that + * are frequently used with finders and other methods that return + * lists of things. + */ +public class PageControl implements Serializable, Cloneable { + public static final int SIZE_UNLIMITED = -1; + + public static final int SORT_UNSORTED = 0; + public static final int SORT_ASC = 1; + public static final int SORT_DESC = 2; + + private int pagenum = 0; + private int pagesize = SIZE_UNLIMITED; + private int sortorder = SORT_UNSORTED; + private int sortattribute = SortAttribute.DEFAULT; + + private Serializable metaData; // Meta-data that PageLists have returned + + + public PageControl() {} + + public PageControl(int pagenum, int pagesize) { + this.pagenum = pagenum; + this.pagesize = pagesize; + } + + public PageControl(int pagenum, int pagesize, + int sortorder, int sortattribute) { + this.pagenum = pagenum; + this.pagesize = pagesize; + this.sortorder = sortorder; + this.sortattribute = sortattribute; + } + + public boolean isAscending() { + return this.sortorder == SORT_ASC; + } + + public boolean isDescending() { + return this.sortorder == SORT_DESC; + } + + /** + * sets the initial defaults for the PageControl. Sort attribute specifies + * which attribute to sort on. + * + * @param pc + * @param defaultSortAttr specifies the attribute to sort on. + * @return PageControl + */ + public static PageControl initDefaults(PageControl pc, + int defaultSortAttr) { + if (pc == null) { + pc = new PageControl(); + } + else { + pc = (PageControl) pc.clone(); + } + + if (pc.getSortattribute() == SortAttribute.DEFAULT) { + pc.setSortattribute(defaultSortAttr); + } + if (pc.getSortorder() == SORT_UNSORTED) { + pc.setSortorder(SORT_ASC); + } + + return pc; + } + + /** @return The current page number (0-based) */ + public int getPagenum() { + return this.pagenum; + } + + /** @param pagenum Set the current page number to pagenum */ + public void setPagenum(int pagenum) { + this.pagenum = pagenum; + } + + /** @return The current page size */ + public int getPagesize() { + return this.pagesize; + } + + /** @param pagesize Set the current page size to this value */ + public void setPagesize(int pagesize) { + this.pagesize = pagesize; + } + + /** @return The sort order used. This is one of the SORT_XXX constants. */ + public int getSortorder() { + return this.sortorder; + } + + /** @param sortorder Sort order to use, one of the SORT_XXX constants. */ + public void setSortorder(int sortorder) { + this.sortorder = sortorder; + } + + /** @return The attribute that the sort is based on. */ + public int getSortattribute() { + return this.sortattribute; + } + + /** @param attr Set the attribute that the sort is based on. */ + public void setSortattribute(int attr) { + this.sortattribute = attr; + } + + public Serializable getMetaData() { + return this.metaData; + } + + public void getMetaData(Serializable metaData) { + this.metaData = metaData; + } + + /** + * Get the index of the first item on the page as dictated by the + * page size and page number. + */ + public int getPageEntityIndex() { + return this.pagenum * this.pagesize; + } + + public String toString() { + StringBuffer s = new StringBuffer("{"); + s.append("pn=" + this.pagenum + " "); + s.append("ps=" + this.pagesize + " "); + + s.append("so="); + + switch(this.sortorder) { + case SORT_ASC: + s.append("asc "); + break; + case SORT_DESC: + s.append("desc"); + break; + case SORT_UNSORTED: + s.append("unsorted "); + break; + default: + s.append(' '); + } + + s.append("sa=" + this.sortattribute + " "); + s.append("}"); + + return s.toString(); + } + + public Object clone() { + PageControl res = + new PageControl(this.pagenum, this.pagesize, + this.sortorder, this.sortattribute); + res.metaData = metaData; + return res; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PageFetchException.java b/bindings/java/src/net/hyperic/sigar/pager/PageFetchException.java new file mode 100644 index 00000000..8f8bfe51 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PageFetchException.java @@ -0,0 +1,11 @@ +package net.hyperic.sigar.pager; + +public class PageFetchException extends Exception { + public PageFetchException() { + super(); + } + + public PageFetchException(String s) { + super(s); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PageFetcher.java b/bindings/java/src/net/hyperic/sigar/pager/PageFetcher.java new file mode 100644 index 00000000..79c2b5ae --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PageFetcher.java @@ -0,0 +1,14 @@ +package net.hyperic.sigar.pager; + +/** + * A class which abstracts the fetching of the data for the pages + * so callers can deal directly with a single object. + */ + +public abstract class PageFetcher { + /** + * Get a page of data, as specified by the control. + */ + public abstract PageList getPage(PageControl control) + throws PageFetchException; +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PageList.java b/bindings/java/src/net/hyperic/sigar/pager/PageList.java new file mode 100644 index 00000000..0b919c02 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PageList.java @@ -0,0 +1,76 @@ +package net.hyperic.sigar.pager; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; + +/** + * A utility class that contains all a "page" of data that is viewable + *
+ * this list may or may not conain the entire list of information. + * generally this list conains a subset of data. + *
+ * ex. say we have a list of 5000 users. the entire list does not need to be + * returned to only display the first 15 items, the user is only going to see + * the first 15 both the user and the application the user will want to know + * that there are 5000 users in the system. + *
+ * + */ +public class PageList extends ArrayList implements Serializable { + private int totalSize = 0; + private boolean isUnbounded; // Is the total size of the list known? + private Serializable metaData; + + public PageList() { + super(); + this.isUnbounded = false; + } + + public PageList(Collection c, int totalSize) { + super(c); + this.totalSize = totalSize; + this.isUnbounded = false; + } + + public String toString() { + StringBuffer s = new StringBuffer("{"); + + s.append("totalSize=" + totalSize + " "); + s.append("}"); + return super.toString() + s.toString(); + + } + + /** returns the total size of the "masterlist" that this page is a + * subset of. + * @return Value of property listSize. + */ + public int getTotalSize() { + return Math.max(this.size(), this.totalSize); + } + + /** Sets the total size of the "masterlist" that this page is a subset of. + * @param totalSize New value of property listSize. + * + */ + public void setTotalSize(int totalSize) { + this.totalSize = totalSize; + } + + public void setMetaData(Serializable metaData){ + this.metaData = metaData; + } + + public Serializable getMetaData(){ + return this.metaData; + } + + public boolean isUnbounded(){ + return this.isUnbounded; + } + + public void setUnbounded(boolean isUnbounded){ + this.isUnbounded = isUnbounded; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/Pager.java b/bindings/java/src/net/hyperic/sigar/pager/Pager.java new file mode 100644 index 00000000..04e64a6c --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/Pager.java @@ -0,0 +1,313 @@ +package net.hyperic.sigar.pager; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Implements a generic pager. What is a pager? Let's say you + * have a large collection of objects, perhaps a long list of + * EJB Local Interfaces. You're interested in breaking the + * mammoth list out into a number pages, each with 25 items on it. + * You're interested in returning page #17 of such a collection. + * Why bother implementing the "skip past a bunch of things, then + * return pagesize items in the resultant colleciton" over and over + * again. + * + * You can also have the elements go through a processor that you + * supply as they move from the source collection to the + * destination collection. + */ +public class Pager { + + public static final String DEFAULT_PROCESSOR_CLASSNAME + = "net.hyperic.sigar.pager.DefaultPagerProcessor"; + + private static Map PagerProcessorMap = + Collections.synchronizedMap(new HashMap()); + private PagerProcessor processor = null; + private boolean skipNulls = false; + private PagerEventHandler eventHandler = null; + + private Pager ( PagerProcessor processor ) { + + this.processor = processor; + this.skipNulls = false; + this.eventHandler = null; + + if ( this.processor instanceof PagerProcessorExt ) { + this.skipNulls = ((PagerProcessorExt) this.processor).skipNulls(); + this.eventHandler + = ((PagerProcessorExt) this.processor).getEventHandler(); + } + } + + public static Pager getDefaultPager () { + try { + return getPager(DEFAULT_PROCESSOR_CLASSNAME); + } catch ( Exception e ) { + throw new IllegalStateException("This should never happen: " + e); + } + } + + /** + * Get a pager based on the PagerProcessor supplied. + */ + public static Pager getPager (String pageProcessorClassName) + throws InstantiationException, IllegalAccessException, + ClassNotFoundException + { + Pager p = null; + p = (Pager) PagerProcessorMap.get(pageProcessorClassName); + if ( p == null ) { + PagerProcessor processor = (PagerProcessor) + Class.forName(pageProcessorClassName).newInstance(); + p = new Pager(processor); + PagerProcessorMap.put(pageProcessorClassName, p); + } + return p; + } + + /** + * Seek to the specified pagenum in the source collection and + * return pagsize numberof of elements in the List. + * If pagenum or pagesize is -1, then everything in the + * source collection will be returned. + * + * @param source The source collection to seek through. + * @param pagenum The page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @param pagesize The size of each page. + * @return PageList containing results of seek. + */ + public PageList seek ( Collection source, int pagenum, int pagesize ) { + return seek(source,pagenum,pagesize,null); + } + + /** + * Seek to the specified pagenum in the source collection and return pagsize + * numberof of elements in the List, as specified the PageControl object. + * If pagenum or pagesize is -1, then everything in the + * source collection will be returned. + * + * @param source The source collection to seek through. + * @param pc The page control used for page size and page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @return PageList containing results of seek. + */ + public PageList seek ( Collection source, PageControl pc ) { + if (pc == null) + pc = new PageControl(); + + return seek(source, pc.getPagenum(), pc.getPagesize(), null); + } + + /** + * Seek to the specified pagenum in the source collection and + * return pagsize numberof of elements in the List. + * If pagenum or pagesize is -1, then everything in the + * source collection will be returned. + * + * @param source The source collection to seek through. + * @param pagenum The page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @param pagesize The size of each page. + * @param procData - any data object required by the processor. + * @return PageList containing results of seek. + */ + public PageList seek ( Collection source, int pagenum, int pagesize, + Object procData ) { + + PageList dest = new PageList(); + seek(source, dest, pagenum, pagesize, procData); + dest.setTotalSize(source.size()); + return dest; + } + + /** + * Seek to the specified pagenum in the source collection and place + * pagesize number of elements into the dest collection. + * If pagenum or pagesize is -1, then everything in the + * source collection will be placed in the dest collection. + * + * @param source The source collection to seek through. + * @param dest The collection to place results into. + * @param pagenum The page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @param pagesize The size of each page. + */ + public void seek ( Collection source, Collection dest, int pagenum, + int pagesize ) { + seek(source,dest,pagenum,pagesize,null); + } + /** + * Seek to the specified pagenum in the source collection and place + * pagesize number of elements into the dest collection. + * If pagenum or pagesize is -1, then everything in the + * source collection will be placed in the dest collection. + * + * @param source The source collection to seek through. + * @param dest The collection to place results into. + * @param pagenum The page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @param pagesize The size of each page. + * @param procData any object required to process the item. + */ + public void seek ( Collection source, Collection dest, int pagenum, + int pagesize, Object procData) { + + Iterator iter = source.iterator(); + int i, currentPage; + + if ( pagesize == -1 || pagenum == -1 ) { + pagenum = 0; + pagesize = Integer.MAX_VALUE; + } + + for ( i=0, currentPage=0; + iter.hasNext() && currentPage < pagenum; + i++, currentPage += (i % pagesize == 0) ? 1:0 ) { + iter.next(); + } + + if ( this.eventHandler != null ) this.eventHandler.init(); + + if ( this.skipNulls ) { + Object elt; + while ( iter.hasNext() && dest.size() < pagesize ) { + if (this.processor instanceof PagerProcessorExt) + elt = ((PagerProcessorExt)this.processor) + .processElement(iter.next(), procData); + else + elt = this.processor.processElement(iter.next()); + if ( elt == null ) + continue; + dest.add(elt); + } + } else { + while ( iter.hasNext() && dest.size() < pagesize ) { + dest.add(this.processor.processElement(iter.next())); + } + } + + if ( this.eventHandler != null ) this.eventHandler.cleanup(); + } + + /** + * Seek to the specified pagenum in the source collection and place + * pagesize number of elements into the dest collection. Unlike, + * seek(), all items are passed to the Processor or ProcessorExt + * regardless whether they are placed in dest collection. If pagenum + * or pagesize is -1, then everything in the source collection will + * be placed in the dest collection. + * + * @param source The source collection to seek through. + * @param pagenum The page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @param pagesize The size of each page. + * @param procData any object required to process the item. + */ + public PageList seekAll ( Collection source, int pagenum, int pagesize, + Object procData ) { + + PageList dest = new PageList(); + seekAll(source, dest, pagenum, pagesize, procData); + dest.setTotalSize(source.size()); + return dest; + } + + /** + * Seek to the specified pagenum in the source collection and place + * pagesize number of elements into the dest collection. Unlike, + * seek(), all items are passed to the Processor or ProcessorExt + * regardless whether they are placed in dest collection. If pagenum + * or pagesize is -1, then everything in the source collection will + * be placed in the dest collection. + * + * @param source The source collection to seek through. + * @param dest The collection to place results into. + * @param pagenum The page number to seek to. If there not + * enough pages in the collection, then an empty list will be returned. + * @param pagesize The size of each page. + * @param procData any object required to process the item. + */ + public void seekAll ( Collection source, Collection dest, int pagenum, + int pagesize, Object procData) { + + Iterator iter = source.iterator(); + int i, currentPage; + + if ( pagesize == -1 || pagenum == -1 ) { + pagenum = 0; + pagesize = Integer.MAX_VALUE; + } + + // PR:8434 : Multi-part paging fixes. + // 1.) Invoke the pager processor external which in many cases may + // be keeping track of element [in|ex]clusion. + // 2.) The counter 'i' is used with modulus arithmetic to determine + // which page we should be on. Don't increment it if the proc-ext + // indicated that the element should not be paged. + // 3.) 'i' begins it's existance at 0. Zero modulus anything yields + // zero. So the ternary expression needs to check for this initial + // condition and not increment the page number. + for ( i=0, currentPage=0; + iter.hasNext() && currentPage < pagenum; + currentPage += (i != 0 && i % pagesize == 0) ? 1:0 ) { + if (this.processor instanceof PagerProcessorExt) { + Object ret = ((PagerProcessorExt)this.processor) + .processElement(iter.next(), procData); + if (ret != null) { + i++; + } + } else { + this.processor.processElement(iter.next()); + i++; + } + } + + if ( this.eventHandler != null ) this.eventHandler.init(); + + if ( this.skipNulls ) { + Object elt; + while ( iter.hasNext() ) { + if (this.processor instanceof PagerProcessorExt) + elt = ((PagerProcessorExt)this.processor) + .processElement(iter.next(), procData); + else + elt = this.processor.processElement(iter.next()); + if ( elt == null ) + continue; + + if (dest.size() < pagesize) + dest.add(elt); + } + } else { + while ( iter.hasNext() ) { + Object elt = this.processor.processElement(iter.next()); + if (dest.size() < pagesize) + dest.add(elt); + } + } + + if ( this.eventHandler != null ) this.eventHandler.cleanup(); + } + + /** Process all objects in the source page list and return the destination + * page list with the same total size + */ + public PageList processAll(PageList source) { + PageList dest = new PageList(); + for (Iterator it = source.iterator(); it.hasNext(); ) { + Object elt = this.processor.processElement(it.next()); + if ( elt == null ) + continue; + dest.add(elt); + } + + dest.setTotalSize(source.getTotalSize()); + return dest; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PagerEventHandler.java b/bindings/java/src/net/hyperic/sigar/pager/PagerEventHandler.java new file mode 100644 index 00000000..acea683b --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PagerEventHandler.java @@ -0,0 +1,15 @@ +package net.hyperic.sigar.pager; + +/** + * This class is useful for classes that implement PagerProcessorExt and + * need to do some initialization before paging begins and some cleanup + * after paging has ended. + */ +public interface PagerEventHandler { + + /** Called before paging begins. */ + public void init(); + + /** Called after paging ends. */ + public void cleanup(); +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PagerProcessor.java b/bindings/java/src/net/hyperic/sigar/pager/PagerProcessor.java new file mode 100644 index 00000000..00ac301e --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PagerProcessor.java @@ -0,0 +1,19 @@ +package net.hyperic.sigar.pager; + +/** + * Provides a point of extensibility in the paging behavior. + * If you supply a PagerProcessor when you get a Pager, + * then that processor will be called to process each element + * as the pager moves it from the source collection to the + * destination collection. + */ +public interface PagerProcessor { + + /** + * Process an element as the pager moves it from the source + * collection to the destination collection. + * @param o The object to process. + * @return The processed object. + */ + public Object processElement(Object o); +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/PagerProcessorExt.java b/bindings/java/src/net/hyperic/sigar/pager/PagerProcessorExt.java new file mode 100644 index 00000000..ac62627a --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/PagerProcessorExt.java @@ -0,0 +1,37 @@ +package net.hyperic.sigar.pager; + +/** + * Provides a point of extensibility in the paging behavior. + * If you supply a PagerProcessor when you get a Pager, + * then that processor will be called to process each element + * as the pager moves it from the source collection to the + * destination collection. + */ +public interface PagerProcessorExt extends PagerProcessor { + + /** + * Get the event handler for this pager. May return null to indicate + * that no event handler should be used. + */ + public PagerEventHandler getEventHandler(); + + /** + * Determines if null values are included in the Pager's results. + * @return If this method returns true, then when the processElement + * method returns null, that element will not be included in the results. + * If this methods returns false, then nulls may be added to the result + * page. + */ + public boolean skipNulls(); + + /** + * Process an element as the pager moves it from the source + * collection to the destination collection. This version + * allows an additional argument to be passed along. + * @param o1 The object to process. + * @param o2 Additional data required to processElement. + * @return The processed object. + */ + public Object processElement(Object o1, Object o2); + +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/SortAttribute.java b/bindings/java/src/net/hyperic/sigar/pager/SortAttribute.java new file mode 100644 index 00000000..df3e014e --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/SortAttribute.java @@ -0,0 +1,52 @@ +package net.hyperic.sigar.pager; + +import java.io.Serializable; + +public class SortAttribute implements Serializable { + + private SortAttribute () {} + + public static final int DEFAULT = 0; + + // Generic attributes + public static final int NAME = 1; + public static final int CTIME = 2; + + // Authz sort attributes - specifieds which column to store on + // for example, for 'subject_name', sort on column #3 + public static final int ROLE_NAME = 1; + public static final int RESGROUP_NAME = 2; + public static final int RESTYPE_NAME = 4; + public static final int RESOURCE_NAME = 5; + public static final int OPERATION_NAME = 6; + public static final int ROLE_MEMBER_CNT= 17; + + public static final int SUBJECT_NAME = 3; + public static final int FIRST_NAME = 7; + public static final int LAST_NAME = 8; + + // Event sort attributes + public static final int EVENT_LOG_CTIME = 1; + + // Control sort attributes + public static final int CONTROL_ACTION = 9; + public static final int CONTROL_STATUS = 10; + public static final int CONTROL_STARTED = 11; + public static final int CONTROL_ELAPSED = 12; + public static final int CONTROL_DATESCHEDULED = 13; + public static final int CONTROL_DESCRIPTION = 14; + public static final int CONTROL_NEXTFIRE = 15; + public static final int CONTROL_ENTITYNAME = 16; + + public static final int OWNER_NAME = 21; + + public static final int SERVICE_NAME = 22; + public static final int SERVICE_TYPE = 23; + + + public static final int RT_NAME = 24; + public static final int RT_LOW = 25; + public static final int RT_AVG = 26; + public static final int RT_PEAK = 27; + +} diff --git a/bindings/java/src/net/hyperic/sigar/pager/StaticPageFetcher.java b/bindings/java/src/net/hyperic/sigar/pager/StaticPageFetcher.java new file mode 100644 index 00000000..249f3a92 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/pager/StaticPageFetcher.java @@ -0,0 +1,53 @@ +package net.hyperic.sigar.pager; + +import java.util.Arrays; +import java.util.List; + +/** + * A fetcher which uses a static array of strings to page + * through. + */ + +public class StaticPageFetcher extends PageFetcher { + + private List data; + + public StaticPageFetcher(String[] data) { + this.data = Arrays.asList(data); + } + + public StaticPageFetcher(List data) { + this.data = data; + } + + public PageList getPage(PageControl control) + throws PageFetchException + { + PageList res = new PageList(); + int startIdx, endIdx; + + res.setTotalSize(this.data.size()); + + if (control.getPagesize() == PageControl.SIZE_UNLIMITED || + control.getPagenum() == -1) + { + res.addAll(this.data); + return res; + } + + startIdx = control.getPageEntityIndex(); + endIdx = startIdx + control.getPagesize(); + + if (startIdx >= this.data.size()) { + return res; + } + + if (endIdx > this.data.size()) { + endIdx = this.data.size(); + } + + res.addAll(this.data.subList(startIdx, endIdx)); + + return res; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ptql/MalformedQueryException.java b/bindings/java/src/net/hyperic/sigar/ptql/MalformedQueryException.java new file mode 100644 index 00000000..b0bbaff5 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ptql/MalformedQueryException.java @@ -0,0 +1,15 @@ +package net.hyperic.sigar.ptql; + +/** + * Exception for malformed process queries which cannot + * be parsed. + */ +public class MalformedQueryException extends Exception { + + public MalformedQueryException() { + } + + public MalformedQueryException(String message) { + super(message); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ptql/PidFileQuery.java b/bindings/java/src/net/hyperic/sigar/ptql/PidFileQuery.java new file mode 100644 index 00000000..a5948695 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ptql/PidFileQuery.java @@ -0,0 +1,54 @@ +package net.hyperic.sigar.ptql; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import net.hyperic.sigar.SigarException; + +public class PidFileQuery extends PidQuery { + + File file; + long modified = -1; + + public PidFileQuery(String file) { + this.file = new File(file); + } + + public long getPid() + throws SigarException { + + if (!file.exists()) { + throw new SigarException(this.file + " does not exist"); + } + + long lastMod = file.lastModified(); + if (lastMod == this.modified) { + return this.pid; + } + + this.modified = lastMod; + + String line; + + try { + BufferedReader in = + new BufferedReader(new FileReader(this.file)); + line = in.readLine(); + } catch (FileNotFoundException e) { + throw new SigarException(e.getMessage()); + } catch (IOException e) { + throw new SigarException(e.getMessage()); + } + + try { + this.pid = Long.parseLong(line); + } catch (NumberFormatException e) { + throw new SigarException("Not a number: " + line); + } + + return this.pid; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ptql/PidQuery.java b/bindings/java/src/net/hyperic/sigar/ptql/PidQuery.java new file mode 100644 index 00000000..bcf1c6b4 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ptql/PidQuery.java @@ -0,0 +1,30 @@ +package net.hyperic.sigar.ptql; + +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarProxy; + +public class PidQuery implements ProcessQuery { + protected long pid; + + protected PidQuery() { } + + public PidQuery(long pid) { + this.pid = pid; + } + + public PidQuery(String pid) { + this.pid = Long.parseLong(pid); + } + + public long getPid() + throws SigarException { + + return this.pid; + } + + public boolean match(SigarProxy sigar, long pid) + throws SigarException { + + return pid == getPid(); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ptql/ProcessFinder.java b/bindings/java/src/net/hyperic/sigar/ptql/ProcessFinder.java new file mode 100644 index 00000000..211e0498 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ptql/ProcessFinder.java @@ -0,0 +1,152 @@ +package net.hyperic.sigar.ptql; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarNotImplementedException; +import net.hyperic.sigar.SigarProxy; +import net.hyperic.sigar.SigarProxyCache; + +public class ProcessFinder { + + private SigarProxy proxy; + + public ProcessFinder(SigarProxy proxy) { + this.proxy = proxy; + //getpid() cache to optimize queries on this process. + this.proxy.getPid(); + } + + public long findSingleProcess(ProcessQuery query) + throws SigarException, SigarNotImplementedException, + MalformedQueryException { + + if (query instanceof PidQuery) { + return ((PidQuery)query).getPid(); + } + + int i, matches = 0; + + long[] pids = this.proxy.getProcList(); + long pid=-1; + + for (i=0; i", + getClassName(), + il, this.pool); + + il.append(InstructionFactory.createLoad(Type.OBJECT, 0)); + il.append(this.factory.createInvoke("java.lang.Object", + "", + Type.VOID, Type.NO_ARGS, + Constants.INVOKESPECIAL)); + + il.append(InstructionFactory.createReturn(Type.VOID)); + method.setMaxStack(); + method.setMaxLocals(); + this.generator.addMethod(method.getMethod()); + il.dispose(); + } + + private void loadStandardArgs(QueryOp qop) { + + if (qop.isParent) { + loadSigarArg(); + } + + loadSigarArg(); + loadPidArg(); + + if (!qop.isParent) { + return; + } + + //e.g. sigar.getProcState(pid).getName() is converted to: + // sigar.getProcState(sigar.getProcState(pid).getPpid()).getName() + final String procState = SIGAR_PACKAGE + "ProcState"; + + this.qi.append(this.factory.createInvoke(PROXY_CLASS, + "getProcState", + new ObjectType(procState), + new Type[] { Type.LONG }, + Constants.INVOKEINTERFACE)); + + this.qi.append(this.factory.createInvoke(procState, + "getPpid", + Type.LONG, Type.NO_ARGS, + Constants.INVOKEVIRTUAL)); + } + + //e.g. sigar.getProcState(pid).getName() + //attrClass == "State" + //attr == "Name" + //type == Type.STRING (return type) + private void createStandardInvoker(String attrClass, String attr, Type type) { + this.qi.append(this.factory.createInvoke(PROXY_CLASS, + "getProc" + attrClass, + new ObjectType(PROC_PREFIX + attrClass), + new Type[] { Type.LONG }, + Constants.INVOKEINTERFACE)); + + this.qi.append(this.factory.createInvoke(PROC_PREFIX + attrClass, + "get" + attr, + type, Type.NO_ARGS, + Constants.INVOKEVIRTUAL)); + } + + private MalformedQueryException unsupportedOp(String name) { + return new MalformedQueryException("Unsupported operator: " + + name); + } + + private MalformedQueryException unsupportedMethod(String name) { + return new MalformedQueryException("Unsupported method: " + + name); + } + + private MalformedQueryException unsupportedAttribute(String name) { + return new MalformedQueryException("Unsupported attribute: " + + name); + } + + private StringOp getStringOp(String op) + throws QueryLoadException, + MalformedQueryException { + + StringOp sop = (StringOp)STROPS.get(op); + + if (sop == null) { + throw unsupportedOp(op); + } + + if (!COMPAT_1_4) { + if (op.equals("re")) { + throw new QueryLoadException(op + " requires jdk 1.4+"); + } + } + + return sop; + } + + private void createStringInvoker(String op) + throws QueryLoadException, + MalformedQueryException { + + StringOp sop = getStringOp(op); + + this.qi.append(this.factory.createInvoke("java.lang.String", sop.name, + sop.returnType, new Type[] { sop.type }, + Constants.INVOKEVIRTUAL)); + + if (op.equals("ct")) { + this.qi.append(new PUSH(this.pool, -1)); + } + + BranchInstruction branch = + InstructionFactory.createBranchInstruction(sop.opcode, null); + this.qi.append(branch); + + this.branches.add(branch); + } + + //special case + public void appendProcPortOp(String flags, String op, long val) + throws MalformedQueryException { + + //XXX flags current unused; could be used to narrow search scope. + QueryOp qop = new QueryOp(op); + + loadSigarArg(); + + this.qi.append(new PUSH(this.pool, val)); //port + + this.qi.append(this.factory.createInvoke(PROXY_CLASS, + "getProcPort", + Type.LONG, + new Type[] { Type.LONG }, + Constants.INVOKEINTERFACE)); + + loadPidArg(); + + this.qi.append(InstructionConstants.LCMP); + + Short sop = (Short)LNUMOPS.get(qop.op); + + if (sop == null) { + throw unsupportedOp(qop.op); + } + + BranchInstruction branch = + InstructionFactory.createBranchInstruction(sop.shortValue(), null); + this.qi.append(branch); + + this.branches.add(branch); + } + + //special case + public void appendEnvOp(String key, String op, String val) + throws QueryLoadException, + MalformedQueryException { + + QueryOp qop = new QueryOp(op); + + loadStandardArgs(qop); + + this.qi.append(new PUSH(this.pool, key)); + + this.qi.append(this.factory.createInvoke(PROXY_HELPER, + "getProcEnv", Type.STRING, + new Type[] { + new ObjectType(PROXY_CLASS), + Type.LONG, Type.STRING + }, + Constants.INVOKESTATIC)); + + this.qi.append(new PUSH(this.pool, val)); + + createStringInvoker(qop.op); + } + + //special case + public void appendArgsOp(int idx, String op, String val) + throws QueryLoadException, + MalformedQueryException { + + QueryOp qop = new QueryOp(op); + + loadStandardArgs(qop); + + this.qi.append(new PUSH(this.pool, idx)); + + this.qi.append(this.factory.createInvoke(PROXY_HELPER, + "getProcArg", Type.STRING, + new Type[] { + new ObjectType(PROXY_CLASS), + Type.LONG, Type.INT + }, + Constants.INVOKESTATIC)); + + this.qi.append(new PUSH(this.pool, val)); + + createStringInvoker(qop.op); + } + + public void appendArgsMatch(String op, String val) + throws QueryLoadException, + MalformedQueryException { + + QueryOp qop = new QueryOp(op); + + getStringOp(qop.op); //validate + + loadStandardArgs(qop); + + this.qi.append(new PUSH(this.pool, val)); + this.qi.append(new PUSH(this.pool, qop.op)); + + this.qi.append(this.factory.createInvoke(PROXY_HELPER, + "argsMatch", Type.BOOLEAN, + new Type[] { + new ObjectType(PROXY_CLASS), + Type.LONG, + Type.STRING, + Type.STRING + }, + Constants.INVOKESTATIC)); + + BranchInstruction branch = + InstructionFactory.createBranchInstruction(Constants.IFEQ, null); + this.qi.append(branch); + + this.branches.add(branch); + } + + public void appendStringOp(String attrClass, String attr, + String op, String val) + throws QueryLoadException, + MalformedQueryException { + + QueryOp qop = new QueryOp(op); + + loadStandardArgs(qop); + + createStandardInvoker(attrClass, attr, Type.STRING); + + if (qop.isValue) { + return; + } + + if (qop.isClone) { + append(val, null); + } + else { + this.qi.append(new PUSH(this.pool, val)); + } + + createStringInvoker(qop.op); + } + + public void appendNumberOp(String attrClass, String attr, + String op, int val) + throws MalformedQueryException { + + appendNumberOp(attrClass, attr, op, Type.INT, + 0, 0.0, val); + } + + public void appendNumberOp(String attrClass, String attr, + String op, long val) + throws MalformedQueryException { + + appendNumberOp(attrClass, attr, op, Type.LONG, + val, 0.0, 0); + } + + public void appendNumberOp(String attrClass, String attr, + String op, double val) + throws MalformedQueryException { + + appendNumberOp(attrClass, attr, op, Type.DOUBLE, + 0, val, 0); + } + + private void appendNumberOp(String attrClass, String attr, + String op, Type type, + long val, double dval, int ival) + throws MalformedQueryException { + + short opcode; + HashMap nops; + + if ((type == Type.INT) || + (type == Type.CHAR)) + { + nops = INUMOPS; + this.qi.append(new PUSH(this.pool, ival)); + } + else if (type == Type.DOUBLE) { + nops = LNUMOPS; + this.qi.append(new PUSH(this.pool, dval)); + } + else { + nops = LNUMOPS; + this.qi.append(new PUSH(this.pool, val)); + } + + QueryOp qop = new QueryOp(op); + + loadStandardArgs(qop); + + createStandardInvoker(attrClass, attr, type); + + Short sop = (Short)nops.get(qop.op); + + if (sop == null) { + throw unsupportedOp(qop.op); + } + + if (type == Type.LONG) { + this.qi.append(InstructionConstants.LCMP); + } + else if (type == Type.DOUBLE) { + this.qi.append(InstructionConstants.DCMPL); + } + + BranchInstruction branch = + InstructionFactory.createBranchInstruction(sop.shortValue(), null); + this.qi.append(branch); + + this.branches.add(branch); + } + + private void appendPidOp(String op, String val) + throws MalformedQueryException { + + long longVal; + short opcode; + HashMap nops = LNUMOPS; + + if (val.equals("$$")) { + loadSigarArg(); + + this.qi.append(this.factory.createInvoke(PROXY_CLASS, + "getPid", + Type.LONG, Type.NO_ARGS, + Constants.INVOKEINTERFACE)); + } + else { + try { + longVal = Long.parseLong(val); + } catch (NumberFormatException e) { + String msg = "Pid value '" + val + "' is not a number"; + throw new MalformedQueryException(msg); + } + + this.qi.append(new PUSH(this.pool, longVal)); + } + + loadPidArg(); + + QueryOp qop = new QueryOp(op); + + Short sop = (Short)nops.get(qop.op); + + if (sop == null) { + throw unsupportedOp(qop.op); + } + + this.qi.append(InstructionConstants.LCMP); + + BranchInstruction branch = + InstructionFactory.createBranchInstruction(sop.shortValue(), null); + this.qi.append(branch); + + this.branches.add(branch); + } + + public void append(String branch, String val) + throws QueryLoadException, + MalformedQueryException { + + QueryBranch qb = new QueryBranch(branch); + + String attrClass=qb.attrClass, attr=qb.attr, op=qb.op; + + if (attrClass.equals("Env")) { + appendEnvOp(attr, op, val); + } + else if (attrClass.equals("Args")) { + if (attr.equals("*")) { + //run op against all args + appendArgsMatch(op, val); + } + else { + int idx; + try { + idx = Integer.parseInt(attr); + } catch (NumberFormatException e) { + String msg = "Array index '" + attr + "' is not a number"; + throw new MalformedQueryException(msg); + } + appendArgsOp(idx, op, val); + } + } + else if (attrClass.equals("Port")) { + long port; + try { + port = Long.parseLong(val); + } catch (NumberFormatException e) { + String msg = "Port value '" + val + "' is not a number"; + throw new MalformedQueryException(msg); + } + appendProcPortOp(attr, op, port); + } + else if (attrClass.equals("Pid")) { + appendPidOp(op, val); + } + else { + Method method = (Method)IFMETHODS.get(attrClass); + + if (method == null) { + throw unsupportedMethod(attrClass); + } + + Class subtype = method.getReturnType(); + boolean isStringType = false; + + if (isSigarClass(subtype)) { + try { + method = subtype.getMethod("get" + attr, + NOPARAM); + } catch (NoSuchMethodException e) { + throw unsupportedAttribute(attr); + } + + if (method.getReturnType() == String.class) { + isStringType = true; + } + else if (method.getReturnType() == Character.TYPE) { + if (val.length() != 1) { + String msg = val + " is not a char"; + throw new MalformedQueryException(msg); + } + + int c = (int)val.charAt(0); + appendNumberOp(attrClass, attr, op, Type.CHAR, + 0, 0.0, c); + return; + } + else if (method.getReturnType() == Double.TYPE) { + try { + double doubleVal = Double.parseDouble(val); + appendNumberOp(attrClass, attr, op, doubleVal); + return; + } catch (NumberFormatException e) { + String msg = val + " is not a double"; + throw new MalformedQueryException(msg); + } + } + } + else { + isStringType = true; + } + + if (!isStringType && Character.isDigit(val.charAt(0))) { + try { + long longVal = Long.parseLong(val); + appendNumberOp(attrClass, attr, op, longVal); + return; + } catch (NumberFormatException e) { + } + } + + appendStringOp(attrClass, attr, op, val); + } + } + + String addModifier(String key, char modifier) + throws MalformedQueryException { + + int ix; + if ((ix = key.lastIndexOf(".")) < 0) { + throw new MalformedQueryException(); + } + + return key.substring(0, ix+1) + modifier + + key.substring(ix+1, key.length()); + } + + public void finish() { + this.qi.append(new PUSH(this.pool, 1)); + + BranchInstruction gotoBranch = + InstructionFactory.createBranchInstruction(Constants.GOTO, null); + + this.qi.append(gotoBranch); + + InstructionHandle target = + this.qi.append(new PUSH(this.pool, 0)); + + InstructionHandle retval = + this.qi.append(InstructionFactory.createReturn(Type.INT)); + + for (int i=0; i= map.branches.size())) { + String msg = "Variable out of range " + var; + throw new MalformedQueryException(msg); + } + + var = (String)map.branches.get(ix); + var = builder.addModifier(var, + ProcessQueryBuilder.MOD_VALUE); + key = builder.addModifier(key, + ProcessQueryBuilder.MOD_CLONE); + } catch (NumberFormatException e) { + var = System.getProperty(var, val); + } + + val = var; + } + + builder.append(key, val); + } + + builder.finish(); + + return builder.load(); + } + + private static ProcessQuery getPidInstance(String query) + throws MalformedQueryException { + + if (query.indexOf(",") > 0) { + throw new MalformedQueryException("Invalid Pid query"); + } + + String[] vals = QueryBranch.split(query); + + QueryBranch branch = new QueryBranch(vals[0]); + String val = vals[1]; + + if (!branch.op.equals("eq")) { + throw new MalformedQueryException("Invalid Pid operator"); + } + + if (branch.attr.equals("PidFile")) { + return new PidFileQuery(val); + } + + throw new MalformedQueryException("Unsupported method: " + + branch.attr); + } + + public static ProcessQuery getInstance(String query) + throws MalformedQueryException, + QueryLoadException { + + return getInstance(query, null); + } + + public static ProcessQuery getInstance(String query, SigarProxy sigar) + throws MalformedQueryException, + QueryLoadException { + + if (query == null) { + throw new MalformedQueryException("null query"); + } + + if (query.length() == 0) { + throw new MalformedQueryException("empty query"); + } + + ProcessQuery pQuery = (ProcessQuery)cache.get(query); + + if (pQuery != null) { + return pQuery; + } + + if (query.startsWith("Pid.PidFile.")) { + pQuery = getPidInstance(query); + cache.put(query, pQuery); + return pQuery; + } + + QueryBranchMap queries = new QueryBranchMap(); + StringTokenizer st = new StringTokenizer(query, ","); + + while (st.hasMoreTokens()) { + String[] vals = QueryBranch.split(st.nextToken()); + + queries.put(vals[0], vals[1]); + } + + ProcessQueryFactory factory = new ProcessQueryFactory(sigar); + + pQuery = factory.prepare(queries); + + cache.put(query, pQuery); + + return pQuery; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ptql/ProcessQueryGenerator.java b/bindings/java/src/net/hyperic/sigar/ptql/ProcessQueryGenerator.java new file mode 100644 index 00000000..f46857db --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ptql/ProcessQueryGenerator.java @@ -0,0 +1,67 @@ +package net.hyperic.sigar.ptql; + +import net.hyperic.sigar.ProcCred; +import net.hyperic.sigar.ProcState; +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarProxy; +import net.hyperic.sigar.SigarProxyCache; + +public class ProcessQueryGenerator { + + private ProcessFinder finder; + private SigarProxy sigar; + + public ProcessQueryGenerator(SigarProxy sigar) { + this.sigar = sigar; + this.finder = new ProcessFinder(sigar); + } + + public String generate(long pid) + throws SigarException { + + StringBuffer query = new StringBuffer(); + + ProcState state = sigar.getProcState(pid); + query.append("State.Name.eq=" + state.getName()); + + if (this.finder.find(query).length == 1) { + return query.toString(); + } + + try { + ProcCred cred = sigar.getProcCred(pid); + query.append(",").append("Cred.Uid.eq=" + cred.getUid()); + query.append(",").append("Cred.Gid.eq=" + cred.getGid()); + + if (this.finder.find(query).length == 1) { + return query.toString(); + } + } catch (SigarException e) { + } + + try { + String[] args = sigar.getProcArgs(pid); + for (int i=args.length-1; i>=0; i--) { + int j; + //common case for java apps, last arg is the classname + //use -1 for query since number of args may change, + //but last arg is always the classname. + if (i == args.length-1) { + j = -1; + } + else { + j = i; + } + query.append(",").append("Args." + j + ".eq=" + args[i]); + + if (this.finder.find(query).length == 1) { + return query.toString(); + } + } + } catch (SigarException e) { + } + + return null; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/ptql/ProcessQueryHelper.java b/bindings/java/src/net/hyperic/sigar/ptql/ProcessQueryHelper.java new file mode 100644 index 00000000..a64f0689 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/ptql/ProcessQueryHelper.java @@ -0,0 +1,132 @@ +package net.hyperic.sigar.ptql; + +import java.util.Map; +import java.util.HashMap; + +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarNotImplementedException; +import net.hyperic.sigar.SigarProxy; + +public class ProcessQueryHelper { + + static HashMap stringMatchers = new HashMap(); + + static { + stringMatchers.put("eq", new StringEqMatcher()); + stringMatchers.put("ne", new StringNeMatcher()); + stringMatchers.put("sw", new StringSwMatcher()); + stringMatchers.put("ew", new StringEwMatcher()); + //stringMatchers.put("re", new StringReMatcher()); + stringMatchers.put("ct", new StringCtMatcher()); + } + + //avoid NPE if key does not exist. + public static String getProcEnv(SigarProxy proxy, long pid, String key) + throws SigarException, SigarNotImplementedException { + + Map vars; + + try { + vars = proxy.getProcEnv(pid); + } catch (SigarNotImplementedException e) { + throw e; + } catch (SigarException e) { + return ""; + } + + String val = (String)vars.get(key); + + if (val == null) { + return ""; + } + + return val; + } + + //avoid ArrayOutOfBoundsException + public static String getProcArg(SigarProxy proxy, long pid, int num) + throws SigarException, SigarNotImplementedException { + + String[] args; + + try { + args = proxy.getProcArgs(pid); + } catch (SigarNotImplementedException e) { + throw e; + } catch (SigarException e) { + return ""; + } + + //e.g. find last element of args: Args.-1.eq=weblogic.Server + if (num < 0) { + num += args.length; + } + + if ((num >= args.length) || + (num < 0)) { + return ""; + } + + return args[num]; + } + + public interface StringMatcher { + public boolean match(String left, String right); + } + + static class StringEqMatcher implements StringMatcher { + public boolean match(String left, String right) { + return left.equals(right); + } + } + + static class StringNeMatcher implements StringMatcher { + public boolean match(String left, String right) { + return !left.equals(right); + } + } + + static class StringEwMatcher implements StringMatcher { + public boolean match(String left, String right) { + return left.endsWith(right); + } + } + + static class StringSwMatcher implements StringMatcher { + public boolean match(String left, String right) { + return left.startsWith(right); + } + } + + static class StringCtMatcher implements StringMatcher { + public boolean match(String left, String right) { + return left.indexOf(right) != -1; + } + } + + //XXX requires jdk 1.4+ to compile + /* + static class StringReMatcher implements StringMatcher { + public boolean match(String left, String right) { + return left.matches(right); + } + } + */ + public static boolean argsMatch(SigarProxy proxy, long pid, + String value, String op) + throws SigarException, SigarNotImplementedException { + + String[] args = proxy.getProcArgs(pid); + + StringMatcher matcher = + (StringMatcher)stringMatchers.get(op); + + for (int i=0; i '" + line + "'"); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/MultiwordShellCommand.java b/bindings/java/src/net/hyperic/sigar/shell/MultiwordShellCommand.java new file mode 100644 index 00000000..bfea38d7 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/MultiwordShellCommand.java @@ -0,0 +1,124 @@ +package net.hyperic.sigar.shell; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import net.hyperic.sigar.util.PrintfFormat; + +public class MultiwordShellCommand extends ShellCommandBase { + + private Map itsSubHandlerMap = new HashMap(); + + public ShellCommandHandler getSubHandler(String subName) { + return (ShellCommandHandler)itsSubHandlerMap.get(subName); + } + + public Set getHandlerNames() { + return this.itsSubHandlerMap.keySet(); + } + + public void registerSubHandler(String subName, + ShellCommandHandler handler) + throws ShellCommandInitException { + + if (!itsSubHandlerMap.containsValue(handler)) { + // Only init the handler if it has not been added yet. + // We do this because a single handler could be + // registered for multiple subName's (as in the case + // of aliasing). + handler.init(getCommandName() + " " + subName, getShell()); + } + + itsSubHandlerMap.put(subName, handler); + } + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + String cmdName = getCommandName(); + ShellCommandHandler handler; + String[] subArgs; + + if (args.length < 1) { + throw new ShellCommandUsageException(cmdName + " command " + + "requires an argument."); + } + + handler = (ShellCommandHandler) + itsSubHandlerMap.get(args[0].toLowerCase()); + + if (handler == null) { + throw new ShellCommandUsageException("don't know how to " + + cmdName + " " + args[0]); + } + + subArgs = new String[args.length - 1]; + System.arraycopy(args, 1, subArgs, 0, subArgs.length); + handler.processCommand(subArgs); + } + + public String getSyntaxArgs() { + StringBuffer res = new StringBuffer(); + + res.append("<"); + for (Iterator i=this.getHandlerNames().iterator(); i.hasNext();) { + res.append((String)i.next()); + + if (i.hasNext()) { + res.append(" | "); + } + } + res.append(">"); + return res.toString(); + } + + public String getUsageHelp(String[] args) { + ShellCommandHandler handler; + String[] subArgs; + + if (args.length == 0) { + StringBuffer res = new StringBuffer(); + Object[] fArgs = new Object[2]; + PrintfFormat fmt; + String fmtStr; + int maxLen; + + res.append(" " + this.getUsageShort()); + res.append(".\n For further help on each subcommand, "); + res.append("type 'help "); + res.append(this.getCommandName() + " '\n\n"); + + maxLen = 0; + for (Iterator i=this.getHandlerNames().iterator(); i.hasNext();) { + String cmdName = (String)i.next(); + + if (cmdName.length() > maxLen) + maxLen = cmdName.length(); + } + + fmtStr = " %-" + (maxLen + 1) + "s %s"; + fmt = new PrintfFormat(fmtStr); + for (Iterator i=this.getHandlerNames().iterator(); i.hasNext();) { + String cmdName = (String)i.next(); + ShellCommandHandler sub = this.getSubHandler(cmdName); + + fArgs[0] = cmdName + ":"; + fArgs[1] = sub.getUsageShort(); + + res.append(fmt.sprintf(fArgs)); + if (i.hasNext()) + res.append("\n"); + } + return res.toString(); + } + + if ((handler = getSubHandler(args[0].toLowerCase())) == null) { + return null; + } + subArgs = new String[args.length - 1]; + System.arraycopy(args, 1, subArgs, 0, subArgs.length); + return handler.getUsageHelp(subArgs); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/NormalQuitCommandException.java b/bindings/java/src/net/hyperic/sigar/shell/NormalQuitCommandException.java new file mode 100644 index 00000000..49827ee2 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/NormalQuitCommandException.java @@ -0,0 +1,9 @@ +package net.hyperic.sigar.shell; + +/** + * This exception is thrown when a command wants to exit the + * shell completely. Typically this is only done for quit commands. + */ +public class NormalQuitCommandException extends RuntimeException { + +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/SIGINT.java b/bindings/java/src/net/hyperic/sigar/shell/SIGINT.java new file mode 100644 index 00000000..9d3d5eed --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/SIGINT.java @@ -0,0 +1,6 @@ +package net.hyperic.sigar.shell; + +public interface SIGINT { + + public void handleSIGINT(); +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellBase.java b/bindings/java/src/net/hyperic/sigar/shell/ShellBase.java new file mode 100644 index 00000000..cb6d06f5 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellBase.java @@ -0,0 +1,716 @@ +package net.hyperic.sigar.shell; + +import java.io.BufferedReader; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.util.Getline; +import net.hyperic.sigar.util.GetlineCompleter; +import net.hyperic.sigar.util.IteratorIterator; + +import net.hyperic.sigar.pager.PageControl; +import net.hyperic.sigar.pager.PageFetchException; +import net.hyperic.sigar.pager.PageFetcher; +import net.hyperic.sigar.pager.PageList; + +public abstract class ShellBase + implements ShellCommandMapper, GetlineCompleter, SIGINT { + // Default size for pages when doing list commands + public static final String PROP_PAGE_SIZE = "page.size"; + private static final int DEFAULT_PAGE_SIZE = 20; + + private String name = null; + private String prompt = null; + private Map handlers = null; + private HashMap hiddenCommands; + protected Getline gl; + protected PrintStream out = System.out; + protected PrintStream err = System.err; + private boolean doHistoryAdd; + private int pageSize; + private boolean isRedirected; + private GetlineCompleter completer; + + public void handleSIGINT() { + this.gl.reset(); + } + + public void initHistory() throws IOException { + + String historyFileName = + "." + this.name + "_history"; + + initHistory(new File(System.getProperty("user.home"), + historyFileName)); + } + + public void initHistory(File file) throws IOException { + + this.doHistoryAdd = true; + this.gl.initHistoryFile(file); + } + + public void registerSigIntHandler() { + ShellIntHandler.register(this); //catch ctrl-c + } + + public void init (String applicationName, + PrintStream out, + PrintStream err) { + this.name = applicationName; + this.prompt = applicationName; + this.gl = new Getline(); + this.out = out; + this.err = err; + this.doHistoryAdd = false; + this.pageSize = Integer.getInteger(PROP_PAGE_SIZE, + DEFAULT_PAGE_SIZE).intValue(); + if (this.pageSize != -1) { + this.pageSize -= 1; + if (this.pageSize < 1) { + this.pageSize = 1; + } + } + + this.isRedirected = false; + + // Create command handler registry + this.handlers = new HashMap(); + hiddenCommands = new HashMap(); + + // Register help and quit commands + try { + ShellCommand_quit quitCommand = new ShellCommand_quit(); + ShellCommand_source sourceCommand = new ShellCommand_source(); + + registerCommandHandler(".", sourceCommand); + registerCommandHandler("alias", new ShellCommand_alias()); + registerCommandHandler("exit", quitCommand); + registerCommandHandler("get", new ShellCommand_get()); + registerCommandHandler("help", new ShellCommand_help()); + registerCommandHandler("q", quitCommand); + registerCommandHandler("quit", quitCommand); + registerCommandHandler("set", new ShellCommand_set()); + registerCommandHandler("source", sourceCommand); + registerCommandHandler("sleep", new ShellCommand_sleep()); + } catch (Exception e) { + err.println("ERROR: could not register standard commands: " + e); + e.printStackTrace(err); + } + + //DWIM commands + setHandlerHidden(".", true); + setHandlerHidden("q", true); + setHandlerHidden("exit", true); + + registerSigIntHandler(); + + this.completer = new CollectionCompleter(this) { + public Iterator getIterator() { + IteratorIterator it = new IteratorIterator(); + it.add(getCommandNameIterator()); + it.add(ShellCommand_alias.getAliases()); + return it; + } + }; + } + + /** + * Read a .rc file into the shell, invoking everything in it (without + * saving the actions to history) + * + * @param rcFile File to read + */ + + public void readRCFile(File rcFile, boolean echoCommands) + throws IOException + { + FileInputStream is = null; + boolean oldHistAdd = this.doHistoryAdd; + + this.doHistoryAdd = false; + try { + BufferedReader in; + String line = null; + + is = new FileInputStream(rcFile); + in = new BufferedReader(new InputStreamReader(is)); + while ((line = in.readLine()) != null) { + line = line.trim(); + if (line.startsWith("#") || (line.length() == 0)) { + continue; + } + + if (echoCommands) { + this.err.println(line); + } + + handleCommand(line); + } + } finally { + if (is != null) { + is.close(); + } + this.doHistoryAdd = oldHistAdd; + } + } + + /** + * Change the prompt + * @param prompt + */ + public void setPrompt(String prompt) { + this.prompt = prompt; + } + + /** + * Register a new command handler. + * @param commandName The command that this handler will process. + * @param handler The handler to register. + */ + public void registerCommandHandler(String commandName, + ShellCommandHandler handler) + throws ShellCommandInitException { + this.handlers.put(commandName, handler); + handler.init(commandName, this); + } + + /** + * If a command needs additional input via the console, they + * can get it this way. + * @param prompt The prompt to display. + * @return The data that the user typed in. + */ + public String getInput(String prompt) throws EOFException, IOException { + return this.gl.getLine(prompt); + } + + /** + * If a command needs additional input via the console, they + * can get it this way. + * @param prompt The prompt to display. + * @param addToHistory If true, the input entered will be added to the + * history file. + * @return The data that the user typed in. + */ + public String getInput(String prompt, boolean addToHistory) + throws EOFException, IOException { + + return this.gl.getLine(prompt, addToHistory); + } + + /** + * If a command needs additional input via the console, they + * can get it this way. The characters that the user types + * are not echoed. + * @param prompt The prompt to display. + * @return The data that the user typed in. + */ + public String getHiddenInput(String prompt) + throws EOFException, IOException + { + return Sigar.getPassword(prompt); + } + + /** + * Write a string to this shell's output stream. + * @param s The string to write to the output stream. + */ + public void sendToOutStream(String s) { + out.println(s); + } + + /** + * Write a string to this shell's output stream. + * @param s The string to write to the output stream. + */ + public void sendToErrStream(String s) { + err.println(s); + } + + public void run() { + String input = null; + + ShellIntHandler.push(this); + + while (true) { + try { + // We don't add it to the history until we know + // that it is not an illegal command + input = this.gl.getLine(this.prompt + "> ", false); + } catch (EOFException e) { + break; + } catch (Exception e) { + err.println("Fatal error reading input line: " + e); + e.printStackTrace(err); + return; + } + if (input == null || input.trim().length() == 0) { + continue; + } + + try { + handleCommand(input); + } catch (NormalQuitCommandException nqce) { + break; + } + } + out.println("Goodbye."); + } + + public void handleCommand(String line) { + String[] args; + + try { + args = explodeQuoted(line); + } catch(IllegalArgumentException exc) { + this.out.println("Syntax error: Unbalanced quotes"); + return; + } + + if (args.length != 0) { + handleCommand(line, args); + } + } + + public void handleCommand(String line, String[] args) { + ShellCommandHandler handler = null; + PrintStream oldSysOut = null, oldOut = null; + String command = args[0]; + String[] subArgs; + int useArgs; + + if (args.length == 0) { + return; + } + + handler = getHandler(command); + if (handler == null) { + String[] aliasArgs = ShellCommand_alias.getAlias(command); + if (aliasArgs == null) { + err.println("unknown command: " + command); + return; + } + + handleCommand(line, aliasArgs); + return; + } + + useArgs = args.length; + if (args.length > 2 && args[args.length - 2].equals(">")) { + PrintStream newOut; + + oldSysOut = System.out; + oldOut = this.out; + + // Re-direction, baby + try { + FileOutputStream fOut; + + fOut = new FileOutputStream(args[args.length -1]); + newOut = new PrintStream(fOut); + } catch(IOException exc) { + this.err.println("Failed to redirect to output file: " + exc); + return; + } + this.isRedirected = true; + this.out = newOut; + System.setOut(newOut); + useArgs = useArgs - 2; + } + + subArgs = new String[useArgs - 1]; + System.arraycopy(args, 1, subArgs, 0, subArgs.length); + + try { + processCommand(handler, subArgs); + } catch (ShellCommandUsageException e) { + String msg = e.getMessage(); + if (msg == null || msg.trim().length() == 0) { + msg = "an unknown error occurred"; + } + err.println(command + ": " + msg); + + } catch (ShellCommandExecException e) { + err.println(e.getMessage()); + } catch (NormalQuitCommandException e) { + throw e; + } catch (Exception e) { + err.println("Unexpected exception processing " + + "command '" + command + "': " + e); + e.printStackTrace(err); + + } finally { + if (this.doHistoryAdd) { + this.gl.addToHistory(line); + } + + if (oldSysOut != null) { + this.isRedirected = false; + System.setOut(oldSysOut); + this.out = oldOut; + } + } + } + + public void processCommand(ShellCommandHandler handler, String args[]) + throws ShellCommandUsageException, ShellCommandExecException + { + handler.processCommand(args); + } + + public PrintStream getOutStream() { + return this.out; + } + + public PrintStream getErrStream() { + return this.err; + } + + public Getline getGetline() { + return this.gl; + } + + public boolean hasCompleter(ShellCommandHandler handler) { + return GetlineCompleter.class.isAssignableFrom(handler.getClass()); + } + + public String complete(ShellCommandHandler handler, String line) { + if (hasCompleter(handler)) { + return ((GetlineCompleter)handler).complete(line); + } + + return line; + } + + public String complete(String line) { + if (line == null) { + return null; + } + int ix = line.indexOf(" "); + + if (ix != -1) { + //if the command name has been completed + //hand off completion of the rest to the command handler + //if it implements GetlineHandler + String cmd = line.substring(0, ix); + String sub = line.substring(ix+1, line.length()); + ShellCommandHandler handler = getHandler(cmd); + + if (handler != null) { + String hline = complete(handler, sub); + return cmd + " " + hline; + } + + return line; + } + + line = this.completer.complete(line); + + if (getHandler(line) != null) { + return line + " "; + } + + return line; + } + + /** + * @see ShellCommandMapper#getHandler + */ + public ShellCommandHandler getHandler(String command) { + if (command == null) { + return null; + } + + return + (ShellCommandHandler)this.handlers.get(command.toLowerCase()); + } + + public void setHandlerHidden(String handlerName, boolean isHidden) { + if (getHandler(handlerName) == null) { + throw new IllegalArgumentException("Unknown handler: " + + handlerName); + } + + this.hiddenCommands.put(handlerName, + isHidden ? Boolean.TRUE : Boolean.FALSE); + } + + public boolean handlerIsHidden(String handlerName) { + return this.hiddenCommands.get(handlerName) != null; + } + + /** + * @see ShellCommandMapper#getCommandNameIterator + */ + public Iterator getCommandNameIterator() { + ArrayList keyArray = new ArrayList(); + String[] keys; + + for (Iterator i = this.handlers.keySet().iterator(); + i.hasNext();) + { + String keyName = (String)i.next(); + + if (!handlerIsHidden(keyName)) { + keyArray.add(keyName); + } + } + + keys = (String[])keyArray.toArray(new String[0]); + Arrays.sort(keys); + return Arrays.asList(keys).iterator(); + } + + public void shutdown() { + } + + /** + * Check to see if the currently running shell command is being + * redirected to a file. + * + * @return true if the shell is redirecting to a file, else false + */ + public boolean isRedirected() { + return this.isRedirected; + } + + /** + * Set the page size for data paging. + * + * @param size Number of rows to include in a page of data -- if + * 0, then unlimited rows will be used. + */ + public void setPageSize(int size) { + if (size == 0 || size < -1) { + throw new IllegalArgumentException("Page size must be > 0 or -1"); + } + this.pageSize = size; + } + + /** + * Get the current page size used when paging data. + * + * @return the # of rows in the current page size. + */ + public int getPageSize() { + return this.pageSize; + } + + /** + * Get the number of pages that the fetcher can fetch, given the + * settings as specified by the control and the # of total entites + * the fetcher can fetch + * + * @param control Control which dictates the page size + * @param list Last pageList queried via the control + */ + private int getNumPages(PageControl control, PageList list) { + int pageSize = control.getPagesize(); + int totalElems; + + totalElems = list.getTotalSize(); + + if (pageSize == PageControl.SIZE_UNLIMITED) { + return 1; + } + else if (pageSize == 0) { + return 0; + } + + if ((totalElems % pageSize) == 0) { + return totalElems / pageSize; + } + + return (totalElems / pageSize) + 1; + } + + /** + * Print a page of data + * + * @param out Stream to print to + * @param data List containing the data to print + * @param lineNo Line number of the first element of data + * @param printLineNumbers If true, prefix lines with their numbers + * + * @return the number of lines printed + */ + private void printPage(PrintStream out, PageList data, int lineNo, + boolean printLineNumbers) + { + for (Iterator i=data.iterator(); i.hasNext(); ) { + if (printLineNumbers) { + out.print(lineNo++ + ": "); + } + + out.println((String)i.next()); + } + } + + public PageControl getDefaultPageControl() { + PageControl res; + + res = new PageControl(0, getPageSize() == -1 ? + PageControl.SIZE_UNLIMITED : + getPageSize()); + return res; + } + + public void performPaging(PageFetcher fetcher) + throws PageFetchException + { + performPaging(fetcher, getDefaultPageControl()); + } + + public void performPaging(PageFetcher fetcher, PageControl control) + throws PageFetchException + { + PrintStream out; + PageControl curPage; + PageList data; + boolean lineNumberMode; + + // Don't know how to handle this case + if (control.getPagesize() == 0) { + return; + } + + lineNumberMode = false; + out = getOutStream(); + + if (isRedirected()) { + control.setPagesize(PageControl.SIZE_UNLIMITED); + } + + data = fetcher.getPage((PageControl)control.clone()); + printPage(out, data, control.getPageEntityIndex() + 1, + lineNumberMode); + + if (control.getPagesize() == PageControl.SIZE_UNLIMITED || + data.size() < control.getPagesize()) + { + return; + } + + while (true) { + boolean printPage = false; + String cmd; + int totalPages; + + totalPages = getNumPages(control, data); + + try { + cmd = getInput("--More-- (Page " + + (control.getPagenum() + 1) + " of " + + totalPages + ")", false); + } catch(IOException exc) { + out.println(); + break; + } + + if (cmd == null || (cmd = cmd.trim()).length() == 0) { + printPage = true; + control.setPagenum(control.getPagenum() + 1); + } + else if (cmd.equals("q")) { + break; + } + else if (cmd.equals("b")) { + printPage = true; + if (control.getPagenum() > 0) { + control.setPagenum(control.getPagenum() - 1); + } + } + else if (cmd.equals("l")) { + lineNumberMode = !lineNumberMode; + printPage = true; + } + else if (cmd.equals("?")) { + out.println(" 'b' - Scroll back one page"); + out.println(" 'l' - Toggle line number mode"); + out.println(" 'q' - Quit paging"); + out.println(" '' - Jump to the specified page #"); + out.println(" '' - Scroll forward one page"); + } + else { + int newPageNo; + + try { + newPageNo = Integer.parseInt(cmd); + } catch(NumberFormatException exc) { + out.println("Unknown command '" + cmd + "' " + + " type '?' for paging help"); + continue; + } + + if (newPageNo < 1 || newPageNo > totalPages) { + out.println(newPageNo + " out of range (must be " + + "1 to " + totalPages + ")"); + } + else { + control.setPagenum(newPageNo - 1); + printPage = true; + } + } + + if (printPage) { + data = fetcher.getPage((PageControl)control.clone()); + printPage(out, data, control.getPageEntityIndex() + 1, + lineNumberMode); + + // Check to see if we printed the last of the data + if (data.size() < control.getPagesize()) { + break; + } + } + } + } + + private static String[] explodeQuoted(String arg) { + ArrayList res = new ArrayList(); + StringTokenizer quoteTok; + boolean inQuote = false; + + arg = arg.trim(); + quoteTok = new StringTokenizer(arg, "\"", true); + + while (quoteTok.hasMoreTokens()) { + String elem = (String)quoteTok.nextElement(); + + if (elem.equals("\"")) { + inQuote = !inQuote; + continue; + } + + if (inQuote) { + res.add(elem); + } + else { + StringTokenizer spaceTok = new StringTokenizer(elem.trim()); + + while (spaceTok.hasMoreTokens()) { + res.add(spaceTok.nextToken()); + } + } + } + + if (inQuote) { + throw new IllegalArgumentException("Unbalanced quotation marks"); + } + + return (String[]) res.toArray(new String[0]); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommandBase.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandBase.java new file mode 100644 index 00000000..5a1b8966 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandBase.java @@ -0,0 +1,54 @@ +package net.hyperic.sigar.shell; + +import java.io.PrintStream; + +public class ShellCommandBase implements ShellCommandHandler { + + protected String itsCommandName = null; + protected ShellBase itsShell = null; + + private PrintStream out = null; + public String getCommandName() { return itsCommandName; } + public ShellBase getShell() { return itsShell; } + + public PrintStream getOutStream() { + return this.getShell().getOutStream(); + } + + public PrintStream getErrStream() { + return this.getShell().getErrStream(); + } + + public void init(String commandName, ShellBase shell) + throws ShellCommandInitException { + itsCommandName = commandName; + itsShell = shell; + } + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException { + + out.println("ShellCommandBase: not implemented: " + itsCommandName); + /* + if (args != null && args.trim().length() > 0) { + out.println("args were: " + args); + } + */ + } + + public String getSyntax() { + return "Syntax: " + this.getCommandName() + " " + this.getSyntaxArgs(); + } + + public String getSyntaxArgs() { + return ""; + } + + public String getUsageShort() { + return ""; + } + + public String getUsageHelp(String[] args) { + return "Help not available for command " + itsCommandName; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommandExecException.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandExecException.java new file mode 100644 index 00000000..56f3001b --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandExecException.java @@ -0,0 +1,10 @@ +package net.hyperic.sigar.shell; + +public class ShellCommandExecException extends Exception { + + public ShellCommandExecException() {} + + public ShellCommandExecException(String s) { + super(s); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommandHandler.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandHandler.java new file mode 100644 index 00000000..0726ee84 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandHandler.java @@ -0,0 +1,44 @@ +package net.hyperic.sigar.shell; + +public interface ShellCommandHandler { + + /** + * Initialize this command handler. + * @param commandName The name of the command. + * @param shell The shell. This is useful for command + * that need to be able to interpret other commands, like the "help" + * command, and for commands that need to get additional user input, + * for example a login command that presents a password prompt. + */ + public void init(String commandName, ShellBase shell) + throws ShellCommandInitException; + + /** + * Handle a command. + * @param args The args to the command. + * @exception ShellCommandUsageException If the args are malformed. + * @exception ShellCommandExecException If an error occurred + * executing the command. + */ + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException; + + /** + * Get some info on how to invoke this command. + * @return Some usage information on how this command is + * expected to be invoked. + */ + public String getUsageHelp(String[] args); + + /** + * Get a very brief (40 character) description of the command + * @return A description of the command. + */ + public String getUsageShort(); + + /** + * Get a description of the syntax for how a command should be invoked. + * @return A description of the syntax + */ + public String getSyntax(); +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommandInitException.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandInitException.java new file mode 100644 index 00000000..69621720 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandInitException.java @@ -0,0 +1,10 @@ +package net.hyperic.sigar.shell; + +public class ShellCommandInitException extends Exception { + + public ShellCommandInitException() {} + + public ShellCommandInitException(String s) { + super(s); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommandMapper.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandMapper.java new file mode 100644 index 00000000..fcb28fbd --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandMapper.java @@ -0,0 +1,16 @@ +package net.hyperic.sigar.shell; + +import java.util.Iterator; + +public interface ShellCommandMapper { + + /** + * Get the command handler for a command. + */ + public ShellCommandHandler getHandler(String command); + + /** + * Get an iterator for the command names. + */ + public Iterator getCommandNameIterator(); +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommandUsageException.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandUsageException.java new file mode 100644 index 00000000..8599b1ec --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommandUsageException.java @@ -0,0 +1,10 @@ +package net.hyperic.sigar.shell; + +public class ShellCommandUsageException extends Exception { + + public ShellCommandUsageException() {} + + public ShellCommandUsageException(String s) { + super(s); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_alias.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_alias.java new file mode 100644 index 00000000..a2920c40 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_alias.java @@ -0,0 +1,62 @@ +package net.hyperic.sigar.shell; + +import java.util.HashMap; +import java.util.Iterator; + +public class ShellCommand_alias extends ShellCommandBase { + + private static HashMap aliases = new HashMap(); + + public static String[] getAlias(String alias) { + return (String[])aliases.get(alias); + } + + public static Iterator getAliases() { + return aliases.keySet().iterator(); + } + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + if (args.length < 2) { + throw new ShellCommandUsageException(this.getSyntax()); + } + + int aliasArgsLen = args.length - 1; + String[] aliasArgs = new String[ aliasArgsLen ]; + System.arraycopy(args, 1, aliasArgs, 0, aliasArgsLen); + + this.aliases.put(args[0], aliasArgs); + } + + public String getSyntaxArgs() { + return " "; + } + + public String getUsageShort() { + return "Create alias command"; + } + + public String getUsageHelp(String[] args) { + if (aliases.size() == 0) { + return "No aliases defined"; + } + + StringBuffer sb = new StringBuffer(); + sb.append("Defined aliases:\n"); + + for (Iterator it=aliases.keySet().iterator(); + it.hasNext(); ) + { + String key = (String)it.next(); + String[] cmd = getAlias(key); + sb.append(key).append(" => "); + for (int i=0; i [key2] ..."; + } + + public String getUsageShort(){ + return "Get system properties"; + } + + public String getUsageHelp(String[] args) { + return " " + getUsageShort() + "."; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_help.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_help.java new file mode 100644 index 00000000..804132e1 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_help.java @@ -0,0 +1,116 @@ +package net.hyperic.sigar.shell; + +import java.io.PrintStream; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import net.hyperic.sigar.util.PrintfFormat; + +public class ShellCommand_help extends ShellCommandBase { + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + ShellCommandHandler handler; + PrintStream out = this.getOutStream(); + int useArgs; + + if (args.length == 0) { + PrintfFormat fmt = new PrintfFormat("\t%-14s - %s"); + Object[] fArgs = new Object[2]; + ArrayList cmdNamesList = new ArrayList(); + String[] cmdNames; + Iterator i; + + i = itsShell.getCommandNameIterator(); + while (i.hasNext()) { + cmdNamesList.add(i.next()); + } + + cmdNames = (String[])cmdNamesList.toArray(new String[0]); + Arrays.sort(cmdNames); + + + out.println("Available commands:"); + + for (int j=0; j [command arguments]"; + } + + public String getUsageShort() { + return "Gives help on shell commands"; + } + + public String getUsageHelp(String[] args) { + return + " Displays help about the given command name. If the \n" + + " command has arguments they may be entered for more " + + "specific\n help"; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_quit.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_quit.java new file mode 100644 index 00000000..a74079c3 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_quit.java @@ -0,0 +1,17 @@ +package net.hyperic.sigar.shell; + +public class ShellCommand_quit extends ShellCommandBase { + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException { + throw new NormalQuitCommandException(); + } + + public String getUsageShort() { + return "Terminate the shell"; + } + + public String getUsageHelp(String[] args) { + return " Terminate the shell."; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_set.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_set.java new file mode 100644 index 00000000..8f82e630 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_set.java @@ -0,0 +1,79 @@ +package net.hyperic.sigar.shell; + +import java.util.HashMap; +import java.util.Iterator; + +public class ShellCommand_set extends ShellCommandBase { + private HashMap keyDescriptions = new HashMap(); + + public ShellCommand_set() { + this.keyDescriptions = new HashMap(); + this.keyDescriptions.put(ShellBase.PROP_PAGE_SIZE, + "The maximum size of a shell page"); + } + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + if (args.length < 1 || args.length > 2) { + throw new ShellCommandUsageException(this.getSyntax()); + } + + if (args.length == 1) { + System.getProperties().remove(args[0]); + } + else { + if (args[0].equalsIgnoreCase(ShellBase.PROP_PAGE_SIZE)) { + int newSize; + + try { + newSize = Integer.parseInt(args[1]); + if (newSize == 0 || newSize < -1) { + throw new NumberFormatException(); + } + } catch(NumberFormatException exc) { + throw new ShellCommandUsageException(args[0] + " must be "+ + "an integer > 0 or " + + "-1"); + } + this.getShell().setPageSize(newSize); + } + + System.setProperty(args[0], args[1]); + } + } + + public void addSetKey(String key, String description) { + this.keyDescriptions.put(key, description); + } + + public String getSyntaxArgs() { + return " [value]"; + } + + public String getUsageShort() { + return "Set system properties"; + } + + public String getUsageHelp(String[] args) { + String res = + " " + this.getUsageShort() + + ". If no value is provided, " + + "the key will be\n deleted."; + + if (this.keyDescriptions.size() != 0) { + res += "\n\n Common keys include:"; + } + + for (Iterator i=this.keyDescriptions.keySet().iterator(); + i.hasNext();) + { + String key = (String)i.next(); + String value = (String)this.keyDescriptions.get(key); + + res += "\n " + key + ": " + value; + } + + return res; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_sleep.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_sleep.java new file mode 100644 index 00000000..d6a59baa --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_sleep.java @@ -0,0 +1,35 @@ +package net.hyperic.sigar.shell; + +public class ShellCommand_sleep extends ShellCommandBase { + + public ShellCommand_sleep() {} + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + if (args.length != 1) { + throw new ShellCommandUsageException(getSyntax()); + } + + try { + Thread.sleep(Integer.parseInt(args[0]) * 1000); + } catch(NumberFormatException exc) { + throw new ShellCommandExecException("Invalid time '" + args[0] + + "' -- must be an integer"); + } catch(InterruptedException exc) { + throw new ShellCommandExecException("Sleep interrupted"); + } + } + + public String getSyntaxArgs() { + return ""; + } + + public String getUsageShort() { + return "Delay execution for the a number of seconds "; + } + + public String getUsageHelp(String[] args) { + return " " + getUsageShort() + "."; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_source.java b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_source.java new file mode 100644 index 00000000..847490bb --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellCommand_source.java @@ -0,0 +1,55 @@ +package net.hyperic.sigar.shell; + +import java.io.File; +import java.io.IOException; + +import net.hyperic.sigar.util.GetlineCompleter; + +public class ShellCommand_source + extends ShellCommandBase + implements GetlineCompleter { + + public String complete(String line) { + return new FileCompleter(getShell()).complete(line); + } + + public void processCommand(String[] args) + throws ShellCommandUsageException, ShellCommandExecException + { + File rcFile; + + if(args.length != 1){ + throw new ShellCommandUsageException("Syntax: " + + this.getCommandName() + + " "); + } + + rcFile = new File(FileCompleter.expand(args[0])); + + if(rcFile.isFile() == false){ + throw new ShellCommandExecException("File '" + rcFile + + "' not found"); + } + + try { + this.getShell().readRCFile(rcFile, true); + } catch(IOException exc){ + throw new ShellCommandExecException("Error reading file '" + + rcFile + ": " + + exc.getMessage()); + } + } + + public String getSyntaxArgs(){ + return ""; + } + + public String getUsageShort(){ + return "Read a file, executing the contents"; + } + + public String getUsageHelp(String[] args) { + return " " + this.getUsageShort() + ". The file must contain " + + "commands\n which are executable by the shell."; + } +} diff --git a/bindings/java/src/net/hyperic/sigar/shell/ShellIntHandler.java b/bindings/java/src/net/hyperic/sigar/shell/ShellIntHandler.java new file mode 100644 index 00000000..836c9ae6 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/shell/ShellIntHandler.java @@ -0,0 +1,44 @@ +package net.hyperic.sigar.shell; + +import java.util.Stack; + +import sun.misc.Signal; +import sun.misc.SignalHandler; + +public class ShellIntHandler implements SignalHandler { + + private static ShellBase handlerShell; + private static Stack handlers; + + public static void register(ShellBase shell) { + handlerShell = shell; + handlers = new Stack(); + + Signal signal = new Signal("INT"); + + try { + Signal.handle(signal, new ShellIntHandler()); + } catch(Exception e) { + //java -Xrs for example will throw IllegalArgumentException + } + } + + public void handle(Signal signal) { + if (handlers.empty()) { + handlerShell.shutdown(); + Runtime.getRuntime().halt(0); + } + else { + SIGINT handler = (SIGINT)handlers.peek(); + handler.handleSIGINT(); + } + } + + public static void push(SIGINT handler) { + handlers.push(handler); + } + + public static void pop() { + handlers.pop(); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/GetPass.java b/bindings/java/src/net/hyperic/sigar/test/GetPass.java new file mode 100644 index 00000000..4fb6457d --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/GetPass.java @@ -0,0 +1,17 @@ +package net.hyperic.sigar.test; + +import java.io.IOException; + +import net.hyperic.sigar.Sigar; + +public class GetPass { + public static void main(String[] args) throws Exception { + + try { + String password = Sigar.getPassword("Enter password: "); + System.out.println("You entered: ->" + password + "<-"); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/Proxy.java b/bindings/java/src/net/hyperic/sigar/test/Proxy.java new file mode 100644 index 00000000..0512a726 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/Proxy.java @@ -0,0 +1,477 @@ +package net.hyperic.sigar.test; + +import java.io.File; +import java.io.FileFilter; +import java.io.PrintStream; + +import net.hyperic.sigar.FileSystem; +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarInvoker; +import net.hyperic.sigar.SigarProxy; +import net.hyperic.sigar.SigarProxyCache; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class Proxy { + + private static final String HOME = System.getProperty("user.home"); + private static boolean sameArg = true; //set false if using leaktest to also stress test + private boolean pause = false; + private boolean verbose = true; + private boolean leakVerbose = false; + private boolean fukksor = false; + //compare SigarProxyCache to straightup reflection + private boolean useReflection = false; + private PrintStream out = System.out; + + private String ourPid; + private Sigar sigar; + private SigarProxy proxy; + private long lastChange = 0, startSize = 0, currentSize = 0; + private PidList pids; + private NetifList netif; + private FsList fs; + private DirList dirs; + private FileList files; + + public Proxy(Sigar sigar, SigarProxy proxy) { + this.sigar = sigar; + this.proxy = proxy; + this.pids = new PidList(sigar); + this.netif = new NetifList(sigar); + this.fs = new FsList(sigar); + this.dirs = new DirList(HOME); + this.files = new FileList(HOME); + } + + public void setOutputStream(PrintStream out) { + this.out = out; + } + + public void setVerbose(boolean value) { + this.verbose = value; + } + + public void setLeakVerbose(boolean value) { + this.leakVerbose = value; + } + + private void output() { + this.out.println(); + } + + private void output(String s) { + String name = Thread.currentThread().getName(); + this.out.println("[" + name + "] " + s); + } + + private long getSize() throws SigarException { + return sigar.getProcMem(ourPid).getVsize(); + } + + private boolean memstat(long i) throws SigarException { + long size = getSize(); + String changed = ""; + if (currentSize != size) { + long diff = size - currentSize; + long iters = i - lastChange; + changed = " (change=" + diff + ", iters=" + iters + ")"; + output(i + ") size=" + size + changed); + currentSize = size; + lastChange = i; + return true; + } + + return false; + } + + private void trace(String msg) { + if (verbose) { + output(msg); + } + } + + private boolean isNonStringArg(Method method) { + Class[] paramTypes = method.getParameterTypes(); + if ((paramTypes.length >= 1) && + (paramTypes[0] != String.class)) { + return true; + } + return false; + } + + private String argsToString(Object[] args) { + if ((args == null) || (args.length == 0)) { + return ""; + } + + StringBuffer sb = new StringBuffer(); + + sb.append('(').append(args[0].toString()); + + for (int i=1; i 0) { + if (type.startsWith("Proc")) { + arg = this.pids.getName(iter); + + switch (parms.length) { + case 1: + objArgs = new Object[] { arg }; + break; + case 2: + //XXX assume ProcEnv for now. + objArgs = new Object[] { arg, "SHELL" }; + break; + } + } + else { + trace("SKIPPING: " + type); + continue; + } + } + + Object obj; + if (useReflection) { + obj = method.invoke((Object)sigar, objArgs); + } + else { + obj = invoke(new SigarInvoker(proxy, type), objArgs, null); + } + + if (iter > 0) { + if (memstat(iter)) { + this.out.print(type); + if (arg != null) { + this.out.print(" " + arg); + } + output(); + } + } + + String value; + if (obj instanceof Object[]) { + value = argsToString((Object[])obj); + } + else { + value = String.valueOf(obj); + } + trace(type + argsToString(objArgs) + "=" + value); + continue; + } + + Method[] attrs = attrClass.getMethods(); + + for (int j=0; j 0) { + if (type.startsWith("Proc")) { + arg = this.pids.getName(iter); + } + else if (type.startsWith("NetIf")) { + arg = this.netif.getName(iter); + } + else if (type.equals("FileSystemUsage") || + type.equals("MountedFileSystemUsage")) + { + arg = this.fs.getName(iter); + } + else if (type.equals("FileInfo") || + type.equals("LinkInfo")) + { + arg = this.files.getName(iter); + } + else if (type.equals("DirStats")) { + arg = this.dirs.getName(iter); + } + else { + trace("SKIPPING: " + type); + continue; + } + + objArgs = new Object[] { arg }; + } + + if (isNonStringArg(method)) { + continue; + } + + Object obj; + if (useReflection) { + Object typeObject = method.invoke((Object)sigar, objArgs); + obj = getter.invoke(typeObject, new Object[0]); + } + else { + obj = invoke(new SigarInvoker(proxy, type), objArgs, attrName); + } + + if (iter > 0) { + if (memstat(iter)) { + this.out.print(type); + if (arg != null) { + this.out.print(" " + arg); + } + output(); + } + } + + trace(type + argsToString(objArgs) + + "." + attrName + "=" + obj); + + if (pause) { + //test cache expire + pause(); + } + } + } + } + + private void pause() { + output("hit enter to continue"); + try { + System.in.read(); + } catch (Exception e) {} + } + + private Object invoke(SigarInvoker invoker, + Object[] args, String attr) { + + String type = invoker.getType(); + + if (fukksor) { + //make args bogus to test exception handling/messages + if (args.length != 0) { + if (args[0] instanceof String) { + if (type.startsWith("Proc")) { + args[0] = new String("666666"); + } + else { + args[0] = new String("bogus"); + } + } + } + } + + if (args.length == 0) { + args = null; + } + + try { + return invoker.invoke(args, attr); + } catch (SigarException e) { + String msg = + type + " failed: " + e.getMessage(); + return null; + } + } + + public static void main(String[] args) throws Exception { + int expire = 30 * 1000; + + Sigar sigar = new Sigar(); + + SigarProxy proxy = SigarProxyCache.newInstance(sigar, expire); + + new Proxy(sigar, proxy).run(args); + } + + public void run(String[] args) throws SigarException { + ourPid = String.valueOf(sigar.getPid()); + + output("ourPid=" + ourPid); + + if (args.length >= 2) { + String type = args[0], arg = null, attr = args[args.length - 1]; + + if (args.length == 3) { + arg = args[1]; + } + + if (type.equals("leaktest")) { + int num = Integer.parseInt(args[1]); + verbose = leakVerbose; + startSize = currentSize = getSize(); + long startTime = System.currentTimeMillis(); + + for (int i=0; i 0); + } + + public void assertGtZeroTrace(String msg, long value) { + traceln(msg + "=" + value); + assertTrue(msg, value > 0); + } + + public void assertGtEqZeroTrace(String msg, long value) { + traceln(msg + "=" + value); + assertTrue(msg, value >= 0); + } + + public void assertEqualsTrace(String msg, long expected, long actual) { + traceln(msg + "=" + actual + "/" + expected); + assertEquals(msg, expected, actual); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/SigarTestPrinter.java b/bindings/java/src/net/hyperic/sigar/test/SigarTestPrinter.java new file mode 100644 index 00000000..1a759cd5 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/SigarTestPrinter.java @@ -0,0 +1,115 @@ +package net.hyperic.sigar.test; + +import java.io.PrintStream; +import java.util.HashMap; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestFailure; +import junit.framework.TestSuite; + +import junit.textui.ResultPrinter; +import junit.textui.TestRunner; + +import net.hyperic.sigar.cmd.Version; + +public class SigarTestPrinter extends ResultPrinter { + + private static String PACKAGE_NAME = + SigarTestCase.class.getPackage().getName(); + + private HashMap failures = new HashMap(); + private int maxNameLen = 0; + + public SigarTestPrinter(PrintStream writer) { + super(writer); + } + + //output similar to perl TestHarness + //more interesting useful than the default '.', 'F', 'E' + //for each test success, failure or error. + public void startTest(Test test) { + PrintStream writer = getWriter(); + String name = test.getClass().getName(); + writer.print(name); + int n = ((maxNameLen+3) - name.length()); + for (int i=0; i printer.maxNameLen) { + printer.maxNameLen = len; + } + + suite.addTestSuite(test); + } + + public static void runTests(Class[] tests, String[] args) { + TestSuite suite = new TestSuite("Sigar tests"); + + SigarTestPrinter printer = new SigarTestPrinter(System.out); + + printer.printVersionInfo(); + + if (args.length > 0) { + SigarTestCase.setVerbose(true); + SigarTestCase.setWriter(printer.getWriter()); + + for (int i=0; i 0; + if (!validFQDN) { + //wont get a valid fqdn on laptop at home + //allow to fake with ant -Dsigar.fqdn=foo.bar + String pfake = props.getProperty("sigar.fqdn"); + String fake = + System.getProperty("sigar.fqdn", pfake); + if ("".equals(fake)) { + fake = pfake; + } + if (fake != null) { + traceln("fake='" + fake + "'"); + validFQDN = fake.indexOf(".") > 0; + } + } + assertTrue(validFQDN); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/TestFileInfo.java b/bindings/java/src/net/hyperic/sigar/test/TestFileInfo.java new file mode 100644 index 00000000..8c596dec --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/TestFileInfo.java @@ -0,0 +1,149 @@ +package net.hyperic.sigar.test; + +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.util.Date; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarNotImplementedException; +import net.hyperic.sigar.DirStat; +import net.hyperic.sigar.FileInfo; + +public class TestFileInfo extends SigarTestCase { + + public TestFileInfo(String name) { + super(name); + } + + private void getFileInfo(Sigar sigar, String file) + throws SigarException { + + traceln("Entry=" + file); + + FileInfo info = sigar.getFileInfo(file); + + assertGtEqZeroTrace("Permisions", + info.getPermissions()); + + assertTrueTrace("Permissions", + info.getPermissionsString()); + + assertGtEqZeroTrace("Mode", info.getMode()); + + assertTrueTrace("Type", info.getTypeString()); + + assertGtEqZeroTrace("Size", info.getSize()); + + assertGtEqZeroTrace("Uid", info.getUid()); + + assertGtEqZeroTrace("Gid", info.getUid()); + + assertGtEqZeroTrace("Inode", info.getInode()); + + traceln("Device=" + info.getDevice()); + + assertGtEqZeroTrace("Nlink", info.getNlink()); + + assertGtZeroTrace("Atime", info.getAtime()); + traceln(new Date(info.getAtime()).toString()); + + assertGtZeroTrace("Mtime", info.getMtime()); + traceln(new Date(info.getMtime()).toString()); + + assertGtZeroTrace("Ctime", info.getCtime()); + traceln(new Date(info.getCtime()).toString()); + + if (info.getType() == FileInfo.TYPE_DIR) { + try { + DirStat stats = sigar.getDirStat(file); + assertEqualsTrace("Total", + new File(file).list().length, + stats.getTotal()); + assertGtEqZeroTrace("Files", stats.getFiles()); + assertGtEqZeroTrace("Subdirs", stats.getSubdirs()); + } catch (SigarNotImplementedException e) { + //XXX win32 + } + } + else { + try { + DirStat stats = sigar.getDirStat(file); + assertTrue(false); + } catch (SigarException e) { + assertTrue(true); + } + } + + FileInfo link = sigar.getLinkInfo(file); + } + + public void testCreate() throws Exception { + Sigar sigar = new Sigar(); + + traceln(""); + + String file; + File dir = new File(System.getProperty("user.dir")); + String[] entries = dir.list(); + + for (int i=0; i= 0); + } + } + + private void testMalformed(SigarProxy proxy) throws Exception { + for (int i=0; i" + single); + } + + key = "dOeSnOtExIsT"; + val = (String)env.get(key); + assertTrue(val == null); + + val = sigar.getProcEnv(pid, key); + assertTrue(val == null); + } catch (SigarNotImplementedException e) { + //ok + } + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/TestProcExe.java b/bindings/java/src/net/hyperic/sigar/test/TestProcExe.java new file mode 100644 index 00000000..03b1ba10 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/TestProcExe.java @@ -0,0 +1,42 @@ +package net.hyperic.sigar.test; + +import java.io.File; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.ProcExe; +import net.hyperic.sigar.SigarNotImplementedException; + +public class TestProcExe extends SigarTestCase { + + public TestProcExe(String name) { + super(name); + } + + public void testCreate() throws Exception { + Sigar sigar = new Sigar(); + + traceln(""); + + try { + ProcExe exe = sigar.getProcExe(sigar.getPid()); + + File exeFile = new File(exe.getName()); + traceln("exe='" + exe.getName() + "'"); + + assertTrue(exeFile.exists()); + + //win32 has .exe + assertTrue(exeFile.getName().startsWith("java")); + + String cwd = exe.getCwd(); + traceln("cwd='" + cwd + "'"); + + //XXX win32 as exe but not cwd + if (cwd.length() != 0) { + assertTrue(new File(cwd).isDirectory()); + } + } catch (SigarNotImplementedException e) { + //ok + } + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/TestProcFd.java b/bindings/java/src/net/hyperic/sigar/test/TestProcFd.java new file mode 100644 index 00000000..14ac07f8 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/TestProcFd.java @@ -0,0 +1,42 @@ +package net.hyperic.sigar.test; + +import java.io.File; +import java.io.FileInputStream; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarNotImplementedException; + +public class TestProcFd extends SigarTestCase { + + public TestProcFd(String name) { + super(name); + } + + public void testCreate() throws Exception { + Sigar sigar = new Sigar(); + + traceln(""); + + try { + long pid = sigar.getPid(); + + long total = sigar.getProcFd(pid).getTotal(); + + File file = new File("bin", "run_tests.sh"); + if (!file.exists()) { + file = new File("build.xml"); + } + FileInputStream is = new FileInputStream(file); + + assertEqualsTrace("Total", total + 1, + sigar.getProcFd(pid).getTotal()); + + is.close(); + + assertEqualsTrace("Total", total, + sigar.getProcFd(pid).getTotal()); + } catch (SigarNotImplementedException e) { + //ok + } + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/TestProcList.java b/bindings/java/src/net/hyperic/sigar/test/TestProcList.java new file mode 100644 index 00000000..c1e14287 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/TestProcList.java @@ -0,0 +1,38 @@ +package net.hyperic.sigar.test; + +import java.util.ArrayList; + +import net.hyperic.sigar.Sigar; + +public class TestProcList extends SigarTestCase { + + public TestProcList(String name) { + super(name); + } + + public void testCreate() throws Exception { + Sigar sigar = new Sigar(); + + traceln(""); + ArrayList traceList = new ArrayList(); + + long[] pids = sigar.getProcList(); + + assertTrue(pids.length > 1); + + long pid = sigar.getPid(); + boolean foundPid = false; + + //find our pid in the process list + for (int i=0; i= 0); + + assertTrue(swap.getUsed() >= 0); + + assertTrue(swap.getFree() >= 0); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/test/TestThreads.java b/bindings/java/src/net/hyperic/sigar/test/TestThreads.java new file mode 100644 index 00000000..23f87e49 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/test/TestThreads.java @@ -0,0 +1,97 @@ +package net.hyperic.sigar.test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import java.util.ArrayList; + +import junit.framework.TestCase; + +import net.hyperic.sigar.Sigar; +import net.hyperic.sigar.SigarException; +import net.hyperic.sigar.SigarProxy; +import net.hyperic.sigar.SigarProxyCache; +import net.hyperic.sigar.test.Proxy; + +//test concurrency +public class TestThreads extends TestCase { + + private static Sigar gSigar = null; + private static SigarProxy gProxy = null; + private static Object lock = new Object(); + private static boolean verbose = true; + + class ProxyThread extends Thread { + SigarException ex; + //true should make things blow up + boolean useGlobal = false; + + public void run() { + int expire = 1000; + + Sigar sigar; + SigarProxy proxy; + + try { + synchronized (lock) { + if (useGlobal) { + if (gSigar == null) { + + gSigar = new Sigar(); + + gProxy = SigarProxyCache.newInstance(gSigar, 30 * 1000); + } + + sigar = gSigar; + proxy = gProxy; + } + else { + sigar = new Sigar(); + + proxy = SigarProxyCache.newInstance(sigar, 30 * 1000); + } + } + + String args[] = {"leaktest", "50"}; + Proxy cmdProxy = new Proxy(sigar, proxy); + + PrintStream ps = new PrintStream(new ByteArrayOutputStream()); + + if (verbose) { + cmdProxy.setVerbose(true); + cmdProxy.setLeakVerbose(true); + cmdProxy.run(args); + } + else { + cmdProxy.setOutputStream(ps); + } + } catch (SigarException e) { + this.ex = e; + } + } + } + + public TestThreads(String name) { + super(name); + } + + public void testCreate() throws Exception { + ArrayList threads = new ArrayList(); + + for (int i=0; i<4; i++) { + ProxyThread pt = new ProxyThread(); + pt.useGlobal = true; + threads.add(pt); + pt.start(); + } + + for (int n=0; n 0); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/util/Getline.java b/bindings/java/src/net/hyperic/sigar/util/Getline.java new file mode 100644 index 00000000..b48295be --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/util/Getline.java @@ -0,0 +1,62 @@ +package net.hyperic.sigar.util; + +import java.io.IOException; +import java.io.EOFException; +import java.io.File; + +public class Getline { + + private String prompt = "> "; + + public Getline() { } + + public Getline(String prompt) { + this.prompt = prompt; + } + + public native static void setCompleter(GetlineCompleter completer); + + public native void redraw(); + + public native void reset(); + + private native void histadd(String line); + + private native void histinit(String file); + + private native String getline(String prompt) + throws IOException, EOFException; + + public String getLine() + throws IOException, EOFException { + + return getLine(this.prompt, true); + } + + public String getLine(String prompt) + throws IOException, EOFException { + + return getLine(prompt, true); + } + + public String getLine(String prompt, boolean addToHistory) + throws IOException, EOFException { + + //XXX provide pure-java fallback + String line = getline(prompt); + if (addToHistory) { + histadd(line); + } + return line; + } + + public void initHistoryFile(File file) + throws IOException { + + histinit(file.getCanonicalPath()); + } + + public void addToHistory(String line) { + histadd(line); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/util/GetlineCompleter.java b/bindings/java/src/net/hyperic/sigar/util/GetlineCompleter.java new file mode 100644 index 00000000..dbf37bd1 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/util/GetlineCompleter.java @@ -0,0 +1,6 @@ +package net.hyperic.sigar.util; + +public interface GetlineCompleter { + + public String complete(String line); +} diff --git a/bindings/java/src/net/hyperic/sigar/util/IteratorIterator.java b/bindings/java/src/net/hyperic/sigar/util/IteratorIterator.java new file mode 100644 index 00000000..048e8044 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/util/IteratorIterator.java @@ -0,0 +1,56 @@ +package net.hyperic.sigar.util; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Iterator for multiple Iterators. + */ +public class IteratorIterator implements Iterator { + + private int ix = 0; + private Iterator curr = null; + + private ArrayList iterators = new ArrayList(); + + public IteratorIterator() { } + + public void add(Iterator iterator) { + this.iterators.add(iterator); + } + + public boolean hasNext() { + int size = this.iterators.size(); + + //first time through + if (this.curr == null) { + if (size == 0) { + return false; + } + + this.curr = (Iterator)this.iterators.get(0); + } + + if (this.curr.hasNext()) { + return true; + } + + this.ix++; + if (this.ix >= size) { + return false; + } + + this.curr = (Iterator)this.iterators.get(this.ix); + + //recurse in the event that this.curr is empty + return hasNext(); + } + + public Object next() { + return this.curr.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/bindings/java/src/net/hyperic/sigar/util/PrintfFormat.java b/bindings/java/src/net/hyperic/sigar/util/PrintfFormat.java new file mode 100644 index 00000000..9d849ae9 --- /dev/null +++ b/bindings/java/src/net/hyperic/sigar/util/PrintfFormat.java @@ -0,0 +1,3095 @@ + +// +// (c) 2000 Sun Microsystems, Inc. +// ALL RIGHTS RESERVED +// +// License Grant- +// +// +// Permission to use, copy, modify, and distribute this Software and its +// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is +// hereby granted. +// +// This Software is provided "AS IS". All express warranties, including any +// implied warranty of merchantability, satisfactory quality, fitness for a +// particular purpose, or non-infringement, are disclaimed, except to the extent +// that such disclaimers are held to be legally invalid. +// +// You acknowledge that Software is not designed, licensed or intended for use in +// the design, construction, operation or maintenance of any nuclear facility +// ("High Risk Activities"). Sun disclaims any express or implied warranty of +// fitness for such uses. +// +// Please refer to the file http://www.sun.com/policies/trademarks/ for further +// important trademark information and to +// http://java.sun.com/nav/business/index.html for further important licensing +// information for the Java Technology. +// + +package net.hyperic.sigar.util; + +import java.util.Enumeration; +import java.util.Vector; +import java.util.Locale; +import java.text.DecimalFormatSymbols; + +/** + * PrintfFormat allows the formatting of an array of + * objects embedded within a string. Primitive types + * must be passed using wrapper types. The formatting + * is controlled by a control string. + *

+ * A control string is a Java string that contains a + * control specification. The control specification + * starts at the first percent sign (%) in the string, + * provided that this percent sign + *

    + *
  1. is not escaped protected by a matching % or is + * not an escape % character, + *
  2. is not at the end of the format string, and + *
  3. precedes a sequence of characters that parses as + * a valid control specification. + *
+ *

+ * A control specification usually takes the form: + *

 % ['-+ #0]* [0..9]* { . [0..9]* }+
+ *                { [hlL] }+ [idfgGoxXeEcs]
+ *
+ * There are variants of this basic form that are + * discussed below.

+ *

+ * The format is composed of zero or more directives + * defined as follows: + *

    + *
  • ordinary characters, which are simply copied to + * the output stream; + *
  • escape sequences, which represent non-graphic + * characters; and + *
  • conversion specifications, each of which + * results in the fetching of zero or more arguments. + *

+ *

+ * The results are undefined if there are insufficient + * arguments for the format. Usually an unchecked + * exception will be thrown. If the format is + * exhausted while arguments remain, the excess + * arguments are evaluated but are otherwise ignored. + * In format strings containing the % form of + * conversion specifications, each argument in the + * argument list is used exactly once.

+ *

+ * Conversions can be applied to the nth + * argument after the format in the argument list, + * rather than to the next unused argument. In this + * case, the conversion characer % is replaced by the + * sequence %n$, where n is + * a decimal integer giving the position of the + * argument in the argument list.

+ *

+ * In format strings containing the %n$ + * form of conversion specifications, each argument + * in the argument list is used exactly once.

+ * + *

Escape Sequences

+ *

+ * The following table lists escape sequences and + * associated actions on display devices capable of + * the action. + * + * + * + * + * + * + * + * + * + * + * + * + *
SequenceNameDescription
\\backlashNone. + *
\aalertAttempts to alert + * the user through audible or visible + * notification. + *
\bbackspaceMoves the + * printing position to one column before + * the current position, unless the + * current position is the start of a line. + *
\fform-feedMoves the + * printing position to the initial + * printing position of the next logical + * page. + *
\nnewlineMoves the + * printing position to the start of the + * next line. + *
\rcarriage-returnMoves + * the printing position to the start of + * the current line. + *
\ttabMoves the printing + * position to the next implementation- + * defined horizontal tab position. + *
\vvertical-tabMoves the + * printing position to the start of the + * next implementation-defined vertical + * tab position. + *

+ *

Conversion Specifications

+ *

+ * Each conversion specification is introduced by + * the percent sign character (%). After the character + * %, the following appear in sequence:

+ *

+ * Zero or more flags (in any order), which modify the + * meaning of the conversion specification.

+ *

+ * An optional minimum field width. If the converted + * value has fewer characters than the field width, it + * will be padded with spaces by default on the left; + * t will be padded on the right, if the left- + * adjustment flag (-), described below, is given to + * the field width. The field width takes the form + * of a decimal integer. If the conversion character + * is s, the field width is the the minimum number of + * characters to be printed.

+ *

+ * An optional precision that gives the minumum number + * of digits to appear for the d, i, o, x or X + * conversions (the field is padded with leading + * zeros); the number of digits to appear after the + * radix character for the e, E, and f conversions, + * the maximum number of significant digits for the g + * and G conversions; or the maximum number of + * characters to be written from a string is s and S + * conversions. The precision takes the form of an + * optional decimal digit string, where a null digit + * string is treated as 0. If a precision appears + * with a c conversion character the precision is + * ignored. + *

+ *

+ * An optional h specifies that a following d, i, o, + * x, or X conversion character applies to a type + * short argument (the argument will be promoted + * according to the integral promotions and its value + * converted to type short before printing).

+ *

+ * An optional l (ell) specifies that a following + * d, i, o, x, or X conversion character applies to a + * type long argument.

+ *

+ * A field width or precision may be indicated by an + * asterisk (*) instead of a digit string. In this + * case, an integer argument supplised the field width + * precision. The argument that is actually converted + * is not fetched until the conversion letter is seen, + * so the the arguments specifying field width or + * precision must appear before the argument (if any) + * to be converted. If the precision argument is + * negative, it will be changed to zero. A negative + * field width argument is taken as a - flag, followed + * by a positive field width.

+ *

+ * In format strings containing the %n$ + * form of a conversion specification, a field width + * or precision may be indicated by the sequence + * *m$, where m is a decimal integer + * giving the position in the argument list (after the + * format argument) of an integer argument containing + * the field width or precision.

+ *

+ * The format can contain either numbered argument + * specifications (that is, %n$ and + * *m$), or unnumbered argument + * specifications (that is % and *), but normally not + * both. The only exception to this is that %% can + * be mixed with the %n$ form. The + * results of mixing numbered and unnumbered argument + * specifications in a format string are undefined.

+ * + *

Flag Characters

+ *

+ * The flags and their meanings are:

+ *
+ *
'
integer portion of the result of a + * decimal conversion (%i, %d, %f, %g, or %G) will + * be formatted with thousands' grouping + * characters. For other conversions the flag + * is ignored. The non-monetary grouping + * character is used. + *
-
result of the conversion is left-justified + * within the field. (It will be right-justified + * if this flag is not specified). + *
+
result of a signed conversion always + * begins with a sign (+ or -). (It will begin + * with a sign only when a negative value is + * converted if this flag is not specified.) + *
<space>
If the first character of a + * signed conversion is not a sign, a space + * character will be placed before the result. + * This means that if the space character and + + * flags both appear, the space flag will be + * ignored. + *
#
value is to be converted to an alternative + * form. For c, d, i, and s conversions, the flag + * has no effect. For o conversion, it increases + * the precision to force the first digit of the + * result to be a zero. For x or X conversion, a + * non-zero result has 0x or 0X prefixed to it, + * respectively. For e, E, f, g, and G + * conversions, the result always contains a radix + * character, even if no digits follow the radix + * character (normally, a decimal point appears in + * the result of these conversions only if a digit + * follows it). For g and G conversions, trailing + * zeros will not be removed from the result as + * they normally are. + *
0
d, i, o, x, X, e, E, f, g, and G + * conversions, leading zeros (following any + * indication of sign or base) are used to pad to + * the field width; no space padding is + * performed. If the 0 and - flags both appear, + * the 0 flag is ignored. For d, i, o, x, and X + * conversions, if a precision is specified, the + * 0 flag will be ignored. For c conversions, + * the flag is ignored. + *
+ * + *

Conversion Characters

+ *

+ * Each conversion character results in fetching zero + * or more arguments. The results are undefined if + * there are insufficient arguments for the format. + * Usually, an unchecked exception will be thrown. + * If the format is exhausted while arguments remain, + * the excess arguments are ignored.

+ * + *

+ * The conversion characters and their meanings are: + *

+ *
+ *
d,i
The int argument is converted to a + * signed decimal in the style [-]dddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + *
o
The int argument is converted to unsigned + * octal format in the style ddddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + *
x
The int argument is converted to unsigned + * hexadecimal format in the style dddd; the + * letters abcdef are used. The precision + * specifies the minimum numberof digits to + * appear; if the value being converted can be + * represented in fewer digits, it will be + * expanded with leading zeros. The default + * precision is 1. The result of converting 0 + * with an explicit precision of 0 is no + * characters. + *
X
Behaves the same as the x conversion + * character except that letters ABCDEF are + * used instead of abcdef. + *
f
The floating point number argument is + * written in decimal notation in the style + * [-]ddd.ddd, where the number of digits after + * the radix character (shown here as a decimal + * point) is equal to the precision + * specification. A Locale is used to determine + * the radix character to use in this format. + * If the precision is omitted from the + * argument, six digits are written after the + * radix character; if the precision is + * explicitly 0 and the # flag is not specified, + * no radix character appears. If a radix + * character appears, at least 1 digit appears + * before it. The value is rounded to the + * appropriate number of digits. + *
e,E
The floating point number argument is + * written in the style [-]d.ddde{+-}dd + * (the symbols {+-} indicate either a plus or + * minus sign), where there is one digit before + * the radix character (shown here as a decimal + * point) and the number of digits after it is + * equal to the precision. A Locale is used to + * determine the radix character to use in this + * format. When the precision is missing, six + * digits are written after the radix character; + * if the precision is 0 and the # flag is not + * specified, no radix character appears. The + * E conversion will produce a number with E + * instead of e introducing the exponent. The + * exponent always contains at least two digits. + * However, if the value to be written requires + * an exponent greater than two digits, + * additional exponent digits are written as + * necessary. The value is rounded to the + * appropriate number of digits. + *
g,G
The floating point number argument is + * written in style f or e (or in sytle E in the + * case of a G conversion character), with the + * precision specifying the number of + * significant digits. If the precision is + * zero, it is taken as one. The style used + * depends on the value converted: style e + * (or E) will be used only if the exponent + * resulting from the conversion is less than + * -4 or greater than or equal to the precision. + * Trailing zeros are removed from the result. + * A radix character appears only if it is + * followed by a digit. + *
c,C
The integer argument is converted to a + * char and the result is written. + * + *
s,S
The argument is taken to be a string and + * bytes from the string are written until the + * end of the string or the number of bytes + * indicated by the precision specification of + * the argument is reached. If the precision + * is omitted from the argument, it is taken to + * be infinite, so all characters up to the end + * of the string are written. + *
%
Write a % character; no argument is + * converted. + *
+ *

+ * If a conversion specification does not match one of + * the above forms, an IllegalArgumentException is + * thrown and the instance of PrintfFormat is not + * created.

+ *

+ * If a floating point value is the internal + * representation for infinity, the output is + * [+]Infinity, where Infinity is either Infinity or + * Inf, depending on the desired output string length. + * Printing of the sign follows the rules described + * above.

+ *

+ * If a floating point value is the internal + * representation for "not-a-number," the output is + * [+]NaN. Printing of the sign follows the rules + * described above.

+ *

+ * In no case does a non-existent or small field width + * cause truncation of a field; if the result of a + * conversion is wider than the field width, the field + * is simply expanded to contain the conversion result. + *

+ *

+ * The behavior is like printf. One exception is that + * the minimum number of exponent digits is 3 instead + * of 2 for e and E formats when the optional L is used + * before the e, E, g, or G conversion character. The + * optional L does not imply conversion to a long long + * double.

+ *

+ * The biggest divergence from the C printf + * specification is in the use of 16 bit characters. + * This allows the handling of characters beyond the + * small ASCII character set and allows the utility to + * interoperate correctly with the rest of the Java + * runtime environment.

+ *

+ * Omissions from the C printf specification are + * numerous. All the known omissions are present + * because Java never uses bytes to represent + * characters and does not have pointers:

+ *
    + *
  • %c is the same as %C. + *
  • %s is the same as %S. + *
  • u, p, and n conversion characters. + *
  • %ws format. + *
  • h modifier applied to an n conversion character. + *
  • l (ell) modifier applied to the c, n, or s + * conversion characters. + *
  • ll (ell ell) modifier to d, i, o, u, x, or X + * conversion characters. + *
  • ll (ell ell) modifier to an n conversion + * character. + *
  • c, C, d,i,o,u,x, and X conversion characters + * apply to Byte, Character, Short, Integer, Long + * types. + *
  • f, e, E, g, and G conversion characters apply + * to Float and Double types. + *
  • s and S conversion characters apply to String + * types. + *
  • All other reference types can be formatted + * using the s or S conversion characters only. + *
+ *

+ * Most of this specification is quoted from the Unix + * man page for the sprintf utility.

+ * + * @author Allan Jacobs + * @version 1 + * Release 1: Initial release. + * Release 2: Asterisk field widths and precisions + * %n$ and *m$ + * Bug fixes + * g format fix (2 digits in e form corrupt) + * rounding in f format implemented + * round up when digit not printed is 5 + * formatting of -0.0f + * round up/down when last digits are 50000... + */ +public class PrintfFormat { + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(String fmtArg) + throws IllegalArgumentException { + this(Locale.getDefault(),fmtArg); + } + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(Locale locale,String fmtArg) + throws IllegalArgumentException { + dfs = new DecimalFormatSymbols(locale); + int ePos=0; + ConversionSpecification sFmt=null; + String unCS = this.nonControl(fmtArg,0); + if (unCS!=null) { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + while(cPos!=-1 && cPosstart and ending at either the end + * of the String s, the next unpaired + * percent sign, or at the end of the String if the + * last character is a percent sign. + * @param s Control string. + * @param start Position in the string + * s to begin looking for the start + * of a control string. + * @return the substring from the start position + * to the beginning of the control string. + */ + private String nonControl(String s,int start) { + cPos=s.indexOf("%",start); + if (cPos==-1) cPos=s.length(); + return s.substring(start,cPos); + } + /** + * Format an array of objects. Byte, Short, + * Integer, Long, Float, Double, and Character + * arguments are treated as wrappers for primitive + * types. + * @param o The array of objects to format. + * @return The formatted String. + */ + public String sprintf(Object[] o) { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + int i=0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else { + if (cs.isPositionalSpecification()) { + i=cs.getArgumentPosition()-1; + if (cs.isPositionalFieldWidth()) { + int ifw=cs.getArgumentPositionForFieldWidth()-1; + cs.setFieldWidthWithArg(((Integer)o[ifw]).intValue()); + } + if (cs.isPositionalPrecision()) { + int ipr=cs.getArgumentPositionForPrecision()-1; + cs.setPrecisionWithArg(((Integer)o[ipr]).intValue()); + } + } + else { + if (cs.isVariableFieldWidth()) { + cs.setFieldWidthWithArg(((Integer)o[i]).intValue()); + i++; + } + if (cs.isVariablePrecision()) { + cs.setPrecisionWithArg(((Integer)o[i]).intValue()); + i++; + } + } + if (o[i] instanceof Byte) + sb.append(cs.internalsprintf( + ((Byte)o[i]).byteValue())); + else if (o[i] instanceof Short) + sb.append(cs.internalsprintf( + ((Short)o[i]).shortValue())); + else if (o[i] instanceof Integer) + sb.append(cs.internalsprintf( + ((Integer)o[i]).intValue())); + else if (o[i] instanceof Long) + sb.append(cs.internalsprintf( + ((Long)o[i]).longValue())); + else if (o[i] instanceof Float) + sb.append(cs.internalsprintf( + ((Float)o[i]).floatValue())); + else if (o[i] instanceof Double) + sb.append(cs.internalsprintf( + ((Double)o[i]).doubleValue())); + else if (o[i] instanceof Character) + sb.append(cs.internalsprintf( + ((Character)o[i]).charValue())); + else if (o[i] instanceof String) + sb.append(cs.internalsprintf( + (String)o[i])); + else + sb.append(cs.internalsprintf( + o[i])); + if (!cs.isPositionalSpecification()) + i++; + } + } + return sb.toString(); + } + /** + * Format nothing. Just use the control string. + * @return the formatted String. + */ + public String sprintf() { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + } + return sb.toString(); + } + /** + * Format an int. + * @param x The int to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(int x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format an long. + * @param x The long to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(long x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format a double. + * @param x The double to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, + * d, d, x, X, or o. + */ + public String sprintf(double x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format a String. + * @param x The String to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + public String sprintf(String x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format an Object. Convert wrapper types to + * their primitive equivalents and call the + * appropriate internal formatting method. Convert + * Strings using an internal formatting method for + * Strings. Otherwise use the default formatter + * (use toString). + * @param x the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is inappropriate for + * formatting an unwrapped value. + */ + public String sprintf(Object x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else { + if (x instanceof Byte) + sb.append(cs.internalsprintf( + ((Byte)x).byteValue())); + else if (x instanceof Short) + sb.append(cs.internalsprintf( + ((Short)x).shortValue())); + else if (x instanceof Integer) + sb.append(cs.internalsprintf( + ((Integer)x).intValue())); + else if (x instanceof Long) + sb.append(cs.internalsprintf( + ((Long)x).longValue())); + else if (x instanceof Float) + sb.append(cs.internalsprintf( + ((Float)x).floatValue())); + else if (x instanceof Double) + sb.append(cs.internalsprintf( + ((Double)x).doubleValue())); + else if (x instanceof Character) + sb.append(cs.internalsprintf( + ((Character)x).charValue())); + else if (x instanceof String) + sb.append(cs.internalsprintf( + (String)x)); + else + sb.append(cs.internalsprintf(x)); + } + } + return sb.toString(); + } + /** + *

+ * ConversionSpecification allows the formatting of + * a single primitive or object embedded within a + * string. The formatting is controlled by a + * format string. Only one Java primitive or + * object can be formatted at a time. + *

+ * A format string is a Java string that contains + * a control string. The control string starts at + * the first percent sign (%) in the string, + * provided that this percent sign + *

    + *
  1. is not escaped protected by a matching % or + * is not an escape % character, + *
  2. is not at the end of the format string, and + *
  3. precedes a sequence of characters that parses + * as a valid control string. + *
+ *

+ * A control string takes the form: + *

 % ['-+ #0]* [0..9]* { . [0..9]* }+
+   *                { [hlL] }+ [idfgGoxXeEcs]
+   *
+ *

+ * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The + * optional L does not imply conversion to a long + * long double. + */ + private class ConversionSpecification { + /** + * Constructor. Used to prepare an instance + * to hold a literal, not a control string. + */ + ConversionSpecification() { } + /** + * Constructor for a conversion specification. + * The argument must begin with a % and end + * with the conversion character for the + * conversion specification. + * @param fmtArg String specifying the + * conversion specification. + * @exception IllegalArgumentException if the + * input string is null, zero length, or + * otherwise malformed. + */ + ConversionSpecification(String fmtArg) + throws IllegalArgumentException { + if (fmtArg==null) + throw new NullPointerException(); + if (fmtArg.length()==0) + throw new IllegalArgumentException( + "Control strings must have positive"+ + " lengths."); + if (fmtArg.charAt(0)=='%') { + fmt = fmtArg; + pos=1; + setArgPosition(); + setFlagCharacters(); + setFieldWidth(); + setPrecision(); + setOptionalHL(); + if (setConversionCharacter()) { + if (pos==fmtArg.length()) { + if(leadingZeros&&leftJustify) + leadingZeros=false; + if(precisionSet&&leadingZeros){ + if(conversionCharacter=='d' + ||conversionCharacter=='i' + ||conversionCharacter=='o' + ||conversionCharacter=='x') + { + leadingZeros=false; + } + } + } + else + throw new IllegalArgumentException( + "Malformed conversion specification="+ + fmtArg); + } + else + throw new IllegalArgumentException( + "Malformed conversion specification="+ + fmtArg); + } + else + throw new IllegalArgumentException( + "Control strings must begin with %."); + } + /** + * Set the String for this instance. + * @param s the String to store. + */ + void setLiteral(String s) { + fmt = s; + } + /** + * Get the String for this instance. Translate + * any escape sequences. + * + * @return s the stored String. + */ + String getLiteral() { + StringBuffer sb=new StringBuffer(); + int i=0; + while (itrue if the conversion + * uses an * field width; otherwise + * false. + */ + boolean isVariableFieldWidth() { + return variableFieldWidth; + } + /** + * Set the field width with an argument. A + * negative field width is taken as a - flag + * followed by a positive field width. + * @param fw the field width. + */ + void setFieldWidthWithArg(int fw) { + if (fw<0) leftJustify = true; + fieldWidthSet = true; + fieldWidth = Math.abs(fw); + } + /** + * Check whether the specifier has a variable + * precision that is going to be set by an + * argument. + * @return true if the conversion + * uses an * precision; otherwise + * false. + */ + boolean isVariablePrecision() { + return variablePrecision; + } + /** + * Set the precision with an argument. A + * negative precision will be changed to zero. + * @param pr the precision. + */ + void setPrecisionWithArg(int pr) { + precisionSet = true; + precision = Math.max(pr,0); + } + /** + * Format an int argument using this conversion + * specification. + * @param s the int to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(int s) + throws IllegalArgumentException { + String s2 = ""; + switch(conversionCharacter) { + case 'd': + case 'i': + if (optionalh) + s2 = printDFormat((short)s); + else if (optionall) + s2 = printDFormat((long)s); + else + s2 = printDFormat(s); + break; + case 'x': + case 'X': + if (optionalh) + s2 = printXFormat((short)s); + else if (optionall) + s2 = printXFormat((long)s); + else + s2 = printXFormat(s); + break; + case 'o': + if (optionalh) + s2 = printOFormat((short)s); + else if (optionall) + s2 = printOFormat((long)s); + else + s2 = printOFormat(s); + break; + case 'c': + case 'C': + s2 = printCFormat((char)s); + break; + default: + throw new IllegalArgumentException( + "Cannot format a int with a format using a "+ + conversionCharacter+ + " conversion character."); + } + return s2; + } + /** + * Format a long argument using this conversion + * specification. + * @param s the long to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(long s) + throws IllegalArgumentException { + String s2 = ""; + switch(conversionCharacter) { + case 'd': + case 'i': + if (optionalh) + s2 = printDFormat((short)s); + else if (optionall) + s2 = printDFormat(s); + else + s2 = printDFormat((int)s); + break; + case 'x': + case 'X': + if (optionalh) + s2 = printXFormat((short)s); + else if (optionall) + s2 = printXFormat(s); + else + s2 = printXFormat((int)s); + break; + case 'o': + if (optionalh) + s2 = printOFormat((short)s); + else if (optionall) + s2 = printOFormat(s); + else + s2 = printOFormat((int)s); + break; + case 'c': + case 'C': + s2 = printCFormat((char)s); + break; + default: + throw new IllegalArgumentException( + "Cannot format a long with a format using a "+ + conversionCharacter+" conversion character."); + } + return s2; + } + /** + * Format a double argument using this conversion + * specification. + * @param s the double to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, i, d, + * x, X, or o. + */ + String internalsprintf(double s) + throws IllegalArgumentException { + String s2 = ""; + switch(conversionCharacter) { + case 'f': + s2 = printFFormat(s); + break; + case 'E': + case 'e': + s2 = printEFormat(s); + break; + case 'G': + case 'g': + s2 = printGFormat(s); + break; + default: + throw new IllegalArgumentException("Cannot "+ + "format a double with a format using a "+ + conversionCharacter+" conversion character."); + } + return s2; + } + /** + * Format a String argument using this conversion + * specification. + * @param s the String to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(String s) + throws IllegalArgumentException { + String s2 = ""; + if(conversionCharacter=='s' + || conversionCharacter=='S') + s2 = printSFormat(s); + else + throw new IllegalArgumentException("Cannot "+ + "format a String with a format using a "+ + conversionCharacter+" conversion character."); + return s2; + } + /** + * Format an Object argument using this conversion + * specification. + * @param s the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(Object s) { + if (s == null) { + return ""; + } + + String s2 = ""; + if(conversionCharacter=='s' + || conversionCharacter=='S') + s2 = printSFormat(s.toString()); + else + throw new IllegalArgumentException( + "Cannot format a String with a format using"+ + " a "+conversionCharacter+ + " conversion character."); + return s2; + } + /** + * For f format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both + * a '+' and a ' ' are specified, the blank flag + * is ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the number of digits + * to appear after the radix character. Padding is + * with trailing 0s. + */ + private char[] fFormatDigits(double x) { + // int defaultDigits=6; + String sx; + int i,j,k; + int n1In,n2In; + int expon=0; + boolean minusSign=false; + if (x>0.0) + sx = Double.toString(x); + else if (x<0.0) { + sx = Double.toString(-x); + minusSign=true; + } + else { + sx = Double.toString(x); + if (sx.charAt(0)=='-') { + minusSign=true; + sx=sx.substring(1); + } + } + int ePos = sx.indexOf('E'); + int rPos = sx.indexOf('.'); + if (rPos!=-1) n1In=rPos; + else if (ePos!=-1) n1In=ePos; + else n1In=sx.length(); + if (rPos!=-1) { + if (ePos!=-1) n2In = ePos-rPos-1; + else n2In = sx.length()-rPos-1; + } + else + n2In = 0; + if (ePos!=-1) { + int ie=ePos+1; + expon=0; + if (sx.charAt(ie)=='-') { + for (++ie; ie0) { + ca6 = new char[ca5.length+nThousands+lead]; + ca6[0]=ca5[0]; + for (i=lead,k=lead; i0 && (dp-i)%3==0) { + // ca6[k]=','; + ca6[k]=dfs.getGroupingSeparator(); + ca6[k+1]=ca5[i]; + k+=2; + } + else { + ca6[k]=ca5[i]; k++; + } + } + for (; i0.0) + sx = Double.toString(x); + else if (x<0.0) { + sx = Double.toString(-x); + minusSign=true; + } + else { + sx = Double.toString(x); + if (sx.charAt(0)=='-') { + minusSign=true; + sx=sx.substring(1); + } + } + ePos = sx.indexOf('E'); + if (ePos==-1) ePos = sx.indexOf('e'); + rPos = sx.indexOf('.'); + if (rPos!=-1) n1In=rPos; + else if (ePos!=-1) n1In=ePos; + else n1In=sx.length(); + if (rPos!=-1) { + if (ePos!=-1) n2In = ePos-rPos-1; + else n2In = sx.length()-rPos-1; + } + else + n2In = 0; + if (ePos!=-1) { + int ie=ePos+1; + expon=0; + if (sx.charAt(ie)=='-') { + for (++ie; ie=100) { + switch(expon/100) { + case 1: ca2[i]='1'; break; + case 2: ca2[i]='2'; break; + case 3: ca2[i]='3'; break; + case 4: ca2[i]='4'; break; + case 5: ca2[i]='5'; break; + case 6: ca2[i]='6'; break; + case 7: ca2[i]='7'; break; + case 8: ca2[i]='8'; break; + case 9: ca2[i]='9'; break; + } + i++; + } + switch((expon%100)/10) { + case 0: ca2[i]='0'; break; + case 1: ca2[i]='1'; break; + case 2: ca2[i]='2'; break; + case 3: ca2[i]='3'; break; + case 4: ca2[i]='4'; break; + case 5: ca2[i]='5'; break; + case 6: ca2[i]='6'; break; + case 7: ca2[i]='7'; break; + case 8: ca2[i]='8'; break; + case 9: ca2[i]='9'; break; + } + i++; + switch(expon%10) { + case 0: ca2[i]='0'; break; + case 1: ca2[i]='1'; break; + case 2: ca2[i]='2'; break; + case 3: ca2[i]='3'; break; + case 4: ca2[i]='4'; break; + case 5: ca2[i]='5'; break; + case 6: ca2[i]='6'; break; + case 7: ca2[i]='7'; break; + case 8: ca2[i]='8'; break; + case 9: ca2[i]='9'; break; + } + int nZeros=0; + if (!leftJustify && leadingZeros) { + int xThousands=0; + if (thousands) { + int xlead=0; + if (ca2[0]=='+'||ca2[0]=='-'||ca2[0]==' ') + xlead=1; + int xdp=xlead; + for (; xdp0) { + ca4 = new char[ca3.length+nThousands+lead]; + ca4[0]=ca3[0]; + for (i=lead,k=lead; i0 && (dp-i)%3==0) { + // ca4[k]=','; + ca4[k]=dfs.getGroupingSeparator(); + ca4[k+1]=ca3[i]; + k+=2; + } + else { + ca4[k]=ca3[i]; k++; + } + } + for (; itrue if the truncation forces + * a round that will change the print + */ + private boolean checkForCarry(char[] ca1,int icarry) { + boolean carry=false; + if (icarry0) { + carry=(ca1[icarry-1]=='1'||ca1[icarry-1]=='3' + ||ca1[icarry-1]=='5'||ca1[icarry-1]=='7' + ||ca1[icarry-1]=='9'); + } + } + } + return carry; + } + /** + * Start the symbolic carry process. The process + * is not quite finished because the symbolic + * carry may change the length of the string and + * change the exponent (in e format). + * @param cLast index of the last digit changed + * by the round + * @param cFirst index of the first digit allowed + * to be changed by this phase of the round + * @return true if the carry forces + * a round that will change the print still + * more + */ + private boolean startSymbolicCarry( + char[] ca,int cLast,int cFirst) { + boolean carry=true; + for (int i=cLast; carry && i>=cFirst; i--) { + carry = false; + switch(ca[i]) { + case '0': ca[i]='1'; break; + case '1': ca[i]='2'; break; + case '2': ca[i]='3'; break; + case '3': ca[i]='4'; break; + case '4': ca[i]='5'; break; + case '5': ca[i]='6'; break; + case '6': ca[i]='7'; break; + case '7': ca[i]='8'; break; + case '8': ca[i]='9'; break; + case '9': ca[i]='0'; carry=true; break; + } + } + return carry; + } + /** + * An intermediate routine on the way to creating + * an e format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @param eChar an 'e' or 'E' to use in the + * converted double value. + * @return the converted double value. + */ + private String eFormatString(double x,char eChar) { + boolean noDigits=false; + char[] ca4,ca5; + if (Double.isInfinite(x)) { + if (x==Double.POSITIVE_INFINITY) { + if (leadingSign) ca4 = "+Inf".toCharArray(); + else if (leadingSpace) + ca4 = " Inf".toCharArray(); + else ca4 = "Inf".toCharArray(); + } + else + ca4 = "-Inf".toCharArray(); + noDigits = true; + } + else if (Double.isNaN(x)) { + if (leadingSign) ca4 = "+NaN".toCharArray(); + else if (leadingSpace) + ca4 = " NaN".toCharArray(); + else ca4 = "NaN".toCharArray(); + noDigits = true; + } + else + ca4 = eFormatDigits(x,eChar); + ca5 = applyFloatPadding(ca4,false); + return new String(ca5); + } + /** + * Apply zero or blank, left or right padding. + * @param ca4 array of characters before padding is + * finished + * @param noDigits NaN or signed Inf + * @return a padded array of characters + */ + private char[] applyFloatPadding( + char[] ca4,boolean noDigits) { + char[] ca5 = ca4; + if (fieldWidthSet) { + int i,j,nBlanks; + if (leftJustify) { + nBlanks = fieldWidth-ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length+nBlanks]; + for (i=0; i 0) { + ca5 = new char[ca4.length+nBlanks]; + for (i=0; i 0) { + ca5 = new char[ca4.length+nBlanks]; + i=0; j=0; + if (ca4[0]=='-') { ca5[0]='-'; i++; j++; } + for (int k=0; k=-4 && expon=0; i--) + if (sy.charAt(i)!='0') break; + if (i>=0 && sy.charAt(i)=='.') i--; + if (i==-1) sz="0"; + else if (!Character.isDigit(sy.charAt(i))) + sz=sy.substring(0,i+1)+"0"; + else sz=sy.substring(0,i+1); + if (expon>=-4 && expon=-4 && expon=0) ret = " "+ret; + ca4 = ret.toCharArray(); + } + // Pad with blanks or zeros. + ca5 = applyFloatPadding(ca4,false); + precision=savePrecision; + return new String(ca5); + } + /** + * Format method for the d conversion specifer and + * short argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printDFormat(short x) { + return printDFormat(Short.toString(x)); + } + /** + * Format method for the d conversion character and + * long argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printDFormat(long x) { + return printDFormat(Long.toString(x)); + } + /** + * Format method for the d conversion character and + * int argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printDFormat(int x) { + return printDFormat(Integer.toString(x)); + } + /** + * Utility method for formatting using the d + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printDFormat(String sx) { + int nLeadingZeros=0; + int nBlanks=0,n=0; + int i=0,jFirst=0; + boolean neg = sx.charAt(0)=='-'; + if (sx.equals("0")&&precisionSet&&precision==0) + sx=""; + if (!neg) { + if (precisionSet && sx.length() < precision) + nLeadingZeros = precision-sx.length(); + } + else { + if (precisionSet&&(sx.length()-1)precision) + nPrint=precision; + if (!fieldWidthSet) width = nPrint; + int n=0; + if (width>nPrint) n+=width-nPrint; + if (nPrint>=x.length()) n+= x.length(); + else n+= nPrint; + char[] ca = new char[n]; + int i=0; + if (leftJustify) { + if (nPrint>=x.length()) { + char[] csx = x.toCharArray(); + for (i=0; i=x.length()) { + char[] csx = x.toCharArray(); + for (int j=0; jtrue if the conversion + * character is there, and + * false otherwise. + */ + private boolean setConversionCharacter() { + /* idfgGoxXeEcs */ + boolean ret = false; + conversionCharacter='\0'; + if (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (c=='i'||c=='d'||c=='f'||c=='g'||c=='G' + || c=='o' || c=='x' || c=='X' || c=='e' + || c=='E' || c=='c' || c=='s' || c=='%') { + conversionCharacter = c; + pos++; + ret = true; + } + } + return ret; + } + /** + * Check for an h, l, or L in a format. An L is + * used to control the minimum number of digits + * in an exponent when using floating point + * formats. An l or h is used to control + * conversion of the input to a long or short, + * respectively, before formatting. If any of + * these is present, store them. + */ + private void setOptionalHL() { + optionalh=false; + optionall=false; + optionalL=false; + if (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (c=='h') { optionalh=true; pos++; } + else if (c=='l') { optionall=true; pos++; } + else if (c=='L') { optionalL=true; pos++; } + } + } + /** + * Set the precision. + */ + private void setPrecision() { + int firstPos = pos; + precisionSet = false; + if (pos firstPos+1) { + String sz = fmt.substring(firstPos+1,pos); + precision = Integer.parseInt(sz); + precisionSet = true; + } + } + } + } + /** + * Set the field width. + */ + private void setFieldWidth() { + int firstPos = pos; + fieldWidth = 0; + fieldWidthSet = false; + if ((pos < fmt.length()) + && (fmt.charAt(pos)=='*')) { + pos++; + if (!setFieldWidthArgPosition()) { + variableFieldWidth = true; + fieldWidthSet = true; + } + } + else { + while (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (Character.isDigit(c)) pos++; + else break; + } + if (firstPosn in %n$ forms. + */ + private void setArgPosition() { + int xPos; + for (xPos=pos; xPospos && xPosn in *n$ forms. + */ + private boolean setFieldWidthArgPosition() { + boolean ret=false; + int xPos; + for (xPos=pos; xPospos && xPosn in *n$ forms. + */ + private boolean setPrecisionArgPosition() { + boolean ret=false; + int xPos; + for (xPos=pos; xPospos && xPosget_name(); + +my $ccname = $Config{ccname}; + +my $define = { + gcc => '-Wall -Werror', + cc_r => '-qhalt=w', +}->{$ccname} || ''; + +WriteMakefile( + 'NAME' => 'Sigar', + 'VERSION_FROM' => 'Sigar.pm', + 'LIBS' => ["-L$installdir/lib -lsigar-$archname"], + 'INC' => "-I$installdir/include", + 'DEFINE' => $define, + 'depend' => { 'Sigar.c' => 'Sigar_generated.xs' }, +); diff --git a/bindings/perl/Sigar.pm b/bindings/perl/Sigar.pm new file mode 100644 index 00000000..cb0ce9e3 --- /dev/null +++ b/bindings/perl/Sigar.pm @@ -0,0 +1,24 @@ +package Sigar; + +use 5.003; +use strict; +use vars qw($VERSION @ISA); + +$VERSION = '0.01'; + +eval { + require XSLoader; +}; + +if ($@) { + #$] < 5.6 + require DynaLoader; + @ISA = qw(DynaLoader); + __PACKAGE__->bootstrap($VERSION); +} +else { + XSLoader::load(__PACKAGE__, $VERSION); +} + +1; +__END__ diff --git a/bindings/perl/Sigar.xs b/bindings/perl/Sigar.xs new file mode 100644 index 00000000..cd864ed0 --- /dev/null +++ b/bindings/perl/Sigar.xs @@ -0,0 +1,350 @@ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include "sigar.h" +#include "sigar_fileinfo.h" + +typedef sigar_t * Sigar; + +/* generated list */ +typedef sigar_uptime_t * Sigar__Uptime; +typedef sigar_mem_t * Sigar__Mem; +typedef sigar_proc_cred_t * Sigar__ProcCred; +typedef sigar_proc_fd_t * Sigar__ProcFd; +typedef sigar_dir_stat_t * Sigar__DirStat; +typedef sigar_proc_cred_name_t * Sigar__ProcCredName; +typedef sigar_file_attrs_t * Sigar__FileAttrs; +typedef sigar_cpu_t * Sigar__Cpu; +typedef sigar_cpu_info_t * Sigar__CpuInfo; +typedef sigar_net_interface_config_t * Sigar__NetInterfaceConfig; +typedef sigar_swap_t * Sigar__Swap; +typedef sigar_proc_state_t * Sigar__ProcState; +typedef sigar_net_connection_t * Sigar__NetConnection; +typedef sigar_proc_exe_t * Sigar__ProcExe; +typedef sigar_proc_time_t * Sigar__ProcTime; +typedef sigar_proc_mem_t * Sigar__ProcMem; +typedef sigar_file_system_t * Sigar__FileSystem; +typedef sigar_file_system_usage_t * Sigar__FileSystemUsage; +typedef sigar_proc_stat_t * Sigar__ProcStat; +typedef sigar_net_route_t * Sigar__NetRoute; +typedef sigar_net_interface_stat_t * Sigar__NetInterfaceStat; + +/* Perl < 5.6 */ +#ifndef aTHX_ +#define aTHX_ +#endif + +#define SIGAR_CROAK(sigar, msg) \ + Perl_croak(aTHX_ msg " %s", sigar_strerror(sigar, status)) + +static SV *convert_2svav(char *data, unsigned long number, + int size, const char *classname) +{ + AV *av = newAV(); + unsigned long i; + + for (i=0; iklen == klen) && + (strEQ(get->key, key))) + { + get->val = newSVpv(val, vlen); + return !SIGAR_OK; /* foundit; stop iterating */ + } + + return SIGAR_OK; +} + +MODULE = Sigar PACKAGE = Sigar + +PROTOTYPES: disable + +INCLUDE: Sigar_generated.xs + +MODULE = Sigar PACKAGE = Sigar PREFIX = sigar_ + +Sigar +new(classname) + char *classname + + PREINIT: + int status; + + CODE: + if ((status = sigar_open(&RETVAL)) != SIGAR_OK) { + classname = classname; /* -Wall */ + SIGAR_CROAK(RETVAL, "open"); + } + + OUTPUT: + RETVAL + +void +DESTROY(sigar) + Sigar sigar + + CODE: + (void)sigar_close(sigar); + +char * +sigar_fqdn(sigar) + Sigar sigar + + PREINIT: + char fqdn[SIGAR_FQDN_LEN]; + + CODE: + sigar_fqdn_get(sigar, fqdn, sizeof(fqdn)); + RETVAL = fqdn; + + OUTPUT: + RETVAL + +SV * +loadavg(sigar) + Sigar sigar + + PREINIT: + sigar_loadavg_t loadavg; + int status; + unsigned long i; + AV *av; + + CODE: + status = sigar_loadavg_get(sigar, &loadavg); + + if (status != SIGAR_OK) { + SIGAR_CROAK(sigar, "loadavg"); + } + + av = newAV(); + av_extend(av, 2); + + for (i=0; i<3; i++) { + av_push(av, newSVnv(loadavg.loadavg[i])); + } + + RETVAL = newRV_noinc((SV*)av); + + OUTPUT: + RETVAL + +SV * +file_system_list(sigar) + Sigar sigar + + PREINIT: + sigar_file_system_list_t fslist; + int status; + + CODE: + status = sigar_file_system_list_get(sigar, &fslist); + + if (status != SIGAR_OK) { + SIGAR_CROAK(sigar, "fslist"); + } + + RETVAL = convert_2svav((char *)&fslist.data[0], + fslist.number, + sizeof(*fslist.data), + "Sigar::FileSystem"); + + sigar_file_system_list_destroy(sigar, &fslist); + + OUTPUT: + RETVAL + +SV * +cpu_infos(sigar) + Sigar sigar + + PREINIT: + sigar_cpu_infos_t cpu_infos; + int status; + + CODE: + status = sigar_cpu_infos_get(sigar, &cpu_infos); + + if (status != SIGAR_OK) { + SIGAR_CROAK(sigar, "cpu_infos"); + } + + RETVAL = convert_2svav((char *)&cpu_infos.data[0], + cpu_infos.number, + sizeof(*cpu_infos.data), + "Sigar::CpuInfo"); + + sigar_cpu_infos_destroy(sigar, &cpu_infos); + + OUTPUT: + RETVAL + +SV * +proc_list(sigar) + Sigar sigar + + PREINIT: + sigar_proc_list_t proclist; + int status; + unsigned long i; + AV *av; + + CODE: + status = sigar_proc_list_get(sigar, &proclist); + + if (status != SIGAR_OK) { + SIGAR_CROAK(sigar, "proc_list"); + } + + av = newAV(); + av_extend(av, proclist.number - 1); + + for (i=0; icpu_infos; + +my $num = scalar @$infos; + +print "$num total CPUs..\n"; + +for my $info (@$infos) { + print "Vendor........" . $info->vendor . "\n"; + print "Model........." . $info->model . "\n"; + print "Mhz..........." . $info->mhz . "\n"; + print "Cache size...." . $info->cache_size . "\n"; +} diff --git a/bindings/perl/examples/df.pl b/bindings/perl/examples/df.pl new file mode 100755 index 00000000..d1a2aaf8 --- /dev/null +++ b/bindings/perl/examples/df.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl + +use strict; +use Sigar; + +my $sigar = new Sigar; + +my $fslist = $sigar->file_system_list; + +print "Filesytem\tSize\tUsed\tAvail\tUse%\tMounted on\tType\n"; + +for my $fs (@$fslist) { + my $dirname = $fs->dir_name; + my $usage = $sigar->filesystemusage($dirname); + + my $total = $usage->total; + my $used = $total - $usage->free; + my $avail = $usage->avail; + my $pct = $usage->use_percent * 100; + my $usePct; + + if ($pct == 0) { + $usePct = "-" + } + else { + $usePct = $pct . '%'; + } + + print + $fs->dev_name . "\t" . + $total . "\t" . + $used . "\t" . + $avail . "\t" . + $usePct . "\t" . + $dirname . "\t" . + $fs->sys_type_name . "/" . $fs->type_name . "\n"; + +} diff --git a/bindings/perl/examples/free.pl b/bindings/perl/examples/free.pl new file mode 100755 index 00000000..86ed2fdf --- /dev/null +++ b/bindings/perl/examples/free.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl + +use strict; +use Sigar; + +my $sigar = new Sigar; + +my $mem = $sigar->mem; + +my $swap = $sigar->swap; + +print "\tTotal\tUsed\tFree\n"; + +print "Mem: " . + ($mem->total / 1024) . "\t" . + ($mem->used / 1024) . "\t" . + ($mem->free / 1024) . "\n"; + +print "Swap: " . + ($swap->total / 1024) . "\t" . + ($swap->used / 1024) . "\t" . + ($swap->free / 1024) . "\n"; + +print "RAM: " . $mem->ram . "MB\n"; diff --git a/bindings/perl/lib/Sigar/ArchName.pm b/bindings/perl/lib/Sigar/ArchName.pm new file mode 100644 index 00000000..14749fac --- /dev/null +++ b/bindings/perl/lib/Sigar/ArchName.pm @@ -0,0 +1,41 @@ +package Sigar::ArchName; + +use strict; +use Config; + +sub get_name { + my $os = lc $^O; + my $vers = $Config{osvers}; + my $arch = $Config{archname}; + + if ($os =~ /win32/) { + return 'x86-winnt'; + } + elsif ($os =~ /linux/) { + return 'x86-linux'; + } + elsif ($os =~ /hpux/) { + if ($vers =~ /11\./) { + return 'hppa2.0-hp-hpux-11.x'; + } + } + elsif ($os =~ /aix/) { + return 'powerpc-ibm-aix-4.3.x'; + } + elsif ($os =~ /solaris/) { + if ($arch =~ /sun4/) { + return 'sparc-sun-solaris-2.x'; + } + elsif ($arch =~ /.86/) { + return 'x86-sun-solaris-2.x'; + } + } + elsif ($os =~ /darwin/) { + return 'powerpc-apple-darwin'; + } + + die "Unsupported platform"; +} + +1; +__END__ diff --git a/bindings/perl/t/load.t b/bindings/perl/t/load.t new file mode 100644 index 00000000..3c17d7c6 --- /dev/null +++ b/bindings/perl/t/load.t @@ -0,0 +1,12 @@ +use strict; +use Test; + +use Sigar (); + +BEGIN { + plan tests => 1; +} + +my $sigar = new Sigar; + +ok $sigar; diff --git a/bindings/perl/typemap b/bindings/perl/typemap new file mode 100644 index 00000000..0317ee42 --- /dev/null +++ b/bindings/perl/typemap @@ -0,0 +1,27 @@ +Sigar T_PTROBJ +sigar_pid_t T_UV +const char * T_PV +UV T_UV + +#generated list +Sigar::Uptime T_PTROBJ +Sigar::Mem T_PTROBJ +Sigar::ProcCred T_PTROBJ +Sigar::ProcFd T_PTROBJ +Sigar::DirStat T_PTROBJ +Sigar::ProcCredName T_PTROBJ +Sigar::FileAttrs T_PTROBJ +Sigar::Cpu T_PTROBJ +Sigar::CpuInfo T_PTROBJ +Sigar::NetInterfaceConfig T_PTROBJ +Sigar::Swap T_PTROBJ +Sigar::ProcState T_PTROBJ +Sigar::NetConnection T_PTROBJ +Sigar::ProcExe T_PTROBJ +Sigar::ProcTime T_PTROBJ +Sigar::ProcMem T_PTROBJ +Sigar::FileSystem T_PTROBJ +Sigar::FileSystemUsage T_PTROBJ +Sigar::ProcStat T_PTROBJ +Sigar::NetRoute T_PTROBJ +Sigar::NetInterfaceStat T_PTROBJ diff --git a/examples/sigfreed.c b/examples/sigfreed.c new file mode 100644 index 00000000..5c662f5f --- /dev/null +++ b/examples/sigfreed.c @@ -0,0 +1,45 @@ +#include +#include + +#include "sigar.h" + +int main(int argc, char *argv[]){ + sigar_swap_t swapinfo; + sigar_mem_t meminfo; + sigar_t *sig; + int err; + + if(sigar_open(&sig) != SIGAR_OK){ + fprintf(stderr, "Error opening sigar context!\n"); + return -1; + } + + if((err = sigar_mem_get(sig, &meminfo)) != SIGAR_OK || + (err = sigar_swap_get(sig, &swapinfo)) != SIGAR_OK) + { + fprintf(stderr, "Failed to get memory info: %s\n", + strerror(err)); + sigar_close(sig); + return -1; + } + + fprintf(stdout, "%18s %10s %10s %10s %10s %10s\n", + "total", "used", "free", "shared", "buffers", "cached"); + fprintf(stdout, "Mem: %10ld %10ld %10ld %10ld %10ld %10ld\n", + meminfo.total / 1024, + meminfo.used / 1024, + meminfo.free / 1024, + meminfo.shared / 1024, + meminfo.buffer / 1024, + meminfo.cached / 1024); + fprintf(stdout, "-/+ buffers/cache: %10ld %10d\n", + (meminfo.used - meminfo.buffer - meminfo.cached) / 1024, + (meminfo.free + meminfo.buffer + meminfo.cached) / 1024); + fprintf(stdout, "Swap: %10ld %10ld %10ld\n", + swapinfo.total / 1024, + swapinfo.used / 1024, + swapinfo.free / 1024); + + sigar_close(sig); + return 0; +} diff --git a/examples/sigps.c b/examples/sigps.c new file mode 100644 index 00000000..e5312626 --- /dev/null +++ b/examples/sigps.c @@ -0,0 +1,15 @@ +#include + +#include "sigar.h" + +int main(int argc, char *argv[]){ + sigar_t *sig; + + if(sigar_open(&sig) != SIGAR_OK){ + fprintf(stderr, "Error opening sigar context!\n"); + return -1; + } + + sigar_close(sig); + return 0; +} diff --git a/exp/README b/exp/README new file mode 100644 index 00000000..8e1c8a02 --- /dev/null +++ b/exp/README @@ -0,0 +1,2 @@ +this directory contains some experiments that are not in the codebase yet. +may or may not move forward, but would prefer not to loose these stuffs. diff --git a/exp/dump_kstats.c b/exp/dump_kstats.c new file mode 100644 index 00000000..12b3f4a3 --- /dev/null +++ b/exp/dump_kstats.c @@ -0,0 +1,24 @@ +#include +#include + +/* gcc -lkstat -o dump_kstats dump_kstats.c */ + +static const char *kstat_type_names[] = { + "raw", "named", "intr", "io", "timer" +}; + +int main(int argc, char **argv) { + kstat_ctl_t *kc = kstat_open(); + kstat_t *kp; + + for (kp = kc->kc_chain; kp != 0; kp = kp->ks_next) { + if (strncmp(kp->ks_name, "kstat_", 6) == 0) { + continue; + } + fprintf(stdout, "%-5s %s\n", + kstat_type_names[kp->ks_type], + kp->ks_name); + } + + kstat_close(kc); +} diff --git a/exp/linux_pid_portmap.c b/exp/linux_pid_portmap.c new file mode 100644 index 00000000..d501404e --- /dev/null +++ b/exp/linux_pid_portmap.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * eam can provide per-process measurements, but at the moment requires + * a pid. something we cannot store in the database. however, we could + * store a service port in the database (e.g. 8009 for tomcat ajp) and + * figure out the pid given that. + * + * it is possible via /proc/net/tcp and /proc/$pid/fd/ + * to map a tcp port to one (or more if forked) process id(s). + * caveats: expensive to scan /proc/$pid/fd + * must own process or be root to read /proc/$pid/fd + * brutal to provide similar functionality on other platforms, + * tho possible (see lsof). + */ + +static char *sigar_skip_token(char *p) +{ + while (isspace(*p)) p++; + while (*p && !isspace(*p)) p++; + return p; +} + +static char *sigar_skip_multiple_token(char *p, int count) +{ + int i; + + for (i = 0; i < count; i++) { + p = sigar_skip_token(p); + } + + return p; +} + +static int get_process_ppid(char *pid, char *buf, int buflen) +{ + FILE *fp; + char buffer[1024], *ptr, *ptr2; + sprintf(buffer, "/proc/%s/stat", pid); + + if (!(fp = fopen(buffer, "r"))) { + return errno; + } + + ptr = fgets(buffer, sizeof(buffer), fp); + fclose(fp); + + ptr = sigar_skip_multiple_token(ptr, 3); + + while (isspace(*ptr)) { + ++ptr; + } + + ptr2 = strchr(ptr, ' '); + *ptr2 = '\0'; + + strncpy(buf, ptr2, buflen); + buf[buflen] = '\0'; + + return 0; +} + +typedef struct { + int port; + int inode; + int uid; + int pid; + char name[1024]; +} portmap_t; + +static int portmap_lookup_inode(portmap_t *map) { + FILE *fp; + char buffer[8192], *ptr; + int inode = 0; + + if ((fp = fopen("/proc/net/tcp", "r")) < 0) { + return -1; + } + + fgets(buffer, sizeof(buffer), fp); /* skip header */ + + while ((ptr = fgets(buffer, sizeof(buffer), fp))) { + ptr = sigar_skip_token(ptr); + + if ((ptr = strchr(ptr, ':'))) { + unsigned long port = strtoul(++ptr, &ptr, 16); + + if (!(map->port == (port & 0xffff))) { + continue; + } + } + + ptr = sigar_skip_multiple_token(ptr, 5); + map->uid = strtoul(ptr, &ptr, 10); + + ptr = sigar_skip_token(ptr); + + inode = map->inode = strtoul(ptr, &ptr, 10); + + break; + } + + fclose(fp); + + return inode ? 0 : ENOENT; +} + +static int portmap_lookup_pid(portmap_t *map, char *pid) { + DIR *dirp; + struct dirent *dp; + char fname[1024], dname[1024]; + + map->pid = -1; + + sprintf(dname, "/proc/%s/fd", pid); + + if (!(dirp = opendir(dname))) { + return errno; + } + + while ((dp = readdir(dirp))) { + struct stat statbuf; + + if (!isdigit(*dp->d_name)) { + continue; + } + + sprintf(fname, "%s/%s", dname, dp->d_name); + //printf("check %s\n", fname); + if (stat(fname, &statbuf)) { + continue; + } + + if (statbuf.st_ino == map->inode) { + map->pid = atoi(pid); + break; + } + } + + closedir(dirp); + + return map->pid == -1 ? ENOENT : 0; +} + +static int portmap_lookup_pid_scan(portmap_t *map) { + DIR *dirp; + struct dirent *dp; + + char dname[1024]; + + if (!(dirp = opendir("/proc"))) { + return -1; + } + + map->pid = -1; + + while ((dp = readdir(dirp))) { + struct stat statbuf; + + if (!isdigit(*dp->d_name)) { + continue; + } + + sprintf(dname, "/proc/%s", dp->d_name); + if (stat(dname, &statbuf)) { + continue; + } + + if (!(statbuf.st_uid == map->uid)) { + continue; + } + + if (portmap_lookup_pid(map, dp->d_name) == 0) { +#if 0 + char *pid = dp->d_name; + char ppid[16]; + get_process_ppid(pid, ppid, sizeof(ppid)); +#endif + //break; + printf("%s has inode %d\n", dp->d_name, map->inode); + } + } + + closedir(dirp); + + return map->pid == -1 ? ENOENT : 0; +} + +int main(int argc, char **argv) { + portmap_t map; + + if (argc != 2) { + printf("usage: %s port\n", argv[0]); + } + + map.port = atoi(argv[1]); + + if (portmap_lookup_inode(&map) == 0) { + if (portmap_lookup_pid_scan(&map) == 0) { + printf("inode=%d, pid=%d\n", map.inode, map.pid); + } + } + + return 1; +} diff --git a/exp/proc/cpuinfo_DB b/exp/proc/cpuinfo_DB new file mode 100644 index 00000000..8def6f8f --- /dev/null +++ b/exp/proc/cpuinfo_DB @@ -0,0 +1,84 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.80GHz +stepping : 7 +cpu MHz : 2787.455 +cache size : 512 KB +physical id : 0 +siblings : 2 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 5557.45 + +processor : 1 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.80GHz +stepping : 7 +cpu MHz : 2787.455 +cache size : 512 KB +physical id : 0 +siblings : 2 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 5570.56 + +processor : 2 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.80GHz +stepping : 7 +cpu MHz : 2787.455 +cache size : 512 KB +physical id : 3 +siblings : 2 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 5570.56 + +processor : 3 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.80GHz +stepping : 7 +cpu MHz : 2787.455 +cache size : 512 KB +physical id : 3 +siblings : 2 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 5570.56 + diff --git a/exp/proc/cpuinfo_EV b/exp/proc/cpuinfo_EV new file mode 100644 index 00000000..f3cca2c3 --- /dev/null +++ b/exp/proc/cpuinfo_EV @@ -0,0 +1,84 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 10 +model name : Pentium III (Cascades) +stepping : 1 +cpu MHz : 699.331 +cache size : 1024 KB +physical id : 0 +siblings : 1 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse +bogomips : 1395.91 + +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 10 +model name : Pentium III (Cascades) +stepping : 1 +cpu MHz : 699.331 +cache size : 1024 KB +physical id : 0 +siblings : 1 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse +bogomips : 1395.91 + +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 10 +model name : Pentium III (Cascades) +stepping : 1 +cpu MHz : 699.331 +cache size : 1024 KB +physical id : 0 +siblings : 1 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse +bogomips : 1395.91 + +processor : 3 +vendor_id : GenuineIntel +cpu family : 6 +model : 10 +model name : Pentium III (Cascades) +stepping : 1 +cpu MHz : 699.331 +cache size : 1024 KB +physical id : 0 +siblings : 1 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse +bogomips : 1395.91 + diff --git a/exp/proc/cpuinfo_hawk b/exp/proc/cpuinfo_hawk new file mode 100644 index 00000000..ac778d98 --- /dev/null +++ b/exp/proc/cpuinfo_hawk @@ -0,0 +1,88 @@ +processor : 0 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.40GHz +stepping : 7 +cpu MHz : 2392.082 +cache size : 512 KB +physical id : 0 +siblings : 2 +runqueue : 0 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 4771.02 + +processor : 1 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.40GHz +stepping : 7 +cpu MHz : 2392.082 +cache size : 512 KB +physical id : 3 +siblings : 2 +runqueue : 1 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 4771.02 + +processor : 2 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.40GHz +stepping : 7 +cpu MHz : 2392.082 +cache size : 512 KB +physical id : 0 +siblings : 2 +runqueue : 0 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 4771.02 + +processor : 3 +vendor_id : GenuineIntel +cpu family : 15 +model : 2 +model name : Intel(R) Xeon(TM) CPU 2.40GHz +stepping : 7 +cpu MHz : 2392.082 +cache size : 512 KB +physical id : 3 +siblings : 2 +runqueue : 1 +fdiv_bug : no +hlt_bug : no +f00f_bug : no +coma_bug : no +fpu : yes +fpu_exception : yes +cpuid level : 2 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm +bogomips : 4771.02 + diff --git a/exp/proc/stat_DB b/exp/proc/stat_DB new file mode 100644 index 00000000..07b91767 --- /dev/null +++ b/exp/proc/stat_DB @@ -0,0 +1,18 @@ +cpu 35136407 290023172 43449938 374366755 +cpu0 11428264 67278276 12035936 1168743416 +cpu1 5905554 81357812 9496308 1162726218 +cpu2 11675543 74058184 12357744 1161394421 +cpu3 6127046 67328900 9559950 1176469996 +page 1313466877 1018279755 +swap 2502992 5804383 +intr 836975522 1259485892 143 0 0 665 0 102 0 2 0 189299694 1917217665 393 +0 1849 1765936413 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +disk_io: (2,0):(66,66,84,0,0) +ctxt 4284712155 +btime 1074702658 +processes 33854757 diff --git a/include/sigar.h b/include/sigar.h new file mode 100644 index 00000000..187ba8c4 --- /dev/null +++ b/include/sigar.h @@ -0,0 +1,489 @@ +#ifndef SIGAR_H +#define SIGAR_H + +/* System Information Gatherer And Reporter */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) + +typedef unsigned __int64 sigar_uint64_t; + +#elif ULONG_MAX > 4294967295UL + +typedef unsigned long sigar_uint64_t; + +#else + +typedef unsigned long long sigar_uint64_t; + +#endif + +#define SIGAR_OK 0 +#define SIGAR_START_ERROR 20000 +#define SIGAR_ENOTIMPL (SIGAR_START_ERROR + 1) +#define SIGAR_OS_START_ERROR (SIGAR_START_ERROR*2) + +#ifdef WIN32 +# define SIGAR_DECLARE(type) \ + __declspec(dllexport) type __stdcall +#else +# define SIGAR_DECLARE(type) type +#endif + +#if defined(PATH_MAX) +# define SIGAR_PATH_MAX PATH_MAX +#elif defined(MAXPATHLEN) +# define SIGAR_PATH_MAX MAXPATHLEN +#else +# define SIGAR_PATH_MAX 8192 +#endif + +#ifdef WIN32 +typedef sigar_uint64_t sigar_pid_t; +typedef unsigned long sigar_uid_t; +typedef unsigned long sigar_gid_t; +#else +#include +typedef pid_t sigar_pid_t; +typedef uid_t sigar_uid_t; +typedef gid_t sigar_gid_t; +#endif + +typedef struct sigar_t sigar_t; + +SIGAR_DECLARE(int) sigar_open(sigar_t **sigar); + +SIGAR_DECLARE(int) sigar_close(sigar_t *sigar); + +SIGAR_DECLARE(sigar_pid_t) sigar_pid_get(sigar_t *sigar); + +SIGAR_DECLARE(int) sigar_proc_kill(sigar_pid_t pid, int signum); + +SIGAR_DECLARE(char *) sigar_strerror(sigar_t *sigar, int err); + +/* system memory info */ + +typedef struct { + sigar_uint64_t + ram, + total, + used, + free, + shared, + buffer, + cached; +} sigar_mem_t; + +SIGAR_DECLARE(int) sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem); + +typedef struct { + sigar_uint64_t + total, + used, + free; +} sigar_swap_t; + +SIGAR_DECLARE(int) sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap); + +typedef struct { + sigar_uint64_t + user, + sys, + nice, + idle, + total; +} sigar_cpu_t; + +SIGAR_DECLARE(int) sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu); + +typedef struct { + unsigned long number; + unsigned long size; + sigar_cpu_t *data; +} sigar_cpu_list_t; + +SIGAR_DECLARE(int) sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist); + +SIGAR_DECLARE(int) sigar_cpu_list_destroy(sigar_t *sigar, + sigar_cpu_list_t *cpulist); + +typedef struct { + char vendor[128]; + char model[128]; + int mhz; + sigar_uint64_t cache_size; +} sigar_cpu_info_t; + +typedef struct { + unsigned long number; + unsigned long size; + sigar_cpu_info_t *data; +} sigar_cpu_infos_t; + +SIGAR_DECLARE(int) sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos); + +SIGAR_DECLARE(int) sigar_cpu_infos_destroy(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos); + +typedef struct { + double uptime; + double idletime; +} sigar_uptime_t; + +SIGAR_DECLARE(int) sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime); + +SIGAR_DECLARE(int) sigar_uptime_string(sigar_t *sigar, + sigar_uptime_t *uptime, + char *buffer, + int buflen); + +SIGAR_DECLARE(char *) sigar_format_size(sigar_uint64_t size, char *buf); + +typedef struct { + double loadavg[3]; +} sigar_loadavg_t; + +SIGAR_DECLARE(int) sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg); + +typedef struct { + unsigned long number; + unsigned long size; + sigar_pid_t *data; +} sigar_proc_list_t; + +SIGAR_DECLARE(int) sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist); + +SIGAR_DECLARE(int) sigar_proc_list_destroy(sigar_t *sigar, + sigar_proc_list_t *proclist); + +typedef struct { + sigar_uint64_t total; +} sigar_proc_stat_t; + +SIGAR_DECLARE(int) sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat); + +typedef struct { + sigar_uint64_t + size, + vsize, + resident, + share, + rss; +} sigar_proc_mem_t; + +SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem); + +typedef struct { + sigar_uid_t uid; + sigar_gid_t gid; + sigar_uid_t euid; + sigar_gid_t egid; +} sigar_proc_cred_t; + +SIGAR_DECLARE(int) sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred); + +#define SIGAR_CRED_NAME_MAX 512 + +typedef struct { + char user[SIGAR_CRED_NAME_MAX]; + char group[SIGAR_CRED_NAME_MAX]; +} sigar_proc_cred_name_t; + +SIGAR_DECLARE(int) +sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_name_t *proccredname); + +typedef struct { + sigar_uint64_t + start_time, + utime, + stime; +} sigar_proc_time_t; + +SIGAR_DECLARE(int) sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime); + +#define SIGAR_PROC_NAME_LEN 128 + +typedef struct { + char name[SIGAR_PROC_NAME_LEN]; + char state; + sigar_pid_t ppid; + int tty; + int priority; + int nice; +} sigar_proc_state_t; + +SIGAR_DECLARE(int) sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate); + +typedef struct { + unsigned long number; + unsigned long size; + char **data; +} sigar_proc_args_t; + +SIGAR_DECLARE(int) sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs); + +SIGAR_DECLARE(int) sigar_proc_args_destroy(sigar_t *sigar, + sigar_proc_args_t *procargs); + +typedef struct { + void *data; /* user data */ + + enum { + SIGAR_PROC_ENV_ALL, + SIGAR_PROC_ENV_KEY + } type; + + /* used for SIGAR_PROC_ENV_KEY */ + const char *key; + int klen; + + int (*env_getter)(void *, const char *, int, char *, int); +} sigar_proc_env_t; + +SIGAR_DECLARE(int) sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv); + +typedef struct { + sigar_uint64_t total; + /* XXX - which are files, sockets, etc. */ +} sigar_proc_fd_t; + +SIGAR_DECLARE(int) sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd); + +typedef struct { + char name[SIGAR_PATH_MAX+1]; + char cwd[SIGAR_PATH_MAX+1]; + char root[SIGAR_PATH_MAX+1]; +} sigar_proc_exe_t; + +SIGAR_DECLARE(int) sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe); + +typedef enum { + SIGAR_FSTYPE_UNKNOWN, + SIGAR_FSTYPE_NONE, + SIGAR_FSTYPE_LOCAL_DISK, + SIGAR_FSTYPE_NETWORK, + SIGAR_FSTYPE_RAM_DISK, + SIGAR_FSTYPE_CDROM, + SIGAR_FSTYPE_SWAP, + SIGAR_FSTYPE_MAX +} sigar_file_system_type_e; + +#define SIGAR_FS_NAME_LEN 64 + +typedef struct { + char dir_name[SIGAR_FS_NAME_LEN]; + char dev_name[SIGAR_FS_NAME_LEN]; + char type_name[SIGAR_FS_NAME_LEN]; /* e.g. "local" */ + char sys_type_name[SIGAR_FS_NAME_LEN]; /* e.g. "ext3" */ + sigar_file_system_type_e type; + unsigned long flags; +} sigar_file_system_t; + +typedef struct { + unsigned long number; + unsigned long size; + sigar_file_system_t *data; +} sigar_file_system_list_t; + +SIGAR_DECLARE(int) +sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist); + +SIGAR_DECLARE(int) +sigar_file_system_list_destroy(sigar_t *sigar, + sigar_file_system_list_t *fslist); + +typedef struct { + sigar_uint64_t + total, + free, + avail, + files, + free_files; + double use_percent; +} sigar_file_system_usage_t; + +SIGAR_DECLARE(int) +sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage); + +typedef struct { + sigar_uint64_t + destination, + gateway, + flags, + refcnt, + use, + metric, + mask, + mtu, + window, + irtt; + char ifname[16]; +} sigar_net_route_t; + +typedef struct { + unsigned long number; + unsigned long size; + sigar_net_route_t *data; +} sigar_net_route_list_t; + +SIGAR_DECLARE(int) sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist); + +SIGAR_DECLARE(int) sigar_net_route_list_destroy(sigar_t *sigar, + sigar_net_route_list_t *routelist); + +/* + * platforms define most of these "standard" flags, + * but of course, with different values in some cases. + */ +#define SIGAR_IFF_UP 0x1 +#define SIGAR_IFF_BROADCAST 0x2 +#define SIGAR_IFF_DEBUG 0x4 +#define SIGAR_IFF_LOOPBACK 0x8 +#define SIGAR_IFF_POINTOPOINT 0x10 +#define SIGAR_IFF_NOTRAILERS 0x20 +#define SIGAR_IFF_RUNNING 0x40 +#define SIGAR_IFF_NOARP 0x80 +#define SIGAR_IFF_PROMISC 0x100 +#define SIGAR_IFF_ALLMULTI 0x200 +#define SIGAR_IFF_MULTICAST 0x800 + +#define SIGAR_NULL_HWADDR "00:00:00:00:00:00" + +typedef struct { + char name[16]; + char hwaddr[64]; + sigar_uint64_t + address, + destination, + broadcast, + netmask, + flags, + mtu, + metric; +} sigar_net_interface_config_t; + +SIGAR_DECLARE(int) +sigar_net_interface_config_get(sigar_t *sigar, + const char *name, + sigar_net_interface_config_t *ifconfig); + +typedef struct { + sigar_uint64_t + /* received */ + rx_packets, + rx_bytes, + rx_errors, + rx_dropped, + rx_overruns, + rx_frame, + /* transmitted */ + tx_packets, + tx_bytes, + tx_errors, + tx_dropped, + tx_overruns, + tx_collisions, + tx_carrier; +} sigar_net_interface_stat_t; + +SIGAR_DECLARE(int) +sigar_net_interface_stat_get(sigar_t *sigar, + const char *name, + sigar_net_interface_stat_t *ifstat); + +typedef struct { + unsigned long number; + unsigned long size; + char **data; +} sigar_net_interface_list_t; + +SIGAR_DECLARE(int) +sigar_net_interface_list_get(sigar_t *sigar, + sigar_net_interface_list_t *iflist); + +SIGAR_DECLARE(int) +sigar_net_interface_list_destroy(sigar_t *sigar, + sigar_net_interface_list_t *iflist); + +#define SIGAR_NETCONN_CLIENT 0x01 +#define SIGAR_NETCONN_SERVER 0x02 + +#define SIGAR_NETCONN_TCP 0x10 +#define SIGAR_NETCONN_UDP 0x20 +#define SIGAR_NETCONN_RAW 0x40 +#define SIGAR_NETCONN_UNIX 0x80 + +#ifndef INET6_ADDRSTRLEN +# define INET6_ADDRSTRLEN 46 +#endif + +typedef struct { + unsigned long local_port; + char local_address[INET6_ADDRSTRLEN]; + unsigned long remote_port; + char remote_address[INET6_ADDRSTRLEN]; + sigar_uid_t uid; + unsigned long inode; + int type; +} sigar_net_connection_t; + +typedef struct { + unsigned long number; + unsigned long size; + sigar_net_connection_t *data; +} sigar_net_connection_list_t; + +SIGAR_DECLARE(int) +sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags); + +SIGAR_DECLARE(int) +sigar_net_connection_list_destroy(sigar_t *sigar, + sigar_net_connection_list_t *connlist); + +SIGAR_DECLARE(const char *)sigar_net_connection_type_get(int type); + +SIGAR_DECLARE(int) sigar_proc_port_get(sigar_t *sigar, unsigned long port, + sigar_pid_t *pid); + +#define SIGAR_INET_ADDR_LEN (3 * 4 + 3 + 1) + +SIGAR_DECLARE(int) sigar_inet_ntoa(sigar_t *sigar, + sigar_uint64_t address, + char *addr_str); + +#define SIGAR_FQDN_LEN 512 + +SIGAR_DECLARE(int) sigar_fqdn_get(sigar_t *sigar, char *name, int namelen); + +SIGAR_DECLARE(char *) sigar_password_get(const char *prompt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/sigar_fileinfo.h b/include/sigar_fileinfo.h new file mode 100644 index 00000000..0e379f5b --- /dev/null +++ b/include/sigar_fileinfo.h @@ -0,0 +1,134 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +#include "sigar.h" + +typedef enum { + SIGAR_FILETYPE_NOFILE = 0, /**< no file type determined */ + SIGAR_FILETYPE_REG, /**< a regular file */ + SIGAR_FILETYPE_DIR, /**< a directory */ + SIGAR_FILETYPE_CHR, /**< a character device */ + SIGAR_FILETYPE_BLK, /**< a block device */ + SIGAR_FILETYPE_PIPE, /**< a FIFO / pipe */ + SIGAR_FILETYPE_LNK, /**< a symbolic link */ + SIGAR_FILETYPE_SOCK, /**< a [unix domain] socket */ + SIGAR_FILETYPE_UNKFILE /**< a file of some other unknown type */ +} sigar_file_type_e; + +#define SIGAR_UREAD 0x0400 /**< Read by user */ +#define SIGAR_UWRITE 0x0200 /**< Write by user */ +#define SIGAR_UEXECUTE 0x0100 /**< Execute by user */ + +#define SIGAR_GREAD 0x0040 /**< Read by group */ +#define SIGAR_GWRITE 0x0020 /**< Write by group */ +#define SIGAR_GEXECUTE 0x0010 /**< Execute by group */ + +#define SIGAR_WREAD 0x0004 /**< Read by others */ +#define SIGAR_WWRITE 0x0002 /**< Write by others */ +#define SIGAR_WEXECUTE 0x0001 /**< Execute by others */ + +typedef struct { + /** The access permissions of the file. Mimics Unix access rights. */ + sigar_uint64_t permissions; + sigar_file_type_e type; + /** The user id that owns the file */ + sigar_uid_t uid; + /** The group id that owns the file */ + sigar_gid_t gid; + /** The inode of the file. */ + sigar_uint64_t inode; + /** The id of the device the file is on. */ + sigar_uint64_t device; + /** The number of hard links to the file. */ + sigar_uint64_t nlink; + /** The size of the file */ + sigar_uint64_t size; + /** The time the file was last accessed */ + sigar_uint64_t atime; + /** The time the file was last modified */ + sigar_uint64_t mtime; + /** The time the file was last changed */ + sigar_uint64_t ctime; +} sigar_file_attrs_t; + +typedef struct { + sigar_uint64_t total; + sigar_uint64_t files; + sigar_uint64_t subdirs; + sigar_uint64_t symlinks; + sigar_uint64_t chrdevs; + sigar_uint64_t blkdevs; + sigar_uint64_t sockets; +} sigar_dir_stat_t; + +SIGAR_DECLARE(const char *) +sigar_file_attrs_type_string_get(sigar_file_type_e type); + +SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs); + +SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs); + +SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions); + +SIGAR_DECLARE(char *) +sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions, + char *str); + +SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar, + const char *dir, + sigar_dir_stat_t *dirstats); diff --git a/include/sigar_getline.h b/include/sigar_getline.h new file mode 100644 index 00000000..f5bbc7c4 --- /dev/null +++ b/include/sigar_getline.h @@ -0,0 +1,18 @@ +#ifndef SIGAR_GETLINE_H +#define SIGAR_GETLINE_H + +#include "sigar.h" + +typedef int (*sigar_getline_completer_t)(char *, int, int *); + +SIGAR_DECLARE(char *) sigar_getline(char *prompt); +SIGAR_DECLARE(void) sigar_getline_setwidth(int width); +SIGAR_DECLARE(void) sigar_getline_redraw(void); +SIGAR_DECLARE(void) sigar_getline_reset(void); +SIGAR_DECLARE(void) sigar_getline_windowchanged(); +SIGAR_DECLARE(void) sigar_getline_histinit(char *file); +SIGAR_DECLARE(void) sigar_getline_histadd(char *buf); +SIGAR_DECLARE(int) sigar_getline_eof(); +SIGAR_DECLARE(void) sigar_getline_completer_set(sigar_getline_completer_t func); + +#endif /* SIGAR_GETLINE_H */ diff --git a/include/sigar_log.h b/include/sigar_log.h new file mode 100644 index 00000000..d7bd0d79 --- /dev/null +++ b/include/sigar_log.h @@ -0,0 +1,49 @@ +#ifndef SIGAR_LOG_H +#define SIGAR_LOG_H + +#include + +#define SIGAR_LOG_FATAL 0 +#define SIGAR_LOG_ERROR 1 +#define SIGAR_LOG_WARN 2 +#define SIGAR_LOG_INFO 3 +#define SIGAR_LOG_DEBUG 4 +#define SIGAR_LOG_TRACE 5 + +#define SIGAR_LOG_IS_FATAL(sigar) \ + (sigar->log_level >= SIGAR_LOG_FATAL) + +#define SIGAR_LOG_IS_ERROR(sigar) \ + (sigar->log_level >= SIGAR_LOG_ERROR) + +#define SIGAR_LOG_IS_WARN(sigar) \ + (sigar->log_level >= SIGAR_LOG_WARN) + +#define SIGAR_LOG_IS_INFO(sigar) \ + (sigar->log_level >= SIGAR_LOG_INFO) + +#define SIGAR_LOG_IS_DEBUG(sigar) \ + (sigar->log_level >= SIGAR_LOG_DEBUG) + +#define SIGAR_LOG_IS_TRACE(sigar) \ + (sigar->log_level >= SIGAR_LOG_TRACE) + +typedef void (*sigar_log_impl_t)(sigar_t *, void *, int, char *); + +SIGAR_DECLARE(void) sigar_log_printf(sigar_t *sigar, int level, + const char *format, ...); + +SIGAR_DECLARE(void) sigar_log(sigar_t *sigar, int level, char *message); + +SIGAR_DECLARE(void) sigar_log_impl_set(sigar_t *sigar, void *data, + sigar_log_impl_t impl); + +SIGAR_DECLARE(void) sigar_log_impl_file(sigar_t *sigar, void *data, + int level, char *message); + +SIGAR_DECLARE(int) sigar_log_level_get(sigar_t *sigar); + +SIGAR_DECLARE(void) sigar_log_level_set(sigar_t *sigar, int level); + + +#endif /* SIGAR_LOG_H */ diff --git a/include/sigar_private.h b/include/sigar_private.h new file mode 100644 index 00000000..f56c724e --- /dev/null +++ b/include/sigar_private.h @@ -0,0 +1,193 @@ +#ifndef SIGAR_PRIVATE_DOT_H +#define SIGAR_PRIVATE_DOT_H + +#include "sigar_log.h" + +#include +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +/* common to all os sigar_t's */ +/* XXX: this is ugly; but don't want the same stuffs + * duplicated on 4 platforms and am too lazy to change + * sigar_t to the way it was originally where sigar_t was + * common and contained a sigar_os_t. + * feel free trav ;-) + */ +#define SIGAR_T_BASE \ + int log_level; \ + void *log_data; \ + sigar_log_impl_t log_impl; \ + unsigned int ncpu; \ + unsigned long version; \ + unsigned long boot_time; \ + int ticks; \ + sigar_pid_t pid; \ + char errbuf[256]; \ + char *ifconf_buf; \ + int ifconf_len + +#if defined(WIN32) +# define SIGAR_INLINE __inline +#elif defined(__GNUC__) +# define SIGAR_INLINE inline +#else +# define SIGAR_INLINE +#endif + +#define SIGAR_ZERO(s) \ + memset(s, '\0', sizeof(*(s))) + +#define SIGAR_STRNCPY(dest, src, len) \ + strncpy(dest, src, len); \ + dest[len-1] = '\0' + +/* we use fixed size buffers pretty much everywhere */ +/* this is strncpy + ensured \0 terminator */ +#define SIGAR_SSTRCPY(dest, src) \ + SIGAR_STRNCPY(dest, src, sizeof(dest)) + +#ifndef strEQ +#define strEQ(s1, s2) (strcmp(s1, s2) == 0) +#endif + +#ifndef strnEQ +#define strnEQ(s1, s2, n) (strncmp(s1, s2, n) == 0) +#endif + +#define SIGAR_LAST_PROC_EXPIRE 2 + +#define SIGAR_FS_MAX 10 + +#define SIGAR_CPU_INFO_MAX 4 + +#define SIGAR_CPU_LIST_MAX 4 + +#define SIGAR_PROC_LIST_MAX 256 + +#define SIGAR_PROC_ARGS_MAX 12 + +#define SIGAR_NET_ROUTE_LIST_MAX 6 + +#define SIGAR_NET_IFLIST_MAX 20 + +#define SIGAR_NET_CONNLIST_MAX 20 + +int sigar_os_open(sigar_t **sigar); + +int sigar_os_close(sigar_t *sigar); + +char *sigar_os_error_string(int err); + +int sigar_proc_list_create(sigar_proc_list_t *proclist); + +int sigar_proc_list_grow(sigar_proc_list_t *proclist); + +#define SIGAR_PROC_LIST_GROW(proclist) \ + if (proclist->number >= proclist->size) { \ + sigar_proc_list_grow(proclist); \ + } + +int sigar_proc_args_create(sigar_proc_args_t *procargs); + +int sigar_proc_args_grow(sigar_proc_args_t *procargs); + +#define SIGAR_PROC_ARGS_GROW(procargs) \ + if (procargs->number >= procargs->size) { \ + sigar_proc_args_grow(procargs); \ + } + +int sigar_file_system_list_create(sigar_file_system_list_t *fslist); + +int sigar_file_system_list_grow(sigar_file_system_list_t *fslist); + +#define SIGAR_FILE_SYSTEM_LIST_GROW(fslist) \ + if (fslist->number >= fslist->size) { \ + sigar_file_system_list_grow(fslist); \ + } + +int sigar_os_fs_type_get(sigar_file_system_t *fsp); + +/* os plugins that set fsp->type call fs_type_get directly */ +#define sigar_fs_type_init(fsp) \ + fsp->type = SIGAR_FSTYPE_UNKNOWN; \ + sigar_fs_type_get(fsp) + +void sigar_fs_type_get(sigar_file_system_t *fsp); + +int sigar_cpu_infos_create(sigar_cpu_infos_t *cpu_infos); + +int sigar_cpu_infos_grow(sigar_cpu_infos_t *cpu_infos); + +#define SIGAR_CPU_INFOS_GROW(cpu_infos) \ + if (cpu_infos->number >= cpu_infos->size) { \ + sigar_cpu_infos_grow(cpu_infos); \ + } + +int sigar_cpu_list_create(sigar_cpu_list_t *cpulist); + +int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist); + +#define SIGAR_CPU_LIST_GROW(cpulist) \ + if (cpulist->number >= cpulist->size) { \ + sigar_cpu_list_grow(cpulist); \ + } + +int sigar_net_route_list_create(sigar_net_route_list_t *routelist); + +int sigar_net_route_list_grow(sigar_net_route_list_t *net_routelist); + +#define SIGAR_NET_ROUTE_LIST_GROW(routelist) \ + if (routelist->number >= routelist->size) { \ + sigar_net_route_list_grow(routelist); \ + } + +int sigar_net_interface_list_create(sigar_net_interface_list_t *iflist); + +int sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist); + +#define SIGAR_NET_IFLIST_GROW(iflist) \ + if (iflist->number >= iflist->size) { \ + sigar_net_interface_list_grow(iflist); \ + } + +int sigar_net_connection_list_create(sigar_net_connection_list_t *connlist); + +int sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist); + +#define SIGAR_NET_CONNLIST_GROW(connlist) \ + if (connlist->number >= connlist->size) { \ + sigar_net_connection_list_grow(connlist); \ + } + +void sigar_hwaddr_format(char *buff, unsigned char *ptr); + +#define sigar_hwaddr_set_null(ifconfig) \ + memcpy(ifconfig->hwaddr, SIGAR_NULL_HWADDR, sizeof(SIGAR_NULL_HWADDR)) + +int sigar_user_id_get(sigar_t *sigar, const char *name, int *uid); + +int sigar_user_name_get(sigar_t *sigar, int uid, char *buf, int buflen); + +int sigar_group_name_get(sigar_t *sigar, int gid, char *buf, int buflen); + +#define SIGAR_PROC_ENV_KEY_LOOKUP() \ + if ((procenv->type == SIGAR_PROC_ENV_KEY) && \ + (pid == sigar->pid)) \ + { \ + char *value = getenv(procenv->key); \ + if (value != NULL) { \ + procenv->env_getter(procenv->data, \ + procenv->key, \ + procenv->klen, \ + value, strlen(value)); \ + } \ + return SIGAR_OK; \ + } + +#endif diff --git a/include/sigar_util.h b/include/sigar_util.h new file mode 100644 index 00000000..a979e2f3 --- /dev/null +++ b/include/sigar_util.h @@ -0,0 +1,70 @@ +#ifndef SIGAR_UTIL_H +#define SIGAR_UTIL_H + +/* most of this is crap for dealing with linux /proc */ +#define UITOA_BUFFER_SIZE \ + (sizeof(int) * 3 + 1) + +#define SSTRLEN(s) \ + (sizeof(s)-1) + +#define sigar_strtoul(ptr) \ + strtoul(ptr, &ptr, 10) + +#define sigar_isspace(c) \ + (isspace(((unsigned char)(c)))) + +#define sigar_isdigit(c) \ + (isdigit(((unsigned char)(c)))) + +#define sigar_isalpha(c) \ + (isalpha(((unsigned char)(c)))) + +#define PROC_FS_ROOT "/proc/" + +char *sigar_uitoa(char *buf, unsigned int n, int *len); + +SIGAR_INLINE char *sigar_skip_line(char *buffer, int buflen); + +SIGAR_INLINE char *sigar_skip_token(char *p); + +SIGAR_INLINE char *sigar_skip_multiple_token(char *p, int count); + +int sigar_file2str(const char *fname, char *buffer, int buflen); + +int sigar_proc_file2str(char *buffer, int buflen, + sigar_pid_t pid, + const char *fname, + int fname_len); + +#define SIGAR_PROC_FILE2STR(buffer, pid, fname) \ + sigar_proc_file2str(buffer, sizeof(buffer), \ + pid, fname, SSTRLEN(fname)) + +#define SIGAR_PROC_FILENAME(buffer, pid, fname) \ + sigar_proc_filename(buffer, sizeof(buffer), \ + pid, fname, SSTRLEN(fname)) + +#define SIGAR_SKIP_SPACE(ptr) \ + while (sigar_isspace(*ptr)) ++ptr + +char *sigar_proc_filename(char *buffer, int buflen, + sigar_pid_t pid, + const char *fname, int fname_len); + +int sigar_proc_list_procfs_get(sigar_t *sigar, + sigar_proc_list_t *proclist); + +int sigar_proc_fd_count(sigar_t *sigar, sigar_pid_t pid, + sigar_uint64_t *total); + +/* generic util functions for all platforms */ + +int sigar_proc_count(sigar_t *sigar, sigar_uint64_t *total); + +int sigar_mem_calc_ram(sigar_t *sigar, sigar_mem_t *mem); + +double sigar_file_system_usage_calc_used(sigar_t *sigar, + sigar_file_system_usage_t *fs); + +#endif /* SIGAR_UTIL_H */ diff --git a/src/os/aix/aix_sigar.c b/src/os/aix/aix_sigar.c new file mode 100644 index 00000000..dfbec4e7 --- /dev/null +++ b/src/os/aix/aix_sigar.c @@ -0,0 +1,1161 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "user_v5.h" +#include "utmp_v5.h" + +#define FIXED_TO_DOUBLE(x) (((double)x) / 65536.0) + +/* these offsets wont change so just lookup them up during open */ +static int get_koffsets(sigar_t *sigar) +{ + int i; + /* see man knlist and nlist.h */ + struct nlist klist[] = { + {"avenrun", 0, 0, 0, 0, 0}, /* KOFFSET_LOADAVG */ + {"v", 0, 0, 0, 0, 0}, /* KOFFSET_VAR */ + {"sysinfo", 0, 0, 0, 0, 0}, /* KOFFSET_SYSINFO */ + {"ifnet", 0, 0, 0, 0, 0}, /* KOFFSET_IFNET */ + {"vmminfo", 0, 0, 0, 0, 0}, /* KOFFSET_VMINFO */ + {"cpuinfo", 0, 0, 0, 0, 0}, /* KOFFSET_CPUINFO */ + {NULL, 0, 0, 0, 0, 0} + }; + + if (knlist(klist, + sizeof(klist) / sizeof(klist[0]), + sizeof(klist[0])) != 0) + { + return errno; + } + + for (i=0; ikoffsets[i] = klist[i].n_value; + } + + return SIGAR_OK; +} + +static int kread(sigar_t *sigar, void *data, int size, long offset) +{ + if (sigar->kmem < 0) { + return SIGAR_EPERM_KMEM; + } + + if (lseek(sigar->kmem, offset, SEEK_SET) != offset) { + return errno; + } + + if (read(sigar->kmem, data, size) != size) { + return errno; + } + + return SIGAR_OK; +} + +int sigar_os_open(sigar_t **sigar) +{ + int status, i; + void *dlhandle; + int kmem = -1; + vminfo_func_t vminfo = NULL; + + if ((dlhandle = dlopen("/unix", RTLD_NOW))) { + vminfo = (vminfo_func_t)dlsym(dlhandle, "vmgetinfo"); + + dlclose(dlhandle); + } + + kmem = open("/dev/kmem", O_RDONLY); + + *sigar = malloc(sizeof(**sigar)); + + (*sigar)->getvminfo = vminfo; + (*sigar)->getprocfd = NULL; /*XXX*/ + (*sigar)->kmem = kmem; + (*sigar)->pagesize = 0; + (*sigar)->boot_time = 0; + (*sigar)->last_pid = -1; + (*sigar)->pinfo = NULL; + (*sigar)->cpuinfo = NULL; + (*sigar)->cpuinfo_size = 0; + SIGAR_ZERO(&(*sigar)->swaps); + + i = getpagesize(); + while ((i >>= 1) > 0) { + (*sigar)->pagesize++; + } + + if (kmem > 0) { + if ((status = get_koffsets(*sigar)) != SIGAR_OK) { + free(*sigar); + return status; + } + } + + return SIGAR_OK; +} + +static void swaps_free(swaps_t *swaps); + +int sigar_os_close(sigar_t *sigar) +{ + swaps_free(&sigar->swaps); + if (sigar->kmem > 0) { + close(sigar->kmem); + } + if (sigar->pinfo) { + free(sigar->pinfo); + } + if (sigar->cpuinfo) { + free(sigar->cpuinfo); + } + free(sigar); + return SIGAR_OK; +} + +char *sigar_os_error_string(int err) +{ + switch (err) { + case SIGAR_EPERM_KMEM: + return "Failed to open /dev/kmem for reading"; + default: + return NULL; + } +} + +#define PAGESHIFT(v) \ + ((v) << sigar->pagesize) + +int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ + struct vminfo vm; + +#if 0 + /* XXX: wtf, this is supposed to be a modern way + * to get the same values below. yet it works on 4.3.3 + * but not 5.1 + */ + if (!sigar->getvminfo) { + return EPERM; + } + + if (sigar->getvminfo(&vm, VMINFO, sizeof(vm)) != 0) { + return errno; + } + +#else + int status; + + status = kread(sigar, &vm, sizeof(vm), + sigar->koffsets[KOFFSET_VMINFO]); + + if (status != SIGAR_OK) { + return status; + } +#endif + + mem->total = PAGESHIFT(vm.memsizepgs); /* lsattr -El sys0 -a realmem */ + mem->free = PAGESHIFT(vm.numfrb); + mem->used = mem->total - mem->free; + + mem->shared = -1; + mem->buffer = -1; + mem->cached = -1; + + sigar_mem_calc_ram(sigar, mem); + + return SIGAR_OK; +} + +static void swaps_free(swaps_t *swaps) +{ + if (swaps->num) { + int i; + + for (i=0; inum; i++) { + free(swaps->devs[i]); + } + + free(swaps->devs); + + swaps->num = 0; + } +} + +/* + * there is no public api for parsing this file. + * well, there is something, but its super ugly and requires + * linking 2 static libraries (libodm and something else) + * maybe will switch to that if it can add value elsewhere too. + */ +#define SWAPSPACES "/etc/swapspaces" + +static int swaps_get(swaps_t *swaps) +{ + FILE *fp; + char buf[512]; + char *ptr; + struct stat statbuf; + + if (stat(SWAPSPACES, &statbuf) < 0) { + return errno; + } + + /* only re-parse if file has changed */ + if (swaps->mtime == statbuf.st_mtime) { + return 0; + } + + swaps->mtime = statbuf.st_mtime; + + /* easier to just start from scratch */ + swaps_free(swaps); + + if (!(fp = fopen(SWAPSPACES, "r"))) { + return errno; + } + + while ((ptr = fgets(buf, sizeof(buf), fp))) { + if (!isalpha(*ptr)) { + continue; + } + + if (strchr(ptr, ':')) { + int len; + + ptr = fgets(buf, sizeof(buf), fp); + + while (isspace(*ptr)) { + ++ptr; + } + + if (strncmp(ptr, "dev", 3)) { + continue; + } + ptr += 3; + while (isspace(*ptr) || (*ptr == '=')) { + ++ptr; + } + + len = strlen(ptr); + ptr[len-1] = '\0'; /* -1 == chomp \n */ + + swaps->devs = realloc(swaps->devs, swaps->num+1 * sizeof(char *)); + swaps->devs[swaps->num] = malloc(len); + memcpy(swaps->devs[swaps->num], ptr, len); + + swaps->num++; + } + } + + fclose(fp); + + return 0; +} + +/* + * documented in aix tech ref, + * but this prototype is not in any friggin header file. + * struct pginfo is in sys/vminfo.h + */ + +int swapqry(char *path, struct pginfo *info); + +int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ + int status, i; + + if ((status = swaps_get(&sigar->swaps)) != SIGAR_OK) { + return status; + } + + swap->total = swap->free = 0; + + for (i=0; iswaps.num; i++) { + struct pginfo info; + + status = swapqry(sigar->swaps.devs[i], &info); + + if (status != 0) { + return errno; + } + + swap->total += PAGESHIFT(info.size); /* lsps -a */ + swap->free += PAGESHIFT(info.free); + } + + swap->used = swap->total - swap->free; + + return SIGAR_OK; +} + +int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ + int i, status; + struct sysinfo data; + + status = kread(sigar, &data, sizeof(data), + sigar->koffsets[KOFFSET_SYSINFO]); + + if (status != SIGAR_OK) { + return status; + } + + cpu->user = data.cpu[CPU_USER]; + cpu->nice = -1; /* N/A */ + cpu->sys = data.cpu[CPU_KERNEL]; + cpu->idle = data.cpu[CPU_IDLE]; + cpu->total = 0; + + for (i=0; itotal += data.cpu[i]; + } + + return SIGAR_OK; +} + +/* + * other possible metrics we could add: + * struct cpuinfo { + * long cpu[CPU_NTIMES]; + * long pswitch; + * long syscall; + * long sysread; + * long syswrite; + * long sysfork; + * long sysexec; + * long readch; + * long writech; + * long iget; + * long namei; + * long dirblk; + * long msg; + * long sema; + * long bread; + * long bwrite; + * long lread; + * long lwrite; + * long phread; + * long phwrite; + * }; + */ + +int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + int status, i, j; + int ncpu = _system_configuration.ncpus; /* this can change */ + int size = ncpu * sizeof(struct cpuinfo); + + if (sigar->cpuinfo_size < size) { + sigar->cpuinfo = realloc(sigar->cpuinfo, size); + } + + status = kread(sigar, sigar->cpuinfo, size, + sigar->koffsets[KOFFSET_CPUINFO]); + + if (status != SIGAR_OK) { + return status; + } + + sigar_cpu_list_create(cpulist); + + for (i=0; idata[cpulist->number++]; + + info = &sigar->cpuinfo[i]; + cpu->user = info->cpu[CPU_USER]; + cpu->nice = 0; /* N/A */ + cpu->sys = info->cpu[CPU_KERNEL]; + cpu->idle = info->cpu[CPU_IDLE]; + cpu->total = 0; + + for (j=0; jtotal += info->cpu[j]; + } + } + + return SIGAR_OK; +} + +static int boot_time_v4(int fd, time_t *time) +{ + struct utmp data; + + do { + if (read(fd, &data, sizeof(data)) != sizeof(data)) { + return errno; + } + } while (data.ut_type != BOOT_TIME); + + *time = data.ut_time; + + return SIGAR_OK; +} + +static int boot_time_v5(int fd, time_t *time) +{ + struct utmp_v5 data; + + do { + if (read(fd, &data, sizeof(data)) != sizeof(data)) { + return errno; + } + } while (data.ut_type != BOOT_TIME); + + *time = data.ut_time; + + return SIGAR_OK; +} + +static int boot_time(time_t *time) +{ + struct utmp_v5 data_v5; + int utmp, status; + + if ((utmp = open(UTMP_FILE, O_RDONLY)) < 0) { + return errno; + } + + if ((status = boot_time_v4(utmp, time)) != SIGAR_OK) { + lseek(utmp, 0, SEEK_SET); + status = boot_time_v5(utmp, time); + } + + close(utmp); + + return status; +} + +int sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + if (sigar->boot_time == 0) { + int status; + time_t time; + + if ((status = boot_time(&time)) != SIGAR_OK) { + return status; + } + + sigar->boot_time = time; + } + + uptime->uptime = time(NULL) - sigar->boot_time; + uptime->idletime = -1; + + return SIGAR_OK; +} + +int sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + int status, i; + int data[3]; + + status = kread(sigar, &data, sizeof(data), + sigar->koffsets[KOFFSET_LOADAVG]); + + if (status != SIGAR_OK) { + return status; + } + + for (i=0; i<3; i++) { + loadavg->loadavg[i] = FIXED_TO_DOUBLE(data[i]); + } + + return SIGAR_OK; +} + +int sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + pid_t pid = 0; + struct procsinfo info; + + sigar_proc_list_create(proclist); + + for (;;) { + int num = getprocs(&info, sizeof(info), + NULL, 0, &pid, 1); + + if (num == 0) { + break; + } + + SIGAR_PROC_LIST_GROW(proclist); + + proclist->data[proclist->number++] = info.pi_pid; + } + + return SIGAR_OK; +} + +int sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +static int sigar_getprocs(sigar_t *sigar, sigar_pid_t pid) +{ + int status, num; + time_t timenow = time(NULL); + + if (sigar->pinfo == NULL) { + sigar->pinfo = malloc(sizeof(*sigar->pinfo)); + } + + if (sigar->last_pid == pid) { + if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { + return SIGAR_OK; + } + } + + sigar->last_pid = pid; + sigar->last_getprocs = timenow; + + num = getprocs(sigar->pinfo, sizeof(*sigar->pinfo), + NULL, 0, &pid, 1); + + if (num != 1) { + return ESRCH; + } + + return SIGAR_OK; +} + +int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + int status = sigar_getprocs(sigar, pid); + struct procsinfo *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + procmem->size = PAGESHIFT(pinfo->pi_size); + procmem->vsize = PAGESHIFT(pinfo->pi_dvm); + procmem->share = PAGESHIFT(pinfo->pi_sdsize); + procmem->rss = PAGESHIFT(pinfo->pi_drss + pinfo->pi_trss); + procmem->resident = -1; /* N/A */ + + return SIGAR_OK; +} + +int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + int status = sigar_getprocs(sigar, pid); + struct procsinfo *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + proccred->uid = pinfo->pi_cred.cr_ruid; + proccred->euid = pinfo->pi_cred.cr_uid; + if (proccred->uid == -1) { + /* + * aix 5.2 has a process named 'jfsz' + * where uid is '-1', getpwuid returns EPERM + */ + proccred->uid = proccred->euid = 0; + } + proccred->gid = pinfo->pi_cred.cr_rgid; + proccred->egid = pinfo->pi_cred.cr_gid; + + return SIGAR_OK; +} + +int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + int status = sigar_getprocs(sigar, pid); + struct procsinfo *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + proctime->start_time = pinfo->pi_start; + proctime->start_time *= 1000; /* convert to ms */ + proctime->utime = pinfo->pi_utime; + proctime->stime = pinfo->pi_stime; + + return SIGAR_OK; +} + +int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + int status = sigar_getprocs(sigar, pid); + struct procsinfo *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + SIGAR_SSTRCPY(procstate->name, pinfo->pi_comm); + procstate->ppid = pinfo->pi_ppid; + procstate->nice = pinfo->pi_nice; + procstate->tty = pinfo->pi_ttyd; + procstate->priority = -1; /* XXX getthrds() */ + + switch (pinfo->pi_state) { + case SACTIVE: + procstate->state = 'R'; + break; + case SIDL: + procstate->state = 'D'; + break; + case SSTOP: + procstate->state = 'S'; + break; + case SZOMB: + procstate->state = 'Z'; + break; + case SSWAP: + procstate->state = 'S'; + break; + } + + return SIGAR_OK; +} + +int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + /* XXX if buffer is not large enough args are truncated */ + char buffer[8192], *ptr; + struct procinfo pinfo; + + pinfo.pi_pid = pid; + + if (getargs(&pinfo, sizeof(pinfo), + buffer, sizeof(buffer)) != 0) + { + return errno; + } + + sigar_proc_args_create(procargs); + + ptr = buffer; + + while (*ptr) { + int alen = strlen(ptr)+1; + char *arg = malloc(alen); + + SIGAR_PROC_ARGS_GROW(procargs); + memcpy(arg, ptr, alen); + + procargs->data[procargs->number++] = arg; + + ptr += alen; + } + + return SIGAR_OK; +} + +int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + /* XXX if buffer is not large enough args are truncated */ + char buffer[8192], *ptr; + struct procinfo pinfo; + + pinfo.pi_pid = pid; + + if (getevars(&pinfo, sizeof(pinfo), + buffer, sizeof(buffer)) != 0) + { + return errno; + } + + ptr = buffer; + + while (*ptr) { + char *val = strchr(ptr, '='); + int klen, vlen, status; + char key[128]; /* XXX is there a max key size? */ + + if (val == NULL) { + /* not key=val format */ + procenv->env_getter(procenv->data, ptr, strlen(ptr), NULL, 0); + break; + } + + klen = val - ptr; + SIGAR_SSTRCPY(key, ptr); + key[klen] = '\0'; + ++val; + + vlen = strlen(val); + status = procenv->env_getter(procenv->data, + key, klen, val, vlen); + + if (status != SIGAR_OK) { + /* not an error; just stop iterating */ + break; + } + + ptr += (klen + 1 + vlen + 1); + } + + return SIGAR_OK; +} + +/* + * V[45]_sigar_proc_fd_get routines are exactly + * the same except for sizeof(uinfo). + */ +static int V5_sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + int i; + struct procinfo pinfo; + struct user_v5 uinfo; /* V5 */ + + procfd->total = 0; + pinfo.pi_pid = pid; + + if (getuser(&pinfo, sizeof(pinfo), + &uinfo, sizeof(uinfo)) != 0) { + if (errno == EINVAL) { + return SIGAR_ENOTIMPL; + } + return errno; + } + + /* see sys/user.h */ + for (i=0; itotal++; + } + } + + return SIGAR_OK; +} + +static int V4_sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + int i; + struct procinfo pinfo; + struct user uinfo; /* V4 */ + + procfd->total = 0; + pinfo.pi_pid = pid; + + if (getuser(&pinfo, sizeof(pinfo), + &uinfo, sizeof(uinfo)) != 0) { + if (errno == EINVAL) { + return errno; + } + } + + /* see sys/user.h */ + for (i=0; itotal++; + } + } + + return SIGAR_OK; +} + +int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + if (sigar->getprocfd == NULL) { + /* + * XXX should determine aix version in sigar_os_open + * and set function pointer there. for now try v4 + * first, if that fails try v5. only costs 1 extra + * call to getuser on v5 for the lifetime of the + * sigar. + */ + int status = V4_sigar_proc_fd_get(sigar, pid, procfd); + + if (status == SIGAR_OK) { + sigar->getprocfd = V4_sigar_proc_fd_get; + return SIGAR_OK; + } + + sigar->getprocfd = V5_sigar_proc_fd_get; + } + + return sigar->getprocfd(sigar, pid, procfd); +} + +int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + return fsp->type; +} + +/* another one documented in aix tech ref + * with no friggin prototype in any header file. + */ +int mntctl(int command, int size, char *buffer); + +int sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + int i, size, num; + char *buf, *mntlist; + + /* get required size */ + if (mntctl(MCTL_QUERY, sizeof(size), (char *)&size) < 0) { + return errno; + } + + mntlist = buf = malloc(size); + + if ((num = mntctl(MCTL_QUERY, size, buf)) < 0) { + free(buf); + return errno; + } + + sigar_file_system_list_create(fslist); + + for (i=0; ivmt_length; + + SIGAR_FILE_SYSTEM_LIST_GROW(fslist); + + fsp = &fslist->data[fslist->number++]; + + switch (ent->vmt_gfstype) { + case MNT_AIX: + typename = "aix"; + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + break; + case MNT_JFS: + typename = "jfs"; + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + break; + case MNT_NFS: + case MNT_NFS3: + typename = "nfs"; + fsp->type = SIGAR_FSTYPE_NETWORK; + break; + case MNT_CDROM: + fsp->type = SIGAR_FSTYPE_CDROM; + break; + case MNT_SFS: + case MNT_CACHEFS: + case MNT_AUTOFS: + default: + if (ent->vmt_flags & MNT_REMOTE) { + fsp->type = SIGAR_FSTYPE_NETWORK; + } + else { + fsp->type = SIGAR_FSTYPE_NONE; + } + } + + SIGAR_SSTRCPY(fsp->dir_name, vmt2dataptr(ent, VMT_STUB)); + + devname = vmt2dataptr(ent, VMT_OBJECT); + + if (fsp->type == SIGAR_FSTYPE_NETWORK) { + char *hostname = vmt2dataptr(ent, VMT_HOSTNAME); +#if 0 + /* XXX: these do not seem reliable */ + int hostname_len = vmt2datasize(ent, VMT_HOSTNAME)-1; /* -1 == skip '\0' */ + int devname_len = vmt2datasize(ent, VMT_OBJECT); /* includes '\0' */ +#else + int hostname_len = strlen(hostname); + int devname_len = strlen(devname) + 1; +#endif + int total_len = hostname_len + devname_len + 1; /* 1 == strlen(":") */ + + if (total_len > sizeof(fsp->dev_name)) { + /* justincase - prevent overflow. chances: slim..none */ + SIGAR_SSTRCPY(fsp->dev_name, devname); + } + else { + /* sprintf(fsp->devname, "%s:%s", hostname, devname) */ + char *ptr = fsp->dev_name; + + memcpy(ptr, hostname, hostname_len); + ptr += hostname_len; + + *ptr++ = ':'; + + memcpy(ptr, devname, devname_len); + } + } + else { + SIGAR_SSTRCPY(fsp->dev_name, devname); + } + + /* we set fsp->type, just looking up sigar.c:fstype_names[type] */ + sigar_fs_type_get(fsp); + + if (typename == NULL) { + typename = fsp->type_name; + } + + SIGAR_SSTRCPY(fsp->sys_type_name, typename); + } + + free(buf); + + return SIGAR_OK; +} + +/* XXX this is exactly the same as linux and hpux, solaris is darn close */ + +#define SIGAR_FS_BLOCKS_TO_BYTES(buf, f) \ + ((buf.f * (buf.f_bsize / 512)) >> 1) + +int sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + struct statfs buf; + + if (statfs((char *)dirname, &buf) != 0) { + return errno; + } + + fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_blocks); + fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bfree); + fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bavail); + fsusage->files = buf.f_files; + fsusage->free_files = buf.f_ffree; + fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); + + return SIGAR_OK; +} + +int sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + int i; + int ncpu = _system_configuration.ncpus; /* this can change */ + + /*XXX should only do this once*/ + sigar_cpu_infos_create(cpu_infos); + + for (i=0; idata[cpu_infos->number++]; + + info->cache_size = _system_configuration.L2_cache_size / 1024; + + switch (info->cache_size) { + case 1024: + info->mhz = 333; + break; + case 4096: + info->mhz = 400; + break; + case 8192: + info->mhz = 450; + break; + default: + info->mhz = -1; + break; + } + + switch (_system_configuration.architecture) { + case POWER_RS: + arch = "Power Classic"; + break; + case POWER_PC: + arch = "Power PC"; + break; + case IA64: + arch = "IA64"; + break; + default: + arch = "unknown"; + break; + } + + if (*arch == 'P') { + SIGAR_SSTRCPY(info->vendor, "IBM"); + + switch (_system_configuration.implementation) { + case POWER_RS1: + model = "RS1"; + break; + case POWER_RSC: + model = "RSC"; + break; + case POWER_RS2: + model = "RS2"; + break; + case POWER_601: + model = "601"; + break; + case POWER_603: + model = "603"; + break; + case POWER_604: + model = "604"; + break; + case POWER_620: + model = "620"; + break; + case POWER_630: + model = "630"; + break; + case POWER_A35: + model = "A35"; + break; + case POWER_RS64II: + model = "RS64II"; + break; + case POWER_RS64III: + model = "RS64III"; + break; + default: + model = "unknown"; + break; + } + } + else if (*arch == 'I') { + SIGAR_SSTRCPY(info->vendor, "Intel"); + + switch (_system_configuration.implementation) { + case IA64_M1: + model = "M1"; + break; + case IA64_M2: + model = "M2"; + break; + default: + model = "unknown"; + break; + } + } + else { + SIGAR_SSTRCPY(info->vendor, "Unknown"); + model = "unknown"; + break; + } + + snprintf(info->model, sizeof(info->model), + "%s %s", arch, model); + } + + return SIGAR_OK; +} + +int sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + sigar_net_route_t *route; + + sigar_net_route_list_create(routelist); + + return SIGAR_OK; +} + +int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + int status; + struct ifnet data; + caddr_t offset = 0; + char if_name[32]; + + status = kread(sigar, &offset, sizeof(offset), + sigar->koffsets[KOFFSET_IFNET]); + + if (status != SIGAR_OK) { + return status; + } + + for (; offset; offset = (caddr_t)data.if_next) { + status = kread(sigar, &data, sizeof(data), (long)offset); + + if (status != SIGAR_OK) { + return status; + } + + status = kread(sigar, if_name, sizeof(if_name), + (long)&data.if_name[0]); + + if (status != SIGAR_OK) { + return status; + } + + /* XXX if_name is 'en' or 'lo', not 'en0' or 'lo0' */ + if (!strnEQ(if_name, name, strlen(if_name))) { + continue; + } + + ifstat->rx_bytes = data.if_ibytes; + ifstat->rx_packets = data.if_ipackets; + ifstat->rx_errors = data.if_ierrors; + ifstat->rx_dropped = data.if_iqdrops; + ifstat->rx_overruns = -1; + ifstat->rx_frame = -1; + + ifstat->tx_bytes = data.if_obytes; + ifstat->tx_packets = data.if_opackets; + ifstat->tx_errors = data.if_oerrors; + ifstat->tx_dropped = -1; + ifstat->tx_overruns = -1; + ifstat->tx_collisions = data.if_collisions; + ifstat->tx_carrier = -1; + + return SIGAR_OK; + } + + return ENXIO; +} + +int sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + return SIGAR_ENOTIMPL; +} diff --git a/src/os/aix/sigar_os.h b/src/os/aix/sigar_os.h new file mode 100644 index 00000000..f09e1210 --- /dev/null +++ b/src/os/aix/sigar_os.h @@ -0,0 +1,49 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +#include +#include +#include +#include + +enum { + KOFFSET_LOADAVG, + KOFFSET_VAR, + KOFFSET_SYSINFO, + KOFFSET_IFNET, + KOFFSET_VMINFO, + KOFFSET_CPUINFO, + KOFFSET_MAX +}; + +typedef struct { + time_t mtime; + int num; + char **devs; +} swaps_t; + +typedef int (*vminfo_func_t) (void *, int, int); + +typedef int (*proc_fd_func_t) (sigar_t *, sigar_pid_t, sigar_proc_fd_t *); + +struct sigar_t { + SIGAR_T_BASE; + int kmem; + /* offsets for seeking on kmem */ + long koffsets[KOFFSET_MAX]; + vminfo_func_t getvminfo; + proc_fd_func_t getprocfd; + int pagesize; + swaps_t swaps; + time_t last_getprocs; + sigar_pid_t last_pid; + struct procsinfo *pinfo; + struct cpuinfo *cpuinfo; + int cpuinfo_size; +}; + +#define HAVE_STRERROR_R + +#define SIGAR_EPERM_KMEM (SIGAR_OS_START_ERROR+1) + +#endif /* SIGAR_OS_H */ diff --git a/src/os/aix/user_v5.h b/src/os/aix/user_v5.h new file mode 100644 index 00000000..5652bc0b --- /dev/null +++ b/src/os/aix/user_v5.h @@ -0,0 +1,312 @@ +/* + * struct user is not binary compatible + * between 4.3 and 5.x + * this structure is taken from 5.1 sys/user.h + * and defines from sys/user.h and sys/types.h + * we compile this on v5. + * XXX: will not compile 5.x if we want to compile + * on 5.x would need to create a struct user_v4 + * to maintain bincompt. + */ + +#define V5_NISIG 3 /* v4 == 2 */ + +#define SIGMAX64 255 +#define SIGMAX32 63 +#ifdef __64BIT__ +#define V5_SIGMAX SIGMAX64 +#else +#define V5_SIGMAX SIGMAX32 +#endif + +#define NSIG64 (SIGMAX64+1) +#define NSIG32 (SIGMAX32+1) +#ifdef __64BIT__ +#define V5_NSIG NSIG64 +#else +#define V5_NSIG NSIG32 +#endif + +#define WLM_TAG_LENGTH 30 +#define U_FD_LCK_CNT 16 + +typedef vmhandle_t vmlpghandle_t; + +typedef struct { +#ifdef _ALL_SOURCE + unsigned int losigs, hisigs; +#else + unsigned int __losigs, __hisigs; +#endif +} sigset32_t; + +struct user_v5 { + + /* swappable process context */ + unsigned long long U_chk_paddr; /* address of checkpnt_pending var */ + struct saioq_head *U_saioq_head;/* anchor for async socket queue */ + uid_t U_sv_sgid; /* set group identifier at exec time */ + int U_num_pmsegs;/* number of PM segments */ + vmhandle_t *U_pm_segs; /* PM segments */ + vmhandle_t U_pm_space; /* space for first PM segment */ + vmhandle_t U_text_vmh; /* text seg vmhandle for memusage */ + unsigned long long U_cancel_func;/* cancelation user entry point */ + unsigned long long U_cancel_toc;/* cancelation user entry point */ + struct proc *U_procp; /* pointer to proc structure */ + Simple_lock U_handy_lock; /* self preservation lock */ + +#ifdef __64BIT_KERNEL +#ifdef _POWER + /* U_segs32 must be pinned and must not cross a page boundary */ + void *U_segs32_raddr; /* real addr of U_segs32 */ + vmhandle_t U_segs32[NSEGS]; /* shadow of first 4Gb for resume() */ +#endif /* _POWER */ +#endif /* __64BIT_KERNEL */ + + /* signal management */ + void (*U_signal[NSIG32+V5_NISIG])(int);/* disposition of sigs */ + sigset32_t U_sigmask[NSIG32+V5_NISIG]; /* sig's to be blocked */ +#ifdef __64BIT_KERNEL + uint U_sigflags[V5_NSIG+V5_NISIG]; +#else + ushort U_sigflags[NSIG64+V5_NISIG];/* ushort-save space for now*/ +#endif /* __64BIT_KERNEL */ + + /* user-mode address space mapping */ +#ifndef __64BIT_KERNEL + adspace_t U_adspace; /* user-mode address space */ + struct segstate U_segst[NSEGS]; /* info on use of each segment */ +#else +#ifdef _POWER + void * U_uastrtp; /* strt addr for V_USERACC */ + vmsize_t U_uasize; /* size of the V_USERACC buf */ +#endif /* _POWER */ + struct asalloc U_snode; /* segment node allocation */ + struct asalloc U_unode; /* uadspace node allocation */ + struct uadnode U_adspace32[NADNODES]; /* usr adspace 32bit process */ + struct segnode U_segnode[NSEGNODES]; /* segnode for 32bit processes */ +#endif /* __64BIT_KERNEL */ + struct vmmlock U_adspace_lock; + int U_vmmflags; /* vmm process state flags */ + + /* auditing stuff */ + int U_auditstatus; /* auditing RESUME or SUSPEND */ + + /* address map (mmap) */ + char *U_map; + + /* current exec file information */ + char U_comm[MAXCOMLEN+1]; /* basename of exec file */ + + int U_ncargs; /* ARGMAX value during exec */ + + /* + * Program model information, 64bit and 32bit, small and large data. + * The 'max' values are the maximums allowed by the model, which + * may be smaller than the current resource limits. + * These fields are filled in during exec. + * + * There is no U_stksize field. The stack is allowed whatever it can + * get, subject to resource limits, model limits (possibly set from + * the a.out header during exec), and, in 32-bit mode, the PRIVSEG + * data break address. The get_stack_size routine will return the + * current maximum effective size of the stack. + * + * Other fields: + * U_breakhiwater: the high water mark for the break value + * U_minbreak: the smallest permitted break value + * U_min_mmap: If non-zero, smallest address allowed for mmap() + * or shmat() at a user-specified address. + */ + unsigned long long U_tstart; /* start of text */ + unsigned long long U_tsize; /* text size (bytes) */ + unsigned long long U_datastart; /* start of data */ + unsigned long long U_datasize; /* Current data size (bytes) */ + + /* DABR watchpoint information */ + unsigned long long U_wp_dabr; /* DABR value for ptrace watchpoint */ + unsigned long long U_wp_value; /* current value at watched address */ + + /* System call table information */ + void *U_svc_table; /* svc table address */ + int U_max_svcnum; /* max allowed svc number for process */ + + /* CHANGES FROM HERE ON DO NOT AFFECT ASSEMBLER CODE + (see ml/POWER/32user.m4) */ + + char U_pad2[32]; + + unsigned long long U_datamax; /* Maximum data size (bytes) */ + unsigned long long U_minbreak; /* minimum/initial break address */ + unsigned long long U_breakhiwater; /* Largest break address so far */ + unsigned long long U_min_mmap; /* Minimum shmat/mmap address allowed */ + + uint U_brkseg; /* segment number of U_breakhiwater */ + uint U_stkseg; /* Lowest segment number of stack */ + + unsigned long long U_stkstart; /* stack start (grows downwards) */ + unsigned long long U_stkmax; /* stack max (bytes) */ + + /* + * The following macros compute some commonly required values + * about the addressing model. + * + * U_BIGDATA Tells you whether the 32-bit large data model + * is in effect. + * U_PDATASIZE Is the current data size for a 32-bit process + * in the private segment (hence the P). + * Note - this is always the PRIVSEG data size; with + * the large data model, the general data size is + * separate, as data begins in the BDATASEG. + * U_PDATABRK Is the current data break address of a 32-bit process + * in the private segment (see above). + * U_DATABRK Is the general data break address in all cases. + * U_DSIZE Is for compatibility only. Use it to fill in fields + * that previously were copies of U_dsize, which is now + * obsolete. + */ + + unsigned int U_sdsize; /* Size of data for privately loaded + modules, if the bigdata model is + being used. */ + short U_lock; /* process/text locking flags */ + char U_64bit; /* 0 if 32-bit process, 0x1 if 64-bit */ + char U_emul; /* emulation type (UW7, etc.) */ + + /* user identification and authorization */ + Simple_lock U_cr_lock; /* credentials lock */ + struct ucred * volatile U_cred; /* user credentials (uid, gid, etc) */ + + uinfo_t U_uinfo; /* usrinfo() buffer */ + int U_compatibility;/* compatibility/user mode bit masks */ + + /* defines for u.u_compatibility bit mask */ + + struct sem_undo *U_semundo; /* semaphore undo struct pointer */ + + /* accounting and profiling data */ + time_t U_start; + time_t U_ticks; + struct profdata U_prof; + short U_acflag; /* accounting flag */ + struct trusage64 U_ru; /* this process resource usage value */ + struct trusage64 U_cru; /* accumulated children's resources */ + /* + * The kernel uses U_ru & U_cru to keep track of the time used by + * the process and its children. + * Even though the ru_utime & ru_stime fields within U_ru & U_cru are + * struct timeval, their granularity within the kernel is nanoseconds, + * not microseconds. This is the granularity returned by getprocs(). + * Other syscalls and library functions convert this to microseconds. + */ + + char U_pthru; /* pthread rusage tracking */ + + /* virtualized resource usage values */ + struct trusage64 U_vru; + struct trusage64 U_vcru; + + /* resource limits and counters */ + unsigned long U_fsblimit; /* fs limit in 512 byte-blks */ + +#if defined(_KERNSYS) || defined(__64BIT_KERNEL) + /* rlimit32 structure is exposed to only 32/64 kernel + * and 64bit kernel extensions + */ + struct rlimit32 U_rlimit[RLIM_NLIMITS]; /* 32-bit resource limits */ +#else + struct rlimit U_rlimit[RLIM_NLIMITS]; /* 32-bit resource limits */ +#endif + struct rlimit64 U_saved_rlimit[RLIM_NLIMITS]; /* saved 64-bit limits */ + /* + * To maximize compatibility with old kernel code, a 32-bit + * representation of each resource limit is maintained in U_rlimit. + * Should the limit require a 64-bit representation, the U_rlimit + * value is set to RLIM_INFINITY, with actual 64-bit limit being + * stored in U_saved_rlimit. These flags indicate what + * the real situation is: + * + * RLFLAG_SML => limit correctly represented in 32-bit U_rlimit + * RLFLAG_INF => limit is infinite + * RLFLAG_MAX => limit is in 64_bit U_saved_rlimit.rlim_max + * RLFLAG_CUR => limit is in 64_bit U_saved_rlimit.rlim_cur + * + * These flags are for use by the getrlimit/setrlimit routines only. + * Kernel code desiring a 64-bit limit must go through kgetrlimit. + */ + struct { + uchar rlim_cur; /* how to determine rlim_cur */ + uchar rlim_max; /* how to determine rlim_max */ + } U_rlimit_flag[RLIM_NLIMITS]; + + unsigned long long U_ioch; /* I/O character count */ + + /* timers */ + Simple_lock U_timer_lock; + struct trb *U_timer[NTIMERS]; /* per process timers */ + + /* controlling tty info */ + pid_t *U_ttysid; /* ptr to session leader id in tty */ + pid_t *U_ttyp; /* ptr to controlling tty pgrp field */ + dev_t U_ttyd; /* controlling tty dev */ + long U_ttympx; /* mpx value for controlling tty */ + unsigned *U_ttys; /* pointer to t_state in tty struct */ + int32long64_t U_ttyid; /* tty id */ + int (*U_ttyf)(); /* tty query function pointer */ + + struct upfbuf *U_message; /* uprintf buffer pointer */ + struct trb * U_trb; /* trb for user scheduler */ + struct pvthread *U_chktv; /* pointer to adv handler pvthread */ + + /* file system state */ + vmid_t U_pn_segno; /* sid of chkpt/restart pathname seg*/ + Simple_lock U_pnseg_lock; /* lock of chkpt/restart pathname seg*/ + struct vnode *U_cdir; /* current directory of process */ + struct vnode *U_rdir; /* root directory of process */ + short U_cmask; /* mask for file creation */ + Simple_lock U_fso_lock; /* other file system fields lock */ + long U_lockflag; /* process has done file locks */ + long U_fdevent; /* fd lock event list */ + + long long U_irss; /* accumulator for memory integral */ + struct pinu_block *U_pinu_block;/* list of control structs for pinu */ + tid_t U_ulocks; /* event list for user locks */ + int32long64_t U_rmmap; /* anchor of rmmap list */ + + unsigned long long U_loader[84]; /* loader area */ + Simple_lock U_aio_susp_lock; /* aio lock used in aio_suspend */ + unsigned int U_fdfree[2]; /* file descriptor management */ + + unsigned int U_cannot_chpt; /* process unable to checkpoint */ + unsigned int U_maxofile; /* maximum u_ofile index in use */ + unsigned int U_freefile; /* first available u_ofile index */ + + /* WLM data */ + dev64_t U_dev; /* device of exec'ed file */ + ino64_t U_ino; /* inode */ + uint_t U_gen; /* generation number */ + + char U_tag[WLM_TAG_LENGTH+1]; /* WLM tag: user settable string */ + + /* structures added for VMM checkpoint/restart support */ + struct ckpt_ipcid *U_ckptshm; /* ptr to shmid blocks for checkpoint */ + struct ckpt_ipcid *U_ckptsem; /* ptr to semid blocks for checkpoint */ + struct ckpt_ipcid *U_ckptmsg; /* ptr to msgid blocks for checkpoint */ + struct _mmapent *U_mapent; /* ptr to mmap entry structure used for restart */ + ut_error_t U_ckpterr; /* u_error saved during ckpt processing */ + +#ifdef __64BIT_KERNEL + char cacheline_pad[128]; /* keep 1st cacheline alligned */ +#else + vmlpghandle_t U_lpgsegs32[NSEGS]; /* lgpg handles for 32bit processes */ + char cacheline_pad[64]; /* keep 1st cacheline alligned */ +#endif /* __64BIT_KERNEL */ + struct { /* file descriptor lock cacheline */ + Simple_lock u_fd_slock; + char cache_pad[128 - sizeof(Simple_lock)]; + } U_fd_slcks[U_FD_LCK_CNT]; + + /* from here on the data is pageable. */ + + struct ufd U_ufd[OPEN_MAX];/* User's file descriptor table */ +}; diff --git a/src/os/aix/utmp_v5.h b/src/os/aix/utmp_v5.h new file mode 100644 index 00000000..7341fea6 --- /dev/null +++ b/src/os/aix/utmp_v5.h @@ -0,0 +1,36 @@ +/* + * struct utmp is not binary compatible + * between 4.3 and 5.x + * this structure is taken from 5.1 utmp.h + * we compile this on v4.3. + * XXX: will not compile 5.x if we want to compile + * on 5.x would need to create a struct user_v4 + * to maintain bincompt. + */ + +struct utmp_v5 { + char ut_user[256] ; /* User login name */ + char ut_id[14] ; /* /etc/inittab id */ + char ut_line[64] ; /* device name (console, lnxx) */ + pid_t ut_pid ; /* process id */ + short ut_type ; /* type of entry */ +#if !defined(__64BIT__) && !defined(__ia64) + int __time_t_space; /* for 32vs64-bit time_t PPC */ +#endif + time_t ut_time ; /* time entry was made */ +#if !defined(__64BIT__) && defined(__ia64) + int __time_t_space; /* for 32vs64-bit time_t IA64 */ +#endif + struct exit_status_v5 + { + short e_termination ; /* Process termination status */ + short e_exit ; /* Process exit status */ + } + ut_exit ; /* The exit status of a process + * marked as DEAD_PROCESS. + */ + char ut_host[256] ; /* host name */ + int __dbl_word_pad; /* for double word alignment */ + int __reservedA[2]; + int __reservedV[6]; +}; diff --git a/src/os/darwin/darwin_sigar.c b/src/os/darwin/darwin_sigar.c new file mode 100644 index 00000000..d829b3e3 --- /dev/null +++ b/src/os/darwin/darwin_sigar.c @@ -0,0 +1,854 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_util.h" +#include "sigar_os.h" + +#ifdef DARWIN +#include +#include +#include +#else +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define NMIB(mib) (sizeof(mib)/sizeof(mib[0])) + +int sigar_os_open(sigar_t **sigar) +{ + int mib[2]; + int ncpu; + size_t len; + struct timeval boottime; + + len = sizeof(ncpu); + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + if (sysctl(mib, NMIB(mib), &ncpu, &len, NULL, 0) < 0) { + return errno; + } + + len = sizeof(boottime); + mib[0] = CTL_KERN; + mib[1] = KERN_BOOTTIME; + if (sysctl(mib, NMIB(mib), &boottime, &len, NULL, 0) < 0) { + return errno; + } + + *sigar = malloc(sizeof(**sigar)); + +#ifdef DARWIN + (*sigar)->mach_port = mach_host_self(); +#endif + + (*sigar)->ncpu = ncpu; + + (*sigar)->boot_time = boottime.tv_sec; /* XXX seems off a bit */ + + (*sigar)->last_pid = -1; + + (*sigar)->pinfo = NULL; + + return SIGAR_OK; +} + +int sigar_os_close(sigar_t *sigar) +{ + if (sigar->pinfo) { + free(sigar->pinfo); + } + free(sigar); + return SIGAR_OK; +} + +char *sigar_os_error_string(int err) +{ + return NULL; +} + +int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ +#ifdef DARWIN + vm_statistics_data_t vmstat; + kern_return_t status; + mach_msg_type_number_t count = sizeof(vmstat) / sizeof(integer_t); +#endif + int mib[2]; + int totmem; + size_t len = sizeof(totmem); + + mib[0] = CTL_HW; + + mib[1] = HW_PAGESIZE; + if (sysctl(mib, NMIB(mib), &sigar->pagesize, &len, NULL, 0) < 0) { + return errno; + } + + mib[1] = HW_PHYSMEM; + if (sysctl(mib, NMIB(mib), &totmem, &len, NULL, 0) < 0) { + return errno; + } + + mem->total = totmem; + + sigar_mem_calc_ram(sigar, mem); + +#ifdef DARWIN + status = host_statistics(sigar->mach_port, HOST_VM_INFO, + (host_info_t)&vmstat, &count); + + if (status != KERN_SUCCESS) { + return errno; + } + + mem->free = vmstat.free_count * sigar->pagesize; +#else + mem->free = 1; /*XXX*/ +#endif + + mem->used = mem->total - mem->free; + mem->shared = -1; /*XXX*/ + mem->buffer = -1; + mem->cached = -1; + + return SIGAR_OK; +} + +#define SIGAR_FS_BLOCKS_TO_BYTES(buf, f) \ + ((buf.f * (buf.f_bsize / 512)) >> 1) + +#define VM_DIR "/private/var/vm" +#define SWAPFILE "swapfile" + +int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ +#ifdef DARWIN + DIR *dirp; + struct dirent *ent; + char swapfile[SSTRLEN(VM_DIR) + SSTRLEN("/") + SSTRLEN(SWAPFILE) + 12]; + struct stat swapstat; + struct statfs vmfs; + + swap->used = swap->total = swap->free = 0; + + if (!(dirp = opendir(VM_DIR))) { + return errno; + } + + /* looking for "swapfile0", "swapfile1", etc. */ + while ((ent = readdir(dirp))) { + char *ptr = swapfile; + + if ((ent->d_namlen < SSTRLEN(SWAPFILE)+1) || /* n/a, see comment above */ + (ent->d_namlen > SSTRLEN(SWAPFILE)+11)) /* ensure no overflow */ + { + continue; + } + + if (!strnEQ(ent->d_name, SWAPFILE, SSTRLEN(SWAPFILE))) { + continue; + } + + /* sprintf(swapfile, "%s/%s", VM_DIR, ent->d_name) */ + + memcpy(ptr, VM_DIR, SSTRLEN(VM_DIR)); + ptr += SSTRLEN(VM_DIR); + + *ptr++ = '/'; + + memcpy(ptr, ent->d_name, ent->d_namlen+1); + + if (stat(swapfile, &swapstat) < 0) { + continue; + } + + swap->used += swapstat.st_size; + } + + closedir(dirp); + + if (statfs(VM_DIR, &vmfs) < 0) { + return errno; + } + + swap->total = SIGAR_FS_BLOCKS_TO_BYTES(vmfs, f_bfree) + swap->used; + + swap->free = swap->total - swap->used; + +#else + /*XXX*/ + swap->total = 0; + swap->used = 0; + swap->free = 0; +#endif + + return SIGAR_OK; +} + +int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ +#ifdef DARWIN + kern_return_t status; + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + host_cpu_load_info_data_t cpuload; + + status = host_statistics(sigar->mach_port, HOST_CPU_LOAD_INFO, + (host_info_t)&cpuload, &count); + + if (status != KERN_SUCCESS) { + return errno; + } + + cpu->user = cpuload.cpu_ticks[CPU_STATE_USER]; + cpu->sys = cpuload.cpu_ticks[CPU_STATE_SYSTEM]; + cpu->idle = cpuload.cpu_ticks[CPU_STATE_IDLE]; + cpu->nice = cpuload.cpu_ticks[CPU_STATE_NICE]; + + cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle; + + +#else + /*XXX*/ + cpu->user = 0; + cpu->nice = 0; + cpu->sys = 0; + cpu->idle = 0; + + cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle; +#endif + + return SIGAR_OK; +} + +int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + uptime->uptime = time(NULL) - sigar->boot_time; + + uptime->idletime = -1; + + return SIGAR_OK; +} + +int sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + getloadavg(loadavg->loadavg, 3); + + return SIGAR_OK; +} + +int sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ +#ifdef DARWIN + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + int i, num; + size_t len; + struct kinfo_proc *proc; + + if (sysctl(mib, NMIB(mib), NULL, &len, NULL, 0) < 0) { + return errno; + } + + proc = malloc(len); + + if (sysctl(mib, NMIB(mib), proc, &len, NULL, 0) < 0) { + free(proc); + return errno; + } + + num = len/sizeof(*proc); + proclist->number = 0; + proclist->size = num; + proclist->data = malloc(sizeof(*(proclist->data)) * num); + + for (i=0; idata[proclist->number++] = proc[i].kp_proc.p_pid; + } + + free(proc); + + return SIGAR_OK; +#else + /*XXX above compiles on freebsd but no workie */ + return sigar_proc_list_procfs_get(sigar, proclist); +#endif +} + +int sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +static int sigar_get_pinfo(sigar_t *sigar, sigar_pid_t pid) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; + size_t len = sizeof(*sigar->pinfo); + time_t timenow = time(NULL); + mib[3] = pid; + + if (sigar->pinfo == NULL) { + sigar->pinfo = malloc(len); + } + + if (sigar->last_pid == pid) { + if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { + return SIGAR_OK; + } + } + + sigar->last_pid = pid; + sigar->last_getprocs = timenow; + + if (sysctl(mib, NMIB(mib), sigar->pinfo, &len, NULL, 0) < 0) { + return errno; + } + + return SIGAR_OK; +} + +int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + mach_port_t task, self = mach_task_self(); + kern_return_t status; + task_basic_info_data_t info; + mach_msg_type_number_t type = TASK_BASIC_INFO_COUNT; + + status = task_for_pid(self, pid, &task); + + if (status != KERN_SUCCESS) { + return errno; + } + + status = task_info(task, TASK_BASIC_INFO, &info, &type); + + if (task != self) { + mach_port_deallocate(self, task); + } + + procmem->vsize = info.virtual_size; + procmem->resident = info.resident_size; + + /*XXX*/ + procmem->size = 1; /* 1 == let ant test pass for now */ + procmem->rss = -1; + procmem->share = -1; + + return SIGAR_OK; +} + +int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + int status = sigar_get_pinfo(sigar, pid); + struct kinfo_proc *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + proccred->uid = pinfo->kp_eproc.e_pcred.p_ruid; + proccred->gid = pinfo->kp_eproc.e_pcred.p_rgid; + proccred->euid = pinfo->kp_eproc.e_pcred.p_svuid; + proccred->egid = pinfo->kp_eproc.e_pcred.p_svgid; + + return SIGAR_OK; +} + +static int get_proc_times(sigar_pid_t pid, sigar_proc_time_t *time) +{ + unsigned int count; + time_value_t utime = {0, 0}, stime = {0, 0}; + task_basic_info_data_t ti; + task_thread_times_info_data_t tti; + task_port_t task, self = mach_task_self(); + kern_return_t status; + + status = task_for_pid(self, pid, &task); + if (status != KERN_SUCCESS) { + return errno; + } + + count = TASK_BASIC_INFO_COUNT; + status = task_info(task, TASK_BASIC_INFO, + (task_info_t)&ti, &count); + if (status != KERN_SUCCESS) { + if (task != self) { + mach_port_deallocate(self, task); + } + return errno; + } + + count = TASK_THREAD_TIMES_INFO_COUNT; + status = task_info(task, TASK_THREAD_TIMES_INFO, + (task_info_t)&tti, &count); + if (status != KERN_SUCCESS) { + if (task != self) { + mach_port_deallocate(self, task); + } + return errno; + } + + time_value_add(&utime, &ti.user_time); + time_value_add(&stime, &ti.system_time); + time_value_add(&utime, &tti.user_time); + time_value_add(&stime, &tti.system_time); + + time->utime = utime.seconds; + time->stime = stime.seconds; + + return SIGAR_OK; +} + +int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + int status = sigar_get_pinfo(sigar, pid); + struct kinfo_proc *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + if ((status = get_proc_times(pid, proctime)) != SIGAR_OK) { + return status; + } + +#ifdef DARWIN + proctime->start_time = pinfo->kp_proc.p_starttime.tv_sec; +#else + proctime->start_time = 1;/*XXX*/ +#endif + + return SIGAR_OK; +} + +int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + int status = sigar_get_pinfo(sigar, pid); + struct kinfo_proc *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + SIGAR_SSTRCPY(procstate->name, pinfo->kp_proc.p_comm); + procstate->ppid = pinfo->kp_eproc.e_ppid; + procstate->priority = pinfo->kp_proc.p_priority; + procstate->nice = pinfo->kp_proc.p_nice; + procstate->tty = -1; /*XXX*/ + + switch (pinfo->kp_proc.p_stat) { + case SIDL: + procstate->state = 'D'; + break; + case SRUN: + procstate->state = 'R'; + break; + case SSLEEP: + procstate->state = 'S'; + break; + case SSTOP: + procstate->state = 'T'; + break; + case SZOMB: + procstate->state = 'Z'; + break; + } + + return SIGAR_OK; +} + +int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + char *type = fsp->sys_type_name; + + /* see sys/disklabel.h */ + switch (*type) { + case 'h': + if (strEQ(type, "hfs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'u': + if (strEQ(type, "ufs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + } + + return fsp->type; +} + +int sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + struct statfs *fs; + int num, i; + long len; + + if ((num = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) { + return errno; + } + + len = sizeof(*fs) * num; + fs = malloc(len); + + if ((num = getfsstat(fs, len, MNT_NOWAIT)) < 0) { + return errno; + } + + sigar_file_system_list_create(fslist); + + for (i=0; idata[fslist->number++]; + + SIGAR_SSTRCPY(fsp->dir_name, fs[i].f_mntonname); + SIGAR_SSTRCPY(fsp->dev_name, fs[i].f_mntfromname); + SIGAR_SSTRCPY(fsp->sys_type_name, fs[i].f_fstypename); + sigar_fs_type_init(fsp); + } + + return SIGAR_OK; +} + +int sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + struct statfs buf; + + if (statfs(dirname, &buf) < 0) { + return errno; + } + + fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_blocks); + fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bfree); + fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bavail); + fsusage->files = buf.f_files; + fsusage->free_files = buf.f_files; + fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); + + return SIGAR_OK; +} + +int sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + int i; + + sigar_cpu_infos_create(cpu_infos); + + for (i=0; incpu; i++) { + sigar_cpu_info_t *info; + + SIGAR_CPU_INFOS_GROW(cpu_infos); + + info = &cpu_infos->data[cpu_infos->number++]; + + SIGAR_SSTRCPY(info->vendor, "Apple"); + SIGAR_SSTRCPY(info->model, "powerpc"); + + info->mhz = -1; /*XXX*/ + info->cache_size = -1; + } + + return SIGAR_OK; +} + +int sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + sigar_net_route_t *route; + + sigar_net_route_list_create(routelist); + + return SIGAR_OK; +} + +typedef enum { + IFMSG_ITER_LIST, + IFMSG_ITER_GET +} ifmsg_iter_e; + +typedef struct { + const char *name; + ifmsg_iter_e type; + union { + sigar_net_interface_list_t *iflist; + struct if_msghdr *ifm; + } data; +} ifmsg_iter_t; + +static int sigar_ifmsg_init(sigar_t *sigar) +{ + int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, 0 }; + size_t len; + + if (sysctl(mib, NMIB(mib), NULL, &len, NULL, 0) < 0) { + return errno; + } + + if (sigar->ifconf_len < len) { + sigar->ifconf_buf = realloc(sigar->ifconf_buf, len); + sigar->ifconf_len = len; + } + + if (sysctl(mib, NMIB(mib), sigar->ifconf_buf, &len, NULL, 0) < 0) { + return errno; + } + + return SIGAR_OK; +} + +static int sigar_ifmsg_iter(sigar_t *sigar, ifmsg_iter_t *iter) +{ + char *end = sigar->ifconf_buf + sigar->ifconf_len; + char *ptr = sigar->ifconf_buf; + + if (iter->type == IFMSG_ITER_LIST) { + sigar_net_interface_list_create(iter->data.iflist); + } + + while (ptr < end) { + char *name; + struct sockaddr_dl *sdl; + struct if_msghdr *ifm = (struct if_msghdr *)ptr; + + if (ifm->ifm_type != RTM_IFINFO) { + break; + } + + ptr += ifm->ifm_msglen; + + while (ptr < end) { + struct if_msghdr *next = (struct if_msghdr *)ptr; + + if (next->ifm_type != RTM_NEWADDR) { + break; + } + + ptr += next->ifm_msglen; + } + + sdl = (struct sockaddr_dl *)(ifm + 1); + if (sdl->sdl_family != AF_LINK) { + continue; + } + + switch (iter->type) { + case IFMSG_ITER_LIST: + SIGAR_NET_IFLIST_GROW(iter->data.iflist); + + name = malloc(sdl->sdl_nlen+1); + memcpy(name, sdl->sdl_data, sdl->sdl_nlen+1); + + iter->data.iflist->data[iter->data.iflist->number++] = name; + break; + + case IFMSG_ITER_GET: + if (strEQ(iter->name, sdl->sdl_data)) { + iter->data.ifm = ifm; + return SIGAR_OK; + } + } + } + + switch (iter->type) { + case IFMSG_ITER_LIST: + return SIGAR_OK; + + case IFMSG_ITER_GET: + default: + return ENXIO; + } +} + +int sigar_net_interface_list_get(sigar_t *sigar, + sigar_net_interface_list_t *iflist) +{ + int status; + ifmsg_iter_t iter; + + if ((status = sigar_ifmsg_init(sigar)) != SIGAR_OK) { + return status; + } + + iter.type = IFMSG_ITER_LIST; + iter.data.iflist = iflist; + + return sigar_ifmsg_iter(sigar, &iter); +} + +int sigar_net_interface_config_get(sigar_t *sigar, const char *name, + sigar_net_interface_config_t *ifconfig) +{ + int sock; + int status; + ifmsg_iter_t iter; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + struct ifreq ifr; + + if (sigar->ifconf_len == 0) { + if ((status = sigar_ifmsg_init(sigar)) != SIGAR_OK) { + return status; + } + } + + iter.type = IFMSG_ITER_GET; + iter.name = name; + + if ((status = sigar_ifmsg_iter(sigar, &iter)) != SIGAR_OK) { + return status; + } + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return errno; + } + + ifm = iter.data.ifm; + + SIGAR_SSTRCPY(ifconfig->name, name); + + sdl = (struct sockaddr_dl *)(ifm + 1); + sigar_hwaddr_format(ifconfig->hwaddr, + (unsigned char *)LLADDR(sdl)); + + ifconfig->flags = ifm->ifm_flags; + ifconfig->mtu = ifm->ifm_data.ifi_mtu; + ifconfig->metric = ifm->ifm_data.ifi_metric; + + SIGAR_SSTRCPY(ifr.ifr_name, name); + +#define ifr_s_addr(ifr) \ + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr + + if (!ioctl(sock, SIOCGIFADDR, &ifr)) { + ifconfig->address = ifr_s_addr(ifr); + } + + if (!ioctl(sock, SIOCGIFNETMASK, &ifr)) { + ifconfig->netmask = ifr_s_addr(ifr); + } + + if (ifconfig->flags & IFF_LOOPBACK) { + ifconfig->destination = ifconfig->address; + ifconfig->broadcast = 0; + } + else { + if (!ioctl(sock, SIOCGIFDSTADDR, &ifr)) { + ifconfig->destination = ifr_s_addr(ifr); + } + + if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) { + ifconfig->broadcast = ifr_s_addr(ifr); + } + } + + close(sock); + + return SIGAR_OK; +} + +int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + int status; + ifmsg_iter_t iter; + struct if_msghdr *ifm; + + if (sigar->ifconf_len == 0) { + if ((status = sigar_ifmsg_init(sigar)) != SIGAR_OK) { + return status; + } + } + + iter.type = IFMSG_ITER_GET; + iter.name = name; + + if ((status = sigar_ifmsg_iter(sigar, &iter)) != SIGAR_OK) { + return status; + } + + ifm = iter.data.ifm; + + ifstat->rx_bytes = ifm->ifm_data.ifi_ibytes; + ifstat->rx_packets = ifm->ifm_data.ifi_ipackets; + ifstat->rx_errors = ifm->ifm_data.ifi_ierrors; + ifstat->rx_dropped = ifm->ifm_data.ifi_iqdrops; + ifstat->rx_overruns = -1; + ifstat->rx_frame = -1; + + ifstat->tx_bytes = ifm->ifm_data.ifi_obytes; + ifstat->tx_packets = ifm->ifm_data.ifi_opackets; + ifstat->tx_errors = ifm->ifm_data.ifi_oerrors; + ifstat->tx_collisions = ifm->ifm_data.ifi_collisions; + ifstat->tx_dropped = -1; + ifstat->tx_overruns = -1; + ifstat->tx_carrier = -1; + + return SIGAR_OK; +} + +int sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + return SIGAR_ENOTIMPL; +} diff --git a/src/os/darwin/sigar_os.h b/src/os/darwin/sigar_os.h new file mode 100644 index 00000000..ce272732 --- /dev/null +++ b/src/os/darwin/sigar_os.h @@ -0,0 +1,22 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +#ifdef DARWIN +#include +#include +#endif + +#include + +struct sigar_t { + SIGAR_T_BASE; + int pagesize; + time_t last_getprocs; + sigar_pid_t last_pid; + struct kinfo_proc *pinfo; +#ifdef DARWIN + mach_port_t mach_port; +#endif +}; + +#endif /* SIGAR_OS_H */ diff --git a/src/os/freebsd/README b/src/os/freebsd/README new file mode 100644 index 00000000..9ad60159 --- /dev/null +++ b/src/os/freebsd/README @@ -0,0 +1,3 @@ +see ../darwin/ +yes, darwin/macosx is based on freebsd (or something like that), but +sigar was ported to darwin/macosx first so the sources live there. diff --git a/src/os/hpux/dlpi.c b/src/os/hpux/dlpi.c new file mode 100644 index 00000000..a489fdab --- /dev/null +++ b/src/os/hpux/dlpi.c @@ -0,0 +1,266 @@ +/* + * talk to Data Link Provider Interface aka /dev/dlpi + * see: http://docs.hp.com/hpux/onlinedocs/B2355-90139/B2355-90139.html + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DLBUF_SIZE 8192 + +#define ERRBUF_SIZE 1024 + +static int send_req(int fd, char *ptr, int len, char *what, char *ebuf) +{ + struct strbuf ctl; + int flags = 0; + + ctl.maxlen = 0; + ctl.len = len; + ctl.buf = ptr; + + if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) { + snprintf(ebuf, ERRBUF_SIZE, "send_req: putmsg \"%s\": %s", + what, strerror(errno)); + return -1; + } + + return 0; +} + +static int recv_ack(int fd, int size, const char *what, char *bufp, char *ebuf) +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags = 0; + + ctl.maxlen = DLBUF_SIZE; + ctl.len = 0; + ctl.buf = bufp; + + if (getmsg(fd, &ctl, (struct strbuf*)NULL, &flags) < 0) { + snprintf(ebuf, ERRBUF_SIZE, "recv_ack: %s getmsg: %s", + what, strerror(errno)); + return -1; + } + + dlp = (union DL_primitives *)ctl.buf; + switch (dlp->dl_primitive) { + case DL_INFO_ACK: + case DL_BIND_ACK: + case DL_OK_ACK: + case DL_HP_PPA_ACK: + case DL_HP_INFO_ACK: + case DL_GET_STATISTICS_ACK: + break; + + case DL_ERROR_ACK: + switch (dlp->error_ack.dl_errno) { + + case DL_SYSERR: + snprintf(ebuf, ERRBUF_SIZE, "recv_ack: %s: system error - %s", + what, strerror(dlp->error_ack.dl_unix_errno)); + break; + + default: + snprintf(ebuf, ERRBUF_SIZE, "recv_ack: %s: dl error - %d", + what, dlp->error_ack.dl_errno); + break; + } + return -1; + default: + snprintf(ebuf, ERRBUF_SIZE, + "recv_ack: %s: unexpected primitive ack %d", + what, dlp->dl_primitive); + return -1; + } + + if (ctl.len < size) { + snprintf(ebuf, ERRBUF_SIZE, + "recv_ack: %s: ack too small (%d < %d)", + what, ctl.len, size); + return -1; + } + + return ctl.len; +} + +static int dl_attach_req(int fd, uint32_t ppa, char *ebuf) +{ + dl_attach_req_t req; + + req.dl_primitive = DL_ATTACH_REQ; + req.dl_ppa = ppa; + + return send_req(fd, (char *)&req, sizeof(req), "attach", ebuf); +} + +static int dl_bind_req(int fd, uint32_t sap, char *ebuf) +{ + dl_bind_req_t req; + + memset((char *)&req, 0, sizeof(req)); + req.dl_primitive = DL_BIND_REQ; + + req.dl_max_conind = 1; + /* 22 == INSAP, see HP-UX DLPI Programmer's Guide */ + req.dl_sap = 22; + req.dl_service_mode = DL_HP_RAWDLS; + req.dl_service_mode = DL_CODLS; + + return send_req(fd, (char *)&req, sizeof(req), "bind", ebuf); +} + +static int dl_bind_ack(int fd, char *bufp, char *ebuf) +{ + return recv_ack(fd, DL_BIND_ACK_SIZE, "bind", bufp, ebuf); +} + +static int dl_ok_ack(int fd, const char *what, char *bufp, char *ebuf) +{ + return recv_ack(fd, DL_OK_ACK_SIZE, what, bufp, ebuf); +} + +static int dl_info_req(int fd, char *ebuf) +{ + dl_info_req_t req; + + req.dl_primitive = DL_INFO_REQ; + + return send_req(fd, (char *)&req, sizeof(req), "info", ebuf); +} + +static int dl_info_ack(int fd, char *bufp, char *ebuf) +{ + return recv_ack(fd, DL_INFO_ACK_SIZE, "info", bufp, ebuf); +} + +static int dl_hp_info_req(int fd, char *ebuf) +{ + dl_hp_info_req_t req; + + req.dl_primitive = DL_HP_INFO_REQ; + + return send_req(fd, (char *)&req, sizeof(req), "hpinfo", ebuf); +} + +static int dl_hp_info_ack(int fd, char *bufp, char *ebuf) +{ + return recv_ack(fd, DL_HP_INFO_ACK_SIZE, "hpinfo", bufp, ebuf); +} + +static int dl_stats_req(int fd, char *ebuf) +{ + dl_get_statistics_req_t req; + + req.dl_primitive = DL_GET_STATISTICS_REQ; + + return send_req(fd, (char *)&req, sizeof(req), "stats", ebuf); +} + +static int dl_stats_ack(int fd, char *bufp, char *ebuf) +{ + return recv_ack(fd, DL_GET_STATISTICS_ACK_SIZE, "stats", bufp, ebuf); +} + +static int dl_open(int ppa, char *ebuf) +{ + char *dev = "/dev/dlpi"; + int fd = -1; + dl_info_ack_t *infop; + uint32_t buf[DLBUF_SIZE]; + char dname[128]; + + if ((fd = open(dev, O_RDWR)) < 0) { + snprintf(ebuf, sizeof(ebuf), + "failed to open %s: %s", dev, strerror(errno)); + return -1; + } + + if (dl_info_req(fd, ebuf) < 0 || + dl_info_ack(fd, (char *)buf, ebuf) < 0) { + return -1; + } + + infop = &((union DL_primitives *)buf)->info_ack; + + if (infop->dl_provider_style == DL_STYLE2 && + (dl_attach_req(fd, ppa, ebuf) < 0 || + dl_ok_ack(fd, "attach", (char *)buf, ebuf) < 0)) + { + return -1; + } + + if (dl_bind_req(fd, 0, ebuf) < 0 || + dl_bind_ack(fd, (char *)buf, ebuf) < 0) + { + return -1; + } + + return fd; +} + +static int dl_get_hp_info(int fd, char *bufp, char *ebuf) +{ + dl_hp_info_ack_t *ip; + + if ((dl_hp_info_req(fd, ebuf) < 0) || + (dl_hp_info_ack(fd, bufp, ebuf) < 0)) { + return -1; + } + + ip = (dl_hp_info_ack_t *)bufp; + if (ip->dl_primitive != DL_HP_INFO_ACK) { + return -1; + } + + return 0; +} + +static int dl_get_stats(int fd, char *bufp, char *ebuf) +{ + dl_get_statistics_ack_t *ip; + + if ((dl_stats_req(fd, ebuf) < 0) || + (dl_stats_ack(fd, bufp, ebuf) < 0)) { + return -1; + } + + ip = (dl_get_statistics_ack_t *)bufp; + if (ip->dl_primitive != DL_GET_STATISTICS_ACK) { + return -1; + } + + return 0; +} + +int hpux_get_mib_ifentry(int ppa, mib_ifEntry *mib) +{ + int fd, status=0; + char ebuf[ERRBUF_SIZE]; + uint32_t buf[DLBUF_SIZE]; + + if ((fd = dl_open(ppa, ebuf)) < 0) { + return errno; + } + + if (dl_get_stats(fd, (char *)buf, ebuf) >= 0) { + dl_get_statistics_ack_t *st = (dl_get_statistics_ack_t *)buf; + memcpy(mib, (u_char *)buf + st->dl_stat_offset, sizeof(*mib)); + } + else { + status = errno; + } + + close(fd); + return status; +} diff --git a/src/os/hpux/hpux_sigar.c b/src/os/hpux/hpux_sigar.c new file mode 100644 index 00000000..252bda43 --- /dev/null +++ b/src/os/hpux/hpux_sigar.c @@ -0,0 +1,709 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#include +#include + +int sigar_os_open(sigar_t **sigar) +{ + *sigar = malloc(sizeof(**sigar)); + + /* does not change while system is running */ + pstat_getstatic(&(*sigar)->pstatic, + sizeof((*sigar)->pstatic), + 1, 0); + + (*sigar)->proctab = NULL; + + (*sigar)->ticks = sysconf(_SC_CLK_TCK); + + (*sigar)->last_pid = -1; + + (*sigar)->pinfo = NULL; + + return SIGAR_OK; + +} + +int sigar_os_close(sigar_t *sigar) +{ + if (sigar->proctab) { + free(sigar->proctab); + } + if (sigar->pinfo) { + free(sigar->pinfo); + } + free(sigar); + return SIGAR_OK; +} + +char *sigar_os_error_string(int err) +{ + return NULL; +} + +int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ + struct pst_dynamic stats; + sigar_uint64_t pagesize = sigar->pstatic.page_size; + + mem->total = sigar->pstatic.physical_memory * pagesize; + + sigar_mem_calc_ram(sigar, mem); + + pstat_getdynamic(&stats, sizeof(stats), 1, 0); + + mem->free = stats.psd_free * pagesize; + mem->used = mem->total - mem->free; + + /*XXX*/ + mem->shared = mem->buffer = mem->cached = 0; + + return SIGAR_OK; +} + +int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ + struct pst_swapinfo swapinfo; + int i=0; + + swap->total = swap->free = 0; + + while (pstat_getswap(&swapinfo, sizeof(swapinfo), 1, i++) > 0) { + swapinfo.pss_nfpgs *= 4; /* nfpgs is in 512 byte blocks */ + + if (swapinfo.pss_nblksenabled == 0) { + swapinfo.pss_nblksenabled = swapinfo.pss_nfpgs; + } + + swap->total += swapinfo.pss_nblksenabled; + swap->free += swapinfo.pss_nfpgs; + } + + swap->used = swap->total - swap->free; + + return SIGAR_OK; +} + +int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ + int i; + struct pst_dynamic stats; + + pstat_getdynamic(&stats, sizeof(stats), 1, 0); + sigar->ncpu = stats.psd_proc_cnt; + + cpu->user = stats.psd_cpu_time[CP_USER]; + cpu->sys = stats.psd_cpu_time[CP_SYS] + stats.psd_cpu_time[CP_SSYS]; + cpu->nice = stats.psd_cpu_time[CP_NICE]; + cpu->idle = stats.psd_cpu_time[CP_IDLE]; + + cpu->total = 0; + + /* states above plus CP_BLOCK, CP_SWAIT, etc. (see sys/dk.h) */ + for (i=0; itotal += stats.psd_cpu_time[i]; + } + + return SIGAR_OK; +} + +int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + int i; + struct pst_dynamic stats; + + pstat_getdynamic(&stats, sizeof(stats), 1, 0); + sigar->ncpu = stats.psd_proc_cnt; + + sigar_cpu_list_create(cpulist); + + for (i=0; incpu; i++) { + int j; + sigar_cpu_t *cpu; + struct pst_processor proc; + + if (pstat_getprocessor(&proc, sizeof(proc), 1, i) < 0) { + continue; + } + + SIGAR_CPU_LIST_GROW(cpulist); + + cpu = &cpulist->data[cpulist->number++]; + + cpu->user = proc.psp_cpu_time[CP_USER]; + cpu->sys = proc.psp_cpu_time[CP_SYS] + proc.psp_cpu_time[CP_SSYS]; + cpu->nice = proc.psp_cpu_time[CP_NICE]; + cpu->idle = proc.psp_cpu_time[CP_IDLE]; + + cpu->total = 0; + + for (j=0; jtotal += proc.psp_cpu_time[j]; + } + } + + return SIGAR_OK; +} + +int sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + uptime->uptime = time(NULL) - sigar->pstatic.boot_time; + uptime->idletime = 0; /*XXX*/; + + return SIGAR_OK; +} + +int sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + struct pst_dynamic stats; + + pstat_getdynamic(&stats, sizeof(stats), 1, 0); + + loadavg->loadavg[0] = stats.psd_avg_1_min; + loadavg->loadavg[1] = stats.psd_avg_5_min; + loadavg->loadavg[2] = stats.psd_avg_15_min; + + return SIGAR_OK; +} + +int sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + int n; + + if (!sigar->proctab) { + /* malloc this only once per-sigar_t as the size will not change */ + sigar->proctab = malloc(sizeof(*sigar->proctab) * + sigar->pstatic.max_proc); + } + + n = pstat_getproc(sigar->proctab, sizeof(*sigar->proctab), + sigar->pstatic.max_proc, 0); + + proclist->number = 0; + proclist->size = n; + proclist->data = malloc(sizeof(*(proclist->data)) * n); + + for (n=0; nsize; n++) { + proclist->data[proclist->number++] = + sigar->proctab[n].pst_pid; + } + + return SIGAR_OK; +} + +int sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +static int sigar_pstat_getproc(sigar_t *sigar, sigar_pid_t pid) +{ + int status, num; + time_t timenow = time(NULL); + + if (sigar->pinfo == NULL) { + sigar->pinfo = malloc(sizeof(*sigar->pinfo)); + } + + if (sigar->last_pid == pid) { + if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { + return SIGAR_OK; + } + } + + sigar->last_pid = pid; + sigar->last_getprocs = timenow; + + if (pstat_getproc(sigar->pinfo, + sizeof(*sigar->pinfo), + 0, pid) == -1) + { + return ESRCH; + } + + return SIGAR_OK; +} + +int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + int pagesize = sigar->pstatic.page_size; + int status = sigar_pstat_getproc(sigar, pid); + struct pst_status *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + procmem->size = + pinfo->pst_tsize + /* text */ + pinfo->pst_dsize + /* data */ + pinfo->pst_ssize; /* stack */ + + procmem->size *= pagesize; + + procmem->vsize = + pinfo->pst_vtsize + /* text */ + pinfo->pst_vdsize + /* data */ + pinfo->pst_vssize; /* stack */ + + procmem->vsize *= pagesize; + + procmem->rss = pinfo->pst_rssize * pagesize; + + procmem->share = pinfo->pst_shmsize * pagesize; + + return SIGAR_OK; +} + +int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + int status = sigar_pstat_getproc(sigar, pid); + struct pst_status *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + proccred->uid = pinfo->pst_uid; + proccred->gid = pinfo->pst_gid; + proccred->euid = pinfo->pst_euid; + proccred->egid = pinfo->pst_egid; + + return SIGAR_OK; +} + +int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + int status = sigar_pstat_getproc(sigar, pid); + struct pst_status *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + proctime->start_time = pinfo->pst_start; + proctime->start_time *= 1000; + proctime->utime = pinfo->pst_utime; + proctime->stime = pinfo->pst_stime; + + return SIGAR_OK; +} + +int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + int status = sigar_pstat_getproc(sigar, pid); + struct pst_status *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + + SIGAR_SSTRCPY(procstate->name, pinfo->pst_ucomm); + procstate->ppid = pinfo->pst_ppid; + procstate->tty = makedev(pinfo->pst_term.psd_major, + pinfo->pst_term.psd_minor); + procstate->priority = pinfo->pst_pri; + procstate->nice = pinfo->pst_nice; + + switch (pinfo->pst_stat) { + case PS_SLEEP: + procstate->state = 'S'; + break; + case PS_RUN: + procstate->state = 'R'; + break; + case PS_STOP: + procstate->state = 'T'; + break; + case PS_ZOMBIE: + procstate->state = 'Z'; + break; + case PS_IDLE: + procstate->state = 'D'; + break; + } + + return SIGAR_OK; +} + +/* XXX: could be useful elsewhere */ +static char *sigar_getword(char **line, char stop) +{ + char *pos = *line; + int len; + char *res; + + while ((*pos != stop) && *pos) { + ++pos; + } + + len = pos - *line; + res = (char *)malloc(len + 1); + memcpy(res, *line, len); + res[len] = 0; + + if (stop) { + while (*pos == stop) { + ++pos; + } + } + + *line = pos; + + return res; +} + +/* + * XXX: pst_cmd is only 64 chars of the command args. + * according to HP forums there isn't a way to get them + * all if > 64 + */ +int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + char *args, *arg; + struct pst_status status; + + if (pstat_getproc(&status, sizeof(status), 0, pid) == -1) { + return ESRCH; + } + + args = status.pst_cmd; + sigar_proc_args_create(procargs); + + while (*args && (arg = sigar_getword(&args, ' '))) { + SIGAR_PROC_ARGS_GROW(procargs); + procargs->data[procargs->number++] = arg; + } + + return SIGAR_OK; +} + +int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + struct pst_status status; + int idx, i, n; + struct pst_fileinfo psf[16]; + + procfd->total = 0; + + if (pstat_getproc(&status, sizeof(status), 0, pid) == -1) { + return ESRCH; + } + + /* man pstat_getfile for index splaination */ + idx = (status.pst_idx << 16) | (0 & 0xffff); + + while ((n = pstat_getfile(psf, sizeof(psf[0]), + sizeof(psf)/sizeof(psf[0]), + idx)) > 0) + { + procfd->total += n; + idx = psf[n-1].psf_idx + 1; + } + + if (n == -1) { + return errno; + } + + return SIGAR_OK; +} + +int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + return SIGAR_ENOTIMPL; +} + +#include + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + char *type = fsp->sys_type_name; + + switch (*type) { + case 'h': + if (strEQ(type, "hfs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'v': + if (strEQ(type, "vxfs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'c': + if (strEQ(type, "cdfs")) { + fsp->type = SIGAR_FSTYPE_CDROM; + } + break; + } + + return fsp->type; +} + +int sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + struct mntent ent; + char buf[1025]; + + FILE *fp; + sigar_file_system_t *fsp; + + if (!(fp = setmntent(MNT_CHECKLIST, "r"))) { + return errno; + } + + sigar_file_system_list_create(fslist); + + while (getmntent_r(fp, &ent, buf, sizeof(buf)) == 0) { + if ((*(ent.mnt_type) == 's') && + strEQ(ent.mnt_type, "swap")) + { + /* + * in this case, devname == "...", for + * which statfs chokes on. so skip it. + * also notice hpux df command has no swap info. + */ + continue; + } + + SIGAR_FILE_SYSTEM_LIST_GROW(fslist); + + fsp = &fslist->data[fslist->number++]; + + SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_dir); + SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_fsname); + SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_type); + sigar_fs_type_init(fsp); + } + + endmntent(fp); + + return SIGAR_OK; +} + +/* XXX this is exactly the same as linux and solaris is darn close */ +#include + +#define SIGAR_FS_BLOCKS_TO_BYTES(buf, f) \ + ((buf.f * (buf.f_bsize / 512)) >> 1) + +int sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + struct statfs buf; + + if (statfs(dirname, &buf) != 0) { + return errno; + } + + fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_blocks); + fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bfree); + fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bavail); + fsusage->files = buf.f_files; + fsusage->free_files = buf.f_ffree; + fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); + + return SIGAR_OK; +} + +int sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + int i; + struct pst_dynamic stats; + + pstat_getdynamic(&stats, sizeof(stats), 1, 0); + sigar->ncpu = stats.psd_proc_cnt; + + sigar_cpu_infos_create(cpu_infos); + + for (i=0; incpu; i++) { + sigar_cpu_info_t *info; + struct pst_processor proc; + + if (pstat_getprocessor(&proc, sizeof(proc), 1, i) < 0) { + perror("pstat_getprocessor"); + continue; + } + + SIGAR_CPU_INFOS_GROW(cpu_infos); + + info = &cpu_infos->data[cpu_infos->number++]; + + SIGAR_SSTRCPY(info->vendor, "HP"); /*XXX*/ + SIGAR_SSTRCPY(info->model, "PA RISC"); /*XXX*/ + info->mhz = sigar->ticks * proc.psp_iticksperclktick / 1000000; + info->cache_size = -1; /*XXX*/ + } + + return SIGAR_OK; +} + +int sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + int fd, count, i; + unsigned int len; + struct nmparms parms; + mib_ipRouteEnt *routes; + sigar_net_route_t *route; + + if ((fd = open_mib("/dev/ip", O_RDWR, 0, 0)) < 0) { + return errno; + } + + len = sizeof(count); + parms.objid = ID_ipRouteNumEnt; + parms.buffer = &count; + parms.len = &len; + + if (get_mib_info(fd, &parms) < 0) { + return errno; + } + + len = count * sizeof(*routes); + routes = malloc(len); + + parms.objid = ID_ipRouteTable; + parms.buffer = routes; + parms.len = &len; + + if (get_mib_info(fd, &parms) < 0) { + free(routes); + return errno; + } + + routelist->size = routelist->number = 0; + + sigar_net_route_list_create(routelist); + + for (i=0; idata[routelist->number++]; + SIGAR_ZERO(route); /* XXX: other fields */ + + route->destination = ent->Dest; + route->mask = ent->Mask; + route->gateway = ent->NextHop; + } + + free(routes); + close_mib(fd); + + return SIGAR_OK; +} + +static int get_mib_ifstat(sigar_t *sigar, + const char *name, + mib_ifEntry *mib) +{ + int fd, count, i; + unsigned int len; + nmapi_phystat *stat; + struct nmparms parms; + + if ((fd = open_mib("/dev/ip", O_RDWR, 0, 0)) < 0) { + return errno; + } + + len = sizeof(count); + parms.objid = ID_ifNumber; + parms.buffer = &count; + parms.len = &len; + + if (get_mib_info(fd, &parms) < 0) { + return errno; + } + + len = sizeof(nmapi_phystat) * count; + + if (sigar->ifconf_len < len) { + sigar->ifconf_buf = realloc(sigar->ifconf_buf, len); + sigar->ifconf_len = len; + } + + if (get_physical_stat(sigar->ifconf_buf, &len) < 0) { + close_mib(fd); + return errno; + } + + for (i=0, stat = (nmapi_phystat *)sigar->ifconf_buf; + inm_device, name)) { + memcpy(mib, &stat->if_entry, sizeof(*mib)); + close_mib(fd); + return SIGAR_OK; + } + } + + close_mib(fd); + return ENXIO; +} + +int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + int status; + mib_ifEntry mib; + + status = get_mib_ifstat(sigar, name, &mib); + + if (status != SIGAR_OK) { + return status; + } + + ifstat->rx_bytes = mib.ifInOctets; + ifstat->rx_packets = mib.ifInUcastPkts + mib.ifInNUcastPkts; + ifstat->rx_errors = mib.ifInErrors; + ifstat->rx_dropped = mib.ifInDiscards; + ifstat->rx_overruns = 0; /*XXX*/ + ifstat->rx_frame = 0; /*XXX*/ + + ifstat->tx_bytes = mib.ifOutOctets; + ifstat->tx_packets = mib.ifOutUcastPkts + mib.ifOutNUcastPkts; + ifstat->tx_errors = mib.ifOutErrors; + ifstat->tx_dropped = mib.ifOutDiscards; + ifstat->tx_overruns = 0; /*XXX*/ + ifstat->tx_collisions = 0; /*XXX*/ + ifstat->tx_carrier = 0; /*XXX*/ + + return SIGAR_OK; +} + +int sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + return SIGAR_ENOTIMPL; +} diff --git a/src/os/hpux/sigar_os.h b/src/os/hpux/sigar_os.h new file mode 100644 index 00000000..179af452 --- /dev/null +++ b/src/os/hpux/sigar_os.h @@ -0,0 +1,20 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +#include +#include +#include +#include + +struct sigar_t { + SIGAR_T_BASE; + struct pst_static pstatic; + struct pst_status *proctab; + time_t last_getprocs; + sigar_pid_t last_pid; + struct pst_status *pinfo; +}; + +int hpux_get_mib_ifentry(int ppa, mib_ifEntry *mib); + +#endif /* SIGAR_OS_H */ diff --git a/src/os/linux/linux_sigar.c b/src/os/linux/linux_sigar.c new file mode 100644 index 00000000..c91bdf1f --- /dev/null +++ b/src/os/linux/linux_sigar.c @@ -0,0 +1,1548 @@ +#include +#include +#include +#include +#include +#include + +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#include /* for PAGE_SHIFT */ + +#define pageshift(x) ((x) << PAGE_SHIFT) + +#define PROC_MEMINFO PROC_FS_ROOT "meminfo" +#define PROC_STAT PROC_FS_ROOT "stat" +#define PROC_UPTIME PROC_FS_ROOT "uptime" +#define PROC_LOADAVG PROC_FS_ROOT "loadavg" + +#define PROC_PSTAT "/stat" +#define PROC_PSTATUS "/status" + +/* + * could also parse /proc/cpuinfo + * but there is less to parse this way. + */ +#define PROC_CPU "/proc/1/cpu" + +static void get_ncpu(sigar_t *sigar) +{ + struct stat sb; + FILE *fp; + + sigar->ncpu = 0; + + if (stat(PROC_CPU, &sb) < 0) { + /* file does not exist unless ncpu > 1 */ + sigar->ncpu = 1; + } + else { + char buffer[BUFSIZ], *ptr; + if ((fp = fopen(PROC_CPU, "r"))) { + while ((ptr = fgets(buffer, sizeof(buffer), fp))) { + if (strnEQ(ptr, "cpu", 3) && + isdigit(ptr[3])) + { + ++sigar->ncpu; + } + } + fclose(fp); + } + } +} + +/* + * /proc/self/stat fields: + * 1 - pid + * 2 - comm + * 3 - state + * 4 - ppid + * 5 - pgrp + * 6 - session + * 7 - tty_nr + * 8 - tpgid + * 9 - flags + * 10 - minflt + * 11 - cminflt + * 12 - majflt + * 13 - cmajflt + * 14 - utime + * 14 - stime + * 15 - cutime + * 16 - cstime + * 17 - priority + * 18 - nice + * 19 - 0 (removed field) + * 20 - itrealvalue + * 21 - starttime + * 22 - vsize + * 23 - rss + * 24 - rlim + * 25 - startcode + * 26 - endcode + * 27 - startstack + * 28 - kstkesp + * 29 - kstkeip + * 30 - signal + * 31 - blocked + * 32 - sigignore + * 33 - sigcache + * 34 - wchan + * 35 - nswap + * 36 - cnswap + * 37 - exit_signal <-- looking for this. + * 38 - processor + * ... more for newer RH + */ + +#define PROC_SIGNAL_IX 38 + +static int get_proc_signal_offset(void) +{ + char buffer[BUFSIZ], *ptr=buffer; + int fields = 0; + int status = sigar_file2str("/proc/self/stat", + buffer, sizeof(buffer)); + + if (status != SIGAR_OK) { + return 1; + } + + while (*ptr) { + if (*ptr++ == ' ') { + fields++; + } + } + + return (fields - PROC_SIGNAL_IX) + 1; +} + +sigar_pid_t sigar_pid_get(sigar_t *sigar) +{ + /* XXX cannot safely cache getpid unless using nptl */ + /* we can however, cache it for optimizations in the + * case of proc_env_get for example. + */ + sigar->pid = getpid(); + return sigar->pid; +} + +int sigar_os_open(sigar_t **sigar) +{ + char buffer[BUFSIZ], *ptr; + int status = sigar_file2str(PROC_STAT, buffer, sizeof(buffer)); + + *sigar = malloc(sizeof(**sigar)); + + if (status != SIGAR_OK) { + return status; + } + + ptr = strstr(buffer, "\nbtime"); + ptr = sigar_skip_token(ptr); + (*sigar)->boot_time = sigar_strtoul(ptr); + + (*sigar)->ticks = 100; /* HZ */ + + (*sigar)->ram = -1; + + (*sigar)->proc_signal_offset = -1; + + get_ncpu(*sigar); + + (*sigar)->last_proc_stat.pid = -1; + + return SIGAR_OK; +} + +int sigar_os_close(sigar_t *sigar) +{ + free(sigar); + return SIGAR_OK; +} + +char *sigar_os_error_string(int err) +{ + return NULL; +} + +static int get_ram(sigar_t *sigar, sigar_mem_t *mem) +{ + char buffer[BUFSIZ], *ptr; + FILE *fp; + int total = 0; + + if (sigar->ram > 0) { + /* return cached value */ + mem->ram = sigar->ram; + return SIGAR_OK; + } + + if (sigar->ram == 0) { + return ENOENT; + } + + /* + * Memory Type Range Registers + * write-back registers add up to the total. + */ + if (!(fp = fopen("/proc/mtrr", "r"))) { + return errno; + } + + while ((ptr = fgets(buffer, sizeof(buffer), fp))) { + if (!(ptr = strstr(ptr, "size="))) { + continue; + } + + if (!strstr(ptr, "write-back")) { + continue; + } + + ptr += 5; + while (sigar_isspace(*ptr)) { + ++ptr; + } + + total += atoi(ptr); + } + + fclose(fp); + + if (total == 0) { + return ENOENT; + } + + mem->ram = sigar->ram = total; + + return SIGAR_OK; +} + +int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ + char buffer[BUFSIZ]; + char *ptr; + + int status = sigar_file2str(PROC_MEMINFO, + buffer, sizeof(buffer)); + + if (status != SIGAR_OK) { + return status; + } + + ptr = sigar_skip_line(buffer, sizeof(buffer)); + ptr = sigar_skip_token(ptr); /* "Mem:" */ + + mem->total = sigar_strtoul(ptr); + mem->used = sigar_strtoul(ptr); + mem->free = sigar_strtoul(ptr); + mem->shared = sigar_strtoul(ptr); + mem->buffer = sigar_strtoul(ptr); + mem->cached = sigar_strtoul(ptr); + + if (get_ram(sigar, mem) != SIGAR_OK) { + /* XXX other options on failure? */ + sigar_mem_calc_ram(sigar, mem); + } + + return SIGAR_OK; +} + +int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ + char buffer[BUFSIZ]; + char *ptr; + + /* XXX: we open/parse the same file here as sigar_mem_get */ + int status = sigar_file2str(PROC_MEMINFO, + buffer, sizeof(buffer)); + + if (status != SIGAR_OK) { + return status; + } + + ptr = sigar_skip_line(buffer, sizeof(buffer)); + ptr = sigar_skip_line(ptr, 0); + ptr = sigar_skip_token(ptr); /* "Swap:" */ + + swap->total = sigar_strtoul(ptr); + swap->used = sigar_strtoul(ptr); + swap->free = sigar_strtoul(ptr); + + return SIGAR_OK; +} + +static void get_cpu_metrics(sigar_cpu_t *cpu, char *line) +{ + char *ptr = sigar_skip_token(line); /* "cpu%d" */ + + cpu->user = sigar_strtoul(ptr); + cpu->nice = sigar_strtoul(ptr); + cpu->sys = sigar_strtoul(ptr); + cpu->idle = sigar_strtoul(ptr); + + cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle; +} + +int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ + char buffer[BUFSIZ]; + int status = sigar_file2str(PROC_STAT, buffer, sizeof(buffer)); + + if (status != SIGAR_OK) { + return status; + } + + get_cpu_metrics(cpu, buffer); + + return SIGAR_OK; +} + +int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + FILE *fp; + char buffer[BUFSIZ], *ptr; + + if (!(fp = fopen(PROC_STAT, "r"))) { + return errno; + } + + /* skip first line */ + (void)fgets(buffer, sizeof(buffer), fp); + + sigar_cpu_list_create(cpulist); + + /* XXX: merge times of logical processors if hyperthreading */ + while ((ptr = fgets(buffer, sizeof(buffer), fp))) { + sigar_cpu_t *cpu; + + if (!strnEQ(ptr, "cpu", 3)) { + break; + } + + SIGAR_CPU_LIST_GROW(cpulist); + cpu = &cpulist->data[cpulist->number++]; + + get_cpu_metrics(cpu, ptr); + } + + fclose(fp); + + return SIGAR_OK; +} + +int sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + char buffer[BUFSIZ], *ptr = buffer; + int status = sigar_file2str(PROC_UPTIME, buffer, sizeof(buffer)); + + if (status != SIGAR_OK) { + return status; + } + + uptime->uptime = strtod(buffer, &ptr); + uptime->idletime = strtod(ptr, &ptr); + + return SIGAR_OK; +} + +int sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + char buffer[BUFSIZ], *ptr = buffer; + int status = sigar_file2str(PROC_LOADAVG, buffer, sizeof(buffer)); + + if (status != SIGAR_OK) { + return status; + } + + loadavg->loadavg[0] = strtod(buffer, &ptr); + loadavg->loadavg[1] = strtod(ptr, &ptr); + loadavg->loadavg[2] = strtod(ptr, &ptr); + + return SIGAR_OK; +} + +/* + * seems the easiest/fastest way to tell if a process listed in /proc + * is a thread is to check the "exit signal" flag in /proc/num/stat. + * any value other than SIGCHLD seems to be a thread. this make hulk mad. + * redhat's procps patch (named "threadbadhack.pat") does not use + * this flag to filter out threads. instead does much more expensive + * comparisions. their patch also bubbles up thread cpu times to the main + * process. functionality we currently lack. + * when nptl is in use, this is not the case and all threads spawned from + * a process have the same pid. however, it seems both old-style linux + * threads and nptl threads can be run on the same machine. + * there is also the "Tgid" field in /proc/self/status which could be used + * to detect threads, but this is not available in older kernels. + */ +static SIGAR_INLINE int proc_isthread(sigar_t *sigar, char *pidstr, int len) +{ + char buffer[BUFSIZ], *ptr=buffer; + int fd, n, offset=sigar->proc_signal_offset; + + /* sprintf(buffer, "/proc/%s/stat", pidstr) */ + memcpy(ptr, PROC_FS_ROOT, SSTRLEN(PROC_FS_ROOT)); + ptr += SSTRLEN(PROC_FS_ROOT); + + memcpy(ptr, pidstr, len); + ptr += len; + + memcpy(ptr, PROC_PSTAT, SSTRLEN(PROC_PSTAT)); + ptr += SSTRLEN(PROC_PSTAT); + + *ptr = '\0'; + + if ((fd = open(buffer, O_RDONLY)) < 0) { + /* unlikely if pid was from readdir proc */ + return 0; + } + + n = read(fd, buffer, sizeof(buffer)); + close(fd); + + if (n < 0) { + return 0; /* chances: slim..none */ + } + + buffer[n--] = '\0'; + + /* exit_signal is the second to last field so we look backwards. + * XXX if newer kernels drop more turds in this file we'll need + * to go the other way. luckily linux has no real api for this shit. + */ + + /* skip trailing crap */ + while ((n > 0) && !isdigit(buffer[n--])) ; + + while (offset-- > 0) { + /* skip last field */ + while ((n > 0) && isdigit(buffer[n--])) ; + + /* skip whitespace */ + while ((n > 0) && !isdigit(buffer[n--])) ; + } + + if (n < 3) { + return 0; /* hulk smashed /proc? */ + } + + ptr = &buffer[n]; + /* + * '17' == SIGCHLD == real process. + * '33' and '0' are threads + */ + if ((*ptr++ == '1') && + (*ptr++ == '7') && + (*ptr++ == ' ')) + { + return 0; + } + + return 1; +} + +int sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + DIR *dirp = opendir("/proc"); + struct dirent *ent, dbuf; + + if (!dirp) { + return errno; + } + + if (sigar->proc_signal_offset == -1) { + sigar->proc_signal_offset = get_proc_signal_offset(); + } + + sigar_proc_list_create(proclist); + + while (readdir_r(dirp, &dbuf, &ent) == 0) { + if (!ent) { + break; + } + + if (!sigar_isdigit(*ent->d_name)) { + continue; + } + + if (proc_isthread(sigar, ent->d_name, strlen(ent->d_name))) + { + continue; + } + + /* XXX: more sanity checking */ + + SIGAR_PROC_LIST_GROW(proclist); + + proclist->data[proclist->number++] = + strtoul(ent->d_name, NULL, 10); + } + + closedir(dirp); + + return SIGAR_OK; +} + +int sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +static int proc_stat_read(sigar_t *sigar, sigar_pid_t pid) +{ + char buffer[BUFSIZ], *ptr=buffer, *tmp; + unsigned int len; + linux_proc_stat_t *pstat = &sigar->last_proc_stat; + int status; + + time_t timenow = time(NULL); + + /* + * short-lived cache read/parse of last /proc/pid/stat + * as this info is spread out across a few functions. + */ + if (pstat->pid == pid) { + if ((timenow - pstat->mtime) < SIGAR_LAST_PROC_EXPIRE) { + return SIGAR_OK; + } + } + + pstat->pid = pid; + pstat->mtime = timenow; + + status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTAT); + + if (status != SIGAR_OK) { + return status; + } + + ptr = strchr(ptr, '(')+1; + + tmp = strrchr(ptr, ')'); + len = tmp-ptr; + + if (len >= sizeof(pstat->name)) { + len = sizeof(pstat->name)-1; + } + + memcpy(pstat->name, ptr, len); + pstat->name[len] = '\0'; + ptr = tmp+1; + + SIGAR_SKIP_SPACE(ptr); + pstat->state = *ptr++; + SIGAR_SKIP_SPACE(ptr); + + pstat->ppid = sigar_strtoul(ptr); + ptr = sigar_skip_token(ptr); /* pgrp */ + ptr = sigar_skip_token(ptr); /* session */ + pstat->tty = sigar_strtoul(ptr); + ptr = sigar_skip_token(ptr); /* tty pgrp */ + + ptr = sigar_skip_token(ptr); /* flags */ + ptr = sigar_skip_token(ptr); /* min flt */ + ptr = sigar_skip_token(ptr); /* cmin flt */ + ptr = sigar_skip_token(ptr); /* maj flt */ + ptr = sigar_skip_token(ptr); /* cmaj flt */ + + pstat->utime = sigar_strtoul(ptr) / sigar->ticks; + pstat->stime = sigar_strtoul(ptr) / sigar->ticks; + + ptr = sigar_skip_token(ptr); /* cutime */ + ptr = sigar_skip_token(ptr); /* cstime */ + + pstat->priority = sigar_strtoul(ptr); + pstat->nice = sigar_strtoul(ptr); + + ptr = sigar_skip_token(ptr); /* timeout */ + ptr = sigar_skip_token(ptr); /* it_real_value */ + + pstat->start_time = sigar_strtoul(ptr); + pstat->start_time /= sigar->ticks; + pstat->start_time += sigar->boot_time; /* seconds */ + pstat->start_time *= 1000; /* milliseconds */ + + pstat->vsize = sigar_strtoul(ptr); + pstat->rss = pageshift(sigar_strtoul(ptr)); + + return SIGAR_OK; +} + +int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + char buffer[BUFSIZ], *ptr=buffer; + int status = proc_stat_read(sigar, pid); + linux_proc_stat_t *pstat = &sigar->last_proc_stat; + + procmem->vsize = pstat->vsize; + procmem->rss = pstat->rss; + + status = SIGAR_PROC_FILE2STR(buffer, pid, "/statm"); + + if (status != SIGAR_OK) { + return status; + } + + procmem->size = pageshift(sigar_strtoul(ptr)); + procmem->resident = pageshift(sigar_strtoul(ptr)); + procmem->share = pageshift(sigar_strtoul(ptr)); + + return SIGAR_OK; +} + +int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + char buffer[BUFSIZ], *ptr; + int status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTATUS); + + if (status != SIGAR_OK) { + return status; + } + + ptr = strstr(buffer, "\nUid:"); + ptr = sigar_skip_token(ptr); + + proccred->uid = sigar_strtoul(ptr); + proccred->euid = sigar_strtoul(ptr); + + ptr = strstr(ptr, "\nGid:"); + ptr = sigar_skip_token(ptr); + + proccred->gid = sigar_strtoul(ptr); + proccred->egid = sigar_strtoul(ptr); + + return SIGAR_OK; +} + +int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + int status = proc_stat_read(sigar, pid); + linux_proc_stat_t *pstat = &sigar->last_proc_stat; + + if (status != SIGAR_OK) { + return status; + } + + proctime->utime = pstat->utime; + proctime->stime = pstat->stime; + proctime->start_time = pstat->start_time; + + return SIGAR_OK; +} + +int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + int status = proc_stat_read(sigar, pid); + linux_proc_stat_t *pstat = &sigar->last_proc_stat; + + if (status != SIGAR_OK) { + return status; + } + + memcpy(procstate->name, pstat->name, sizeof(procstate->name)); + procstate->state = pstat->state; + + procstate->ppid = pstat->ppid; + procstate->tty = pstat->tty; + procstate->priority = pstat->priority; + procstate->nice = pstat->nice; + + return SIGAR_OK; +} + +int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + char buffer[BUFSIZ], *buf=NULL, *ptr; + int fd, len, total=0; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/cmdline"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + if (errno == ENOENT) { + return ESRCH; + } + return errno; + } + + buffer[0] = '\0'; + + /* XXX: possible to get rid of some mallocs here. + * but, unlikely this will be called often so it + * might not even matter much. + */ + while ((len = read(fd, buffer, sizeof(buffer)-1)) > 0) { + if (len == 0) { + break; + } + if (buf) { + buf = realloc(buf, total+len); + } + else { + buf = malloc(len+1); + } + memcpy(buf+total, buffer, len); + total += len; + } + + close(fd); + + sigar_proc_args_create(procargs); + + //e.g. /proc/2/cmdline + if (total == 0) { + procargs->number = 0; + return SIGAR_OK; + } + + buf[total] = '\0'; + ptr = buf; + + while (*ptr) { + int alen = strlen(ptr)+1; + char *arg = malloc(alen); + + SIGAR_PROC_ARGS_GROW(procargs); + memcpy(arg, ptr, alen); + + procargs->data[procargs->number++] = arg; + + ptr += alen; + } + + free(buf); + + return SIGAR_OK; +} + +int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + int fd; + char buffer[ARG_MAX]; /* XXX: ARG_MAX == 130k */ + char name[BUFSIZ]; + size_t len; + char *ptr, *end; + + /* optimize if pid == $$ and type == ENV_KEY */ + SIGAR_PROC_ENV_KEY_LOOKUP(); + + (void)SIGAR_PROC_FILENAME(name, pid, "/environ"); + + if ((fd = open(name, O_RDONLY)) < 0) { + if (errno == ENOENT) { + return ESRCH; + } + return errno; + } + + len = read(fd, buffer, sizeof(buffer)); + + close(fd); + + buffer[len] = '\0'; + ptr = buffer; + + end = buffer + len; + while (ptr < end) { + char *val = strchr(ptr, '='); + int klen, vlen, status; + char key[128]; /* XXX is there a max key size? */ + + if (val == NULL) { + /* not key=val format */ + break; + } + + klen = val - ptr; + SIGAR_SSTRCPY(key, ptr); + key[klen] = '\0'; + ++val; + + vlen = strlen(val); + status = procenv->env_getter(procenv->data, + key, klen, val, vlen); + + if (status != SIGAR_OK) { + /* not an error; just stop iterating */ + break; + } + + ptr += (klen + 1 + vlen + 1); + } + + return SIGAR_OK; +} + +int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + int status = + sigar_proc_fd_count(sigar, pid, &procfd->total); + + return status; +} + +int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + int len; + char name[BUFSIZ]; + + (void)SIGAR_PROC_FILENAME(name, pid, "/cwd"); + + if ((len = readlink(name, procexe->cwd, + sizeof(procexe->cwd)-1)) < 0) + { + return errno; + } + + procexe->cwd[len] = '\0'; + + (void)SIGAR_PROC_FILENAME(name, pid, "/exe"); + + if ((len = readlink(name, procexe->name, + sizeof(procexe->name)-1)) < 0) + { + return errno; + } + + procexe->name[len] = '\0'; + + (void)SIGAR_PROC_FILENAME(name, pid, "/root"); + + if ((len = readlink(name, procexe->root, + sizeof(procexe->root)-1)) < 0) + { + return errno; + } + + procexe->root[len] = '\0'; + + return SIGAR_OK; +} + +#include +#include + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + char *type = fsp->sys_type_name; + + switch (*type) { + case 'e': + if (strnEQ(type, "ext", 3)) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'h': + if (strEQ(type, "hpfs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'r': + if (strEQ(type, "reiserfs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'x': + if (strEQ(type, "xfs") || strEQ(type, "xiafs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + } + + return fsp->type; +} + +int sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + struct mntent ent; + char buf[1025]; /* buffer for strings within ent */ + FILE *fp; + sigar_file_system_t *fsp; + + if (!(fp = setmntent(MOUNTED, "r"))) { + return errno; + } + + sigar_file_system_list_create(fslist); + + while (getmntent_r(fp, &ent, buf, sizeof(buf))) { + SIGAR_FILE_SYSTEM_LIST_GROW(fslist); + + fsp = &fslist->data[fslist->number++]; + + SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_dir); + SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_fsname); + SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_type); + sigar_fs_type_get(fsp); + } + + endmntent(fp); + + return SIGAR_OK; +} + +#include + +#define SIGAR_FS_BLOCKS_TO_BYTES(buf, f) \ + ((buf.f * (buf.f_bsize / 512)) >> 1) + +int sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + struct statfs buf; + + if (statfs(dirname, &buf) != 0) { + return errno; + } + + fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_blocks); + fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bfree); + fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bavail); + fsusage->files = buf.f_files; + fsusage->free_files = buf.f_ffree; + fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); + + return SIGAR_OK; +} + +static SIGAR_INLINE char *cpu_info_strval(char *ptr) +{ + if ((ptr = strchr(ptr, ':'))) { + ptr++; + while (isspace (*ptr)) ptr++; + return ptr; + } + return NULL; +} + +static SIGAR_INLINE void cpu_info_strcpy(char *ptr, char *buf, int len) +{ + int slen; + ptr = cpu_info_strval(ptr); + if (!ptr) { + return; + } + slen = strlen(ptr); + strncpy(buf, ptr, len); + buf[len] = '\0'; + if (slen < len) { + buf[slen-1] = '\0'; /* rid \n */ + } +} + +static int get_cpu_info(sigar_cpu_info_t *info, FILE *fp, int *id) +{ + char buffer[BUFSIZ], *ptr; + + int found = 0, siblings = 0; + + *id = -1; + + while ((ptr = fgets(buffer, sizeof(buffer), fp))) { + switch (*ptr) { + case 'p': /* processor : 0 */ + if (strnEQ(ptr, "processor", 9)) { + found = 1; + } + else if (strnEQ(ptr, "physical id", 11)) { + ptr = cpu_info_strval(ptr); + *id = atoi(ptr); + } + break; + case 'v': + if (strnEQ(ptr, "vendor_id", 9)) { + cpu_info_strcpy(ptr, info->vendor, sizeof(info->vendor)); + } + break; + case 'm': + if (strnEQ(ptr, "model name", 10)) { + cpu_info_strcpy(ptr, info->model, sizeof(info->model)); + } + break; + case 'c': + if (strnEQ(ptr, "cpu MHz", 7)) { + ptr = cpu_info_strval(ptr); + info->mhz = atoi(ptr); + } + else if (strnEQ(ptr, "cache size", 10)) { + ptr = cpu_info_strval(ptr); + info->cache_size = sigar_strtoul(ptr); + } + break; + case 's': + /* this is horseshit. why doesn't linux have a real api + * like every other operation system. if siblings == 1 + * then hyperthreading is disabled, so we shouldn't fold + * the dups based on physical id attribute. + */ + if (strnEQ(ptr, "siblings", 8)) { + ptr = cpu_info_strval(ptr); + siblings = atoi(ptr); + if (siblings == 1) { + *id = -1; + } + } + break; + case 't': + /* same as siblings, renamed in new kernels */ + if (strnEQ(ptr, "threads", 7)) { + ptr = cpu_info_strval(ptr); + siblings = atoi(ptr); + if (siblings == 1) { + *id = -1; + } + } + break; + /* lone \n means end of info for this processor */ + case '\n': + return found; + } + } + + return found; +} + +int sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + FILE *fp; + int id; + int cpu_id[36], cpu_ix=0; + /* in the event that a box has > 36 cpus */ + int cpu_id_max = sizeof(cpu_id)/sizeof(int); + + if (!(fp = fopen("/proc/cpuinfo", "r"))) { + return errno; + } + + sigar_cpu_infos_create(cpu_infos); + memset(&cpu_id[0], -1, sizeof(cpu_id)); + + while (get_cpu_info(&cpu_infos->data[cpu_infos->number], fp, &id)) { + if (id >= 0) { + int i, fold=0; + for (i=0; (inumber; + SIGAR_CPU_INFOS_GROW(cpu_infos); + } + + fclose(fp); + + return SIGAR_OK; +} + +static unsigned int hex2int(const char *x) +{ + int i, ch; + unsigned int j; + + for (i=0, j=0; i<8; i++) { + ch = x[i]; + j <<= 4; + if (isdigit(ch)) { + j |= ch - '0'; + } + else if (isupper(ch)) { + j |= ch - ('A' - 10); + } + else { + j |= ch - ('a' - 10); + } + } + + return j; +} + +#define ROUTE_FMT "%16s %128s %128s %X %lld %lld %lld %128s %lld %lld %lld\n" +#define RTF_UP 0x0001 + +int sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + FILE *fp; + char buffer[1024]; + char net_addr[128], gate_addr[128], mask_addr[128]; + int flags; + sigar_net_route_t *route; + + routelist->size = routelist->number = 0; + + if (!(fp = fopen("/proc/net/route", "r"))) { + return errno; + } + + sigar_net_route_list_create(routelist); + + (void)fgets(buffer, sizeof(buffer), fp); /* skip header */ + while (fgets(buffer, sizeof(buffer), fp)) { + int num; + + SIGAR_NET_ROUTE_LIST_GROW(routelist); + route = &routelist->data[routelist->number++]; + + /* XXX rid sscanf */ + num = sscanf(buffer, ROUTE_FMT, + route->ifname, net_addr, gate_addr, + &flags, &route->refcnt, &route->use, + &route->metric, mask_addr, + &route->mtu, &route->window, &route->irtt); + + if ((num < 10) || !(flags & RTF_UP)) { + --routelist->number; + continue; + } + + route->flags = flags; + route->destination = hex2int(net_addr); + route->gateway = hex2int(gate_addr); + route->mask = hex2int(mask_addr); + } + + fclose(fp); + + return SIGAR_OK; +} + +int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + int found = 0; + char buffer[BUFSIZ]; + FILE *fp = fopen("/proc/net/dev", "r"); + + if (!fp) { + return errno; + } + + /* skip header */ + fgets(buffer, sizeof(buffer), fp); + fgets(buffer, sizeof(buffer), fp); + + while (fgets(buffer, sizeof(buffer), fp)) { + char *ptr, *dev; + + dev = buffer; + while (isspace(*dev)) { + dev++; + } + + if (!(ptr = strchr(dev, ':'))) { + continue; + } + + *ptr++ = 0; + + if (!strEQ(dev, name)) { + continue; + } + + found = 1; + ifstat->rx_bytes = sigar_strtoul(ptr); + ifstat->rx_packets = sigar_strtoul(ptr); + ifstat->rx_errors = sigar_strtoul(ptr); + ifstat->rx_dropped = sigar_strtoul(ptr); + ifstat->rx_overruns = sigar_strtoul(ptr); + ifstat->rx_frame = sigar_strtoul(ptr); + + /* skip: compressed multicast */ + ptr = sigar_skip_multiple_token(ptr, 2); + + ifstat->tx_bytes = sigar_strtoul(ptr); + ifstat->tx_packets = sigar_strtoul(ptr); + ifstat->tx_errors = sigar_strtoul(ptr); + ifstat->tx_dropped = sigar_strtoul(ptr); + ifstat->tx_overruns = sigar_strtoul(ptr); + ifstat->tx_collisions = sigar_strtoul(ptr); + ifstat->tx_carrier = sigar_strtoul(ptr); + break; + } + + fclose(fp); + + return found ? SIGAR_OK : ENXIO; +} + +static SIGAR_INLINE int ip_format(char *buffer, int buflen, char *ptr) +{ + int alen = strlen(ptr); + + if (alen > 8) { + struct in6_addr addr; + int i; + for (i=0; i<=3; i++, ptr+=8) { + addr.s6_addr32[i] = hex2int(ptr); + } + if (!inet_ntop(AF_INET6, &addr, buffer, buflen)) { + return errno; + } + } + else { + struct in_addr addr; + addr.s_addr = hex2int(ptr); + if (!inet_ntop(AF_INET, &addr, buffer, buflen)) { + return errno; + } + } + + return SIGAR_OK; +} + +typedef struct { + sigar_net_connection_list_t *connlist; + sigar_net_connection_t *conn; + unsigned long port; +} net_conn_getter_t; + +#define CONN_GET_DONE -2 + +static int proc_net_read(net_conn_getter_t *getter, + const char *fname, + int flags, int type) +{ + FILE *fp; + char buffer[8192], *ptr; + + if (!(fp = fopen(fname, "r"))) { + return errno; + } + + fgets(buffer, sizeof(buffer), fp); /* skip header */ + + while ((ptr = fgets(buffer, sizeof(buffer), fp))) { + sigar_net_connection_t conn; + char *port = NULL; + char *laddr, *raddr; + int status; + + ptr = sigar_skip_token(ptr); /* skip number */ + while (isspace(*ptr)) { + ptr++; + } + + port = strchr(ptr, ':'); + *port = '\0'; + ++port; + + conn.local_port = (strtoul(port, &port, 16) & 0xffff); + laddr = ptr; + + ptr = port; + while (isspace(*ptr)) { + ptr++; + } + + port = strchr(ptr, ':'); + *port = '\0'; + ++port; + + conn.remote_port = (strtoul(port, &port, 16) & 0xffff); + raddr = ptr; + + ptr = port; + while (isspace(*ptr)) { + ptr++; + } + + if (!((conn.remote_port && (flags & SIGAR_NETCONN_CLIENT)) || + (!conn.remote_port && (flags & SIGAR_NETCONN_SERVER)))) + { + continue; + } + + conn.type = type; + + status = ip_format(conn.local_address, + sizeof(conn.local_address), + laddr); + + if (status != SIGAR_OK) { + return status; + } + + status = ip_format(conn.remote_address, + sizeof(conn.remote_address), + raddr); + + if (status != SIGAR_OK) { + return status; + } + + /* XXX state, rx/tx queue info would be useful */ + ptr = sigar_skip_multiple_token(ptr, 4); + conn.uid = sigar_strtoul(ptr); + + ptr = sigar_skip_token(ptr); + + conn.inode = sigar_strtoul(ptr); + + if (getter->connlist) { + SIGAR_NET_CONNLIST_GROW(getter->connlist); + memcpy(&getter->connlist->data[getter->connlist->number++], + &conn, sizeof(conn)); + } + else { + if ((getter->port == conn.local_port) && + (conn.remote_port == 0)) + { + memcpy(getter->conn, &conn, sizeof(conn)); + fclose(fp); + return CONN_GET_DONE; + } + } + } + + fclose(fp); + + return SIGAR_OK; +} + +static int net_conn_get(sigar_t *sigar, + net_conn_getter_t *getter, + int flags) +{ + int status; + + if (flags & SIGAR_NETCONN_TCP) { + status = proc_net_read(getter, + "/proc/net/tcp", + flags, SIGAR_NETCONN_TCP); + + if (status != SIGAR_OK) { + return status; + } + + status = proc_net_read(getter, + "/proc/net/tcp6", + flags, SIGAR_NETCONN_TCP); + + if (!((status == SIGAR_OK) || (status == ENOENT))) { + return status; + } + } + + if (flags & SIGAR_NETCONN_UDP) { + status = proc_net_read(getter, + "/proc/net/udp", + flags, SIGAR_NETCONN_UDP); + + if (status != SIGAR_OK) { + return status; + } + + status = proc_net_read(getter, + "/proc/net/udp6", + flags, SIGAR_NETCONN_UDP); + + if (!((status == SIGAR_OK) || (status == ENOENT))) { + return status; + } + } + + if (flags & SIGAR_NETCONN_RAW) { + status = proc_net_read(getter, + "/proc/net/raw", + flags, SIGAR_NETCONN_RAW); + + if (status != SIGAR_OK) { + return status; + } + + status = proc_net_read(getter, + "/proc/net/raw6", + flags, SIGAR_NETCONN_RAW); + + if (!((status == SIGAR_OK) || (status == ENOENT))) { + return status; + } + } + + /* XXX /proc/net/unix */ + + return SIGAR_OK; +} + +int sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + int status; + net_conn_getter_t getter; + + sigar_net_connection_list_create(connlist); + + getter.conn = NULL; + getter.connlist = connlist; + + status = net_conn_get(sigar, &getter, flags); + + if (status != SIGAR_OK) { + sigar_net_connection_list_destroy(sigar, connlist); + } + + return status; +} + +static int sigar_net_connection_get(sigar_t *sigar, + sigar_net_connection_t *netconn, + unsigned long port, + int flags) +{ + int status; + net_conn_getter_t getter; + + getter.conn = netconn; + getter.connlist = NULL; + getter.port = port; + + status = net_conn_get(sigar, &getter, flags); + + if (status == CONN_GET_DONE) { + return SIGAR_OK; + } + + return status; +} + +int sigar_proc_port_get(sigar_t *sigar, unsigned long port, sigar_pid_t *pid) +{ + int status; + sigar_net_connection_t netconn; + DIR *dirp; + struct dirent *ent, dbuf; + + SIGAR_ZERO(&netconn); + *pid = 0; + + status = sigar_net_connection_get(sigar, &netconn, port, + SIGAR_NETCONN_SERVER| + SIGAR_NETCONN_TCP|SIGAR_NETCONN_UDP); + + if (status != SIGAR_OK) { + return status; + } + + if (netconn.local_port != port) { + return SIGAR_OK; /* XXX or ENOENT? */ + } + + if (!(dirp = opendir("/proc"))) { + return errno; + } + + while (readdir_r(dirp, &dbuf, &ent) == 0) { + DIR *fd_dirp; + struct dirent *fd_ent, fd_dbuf; + struct stat sb; + char fd_name[BUFSIZ], pid_name[BUFSIZ]; + int len, slen; + + if (ent == NULL) { + break; + } + + if (!sigar_isdigit(*ent->d_name)) { + continue; + } + + /* sprintf(pid_name, "/proc/%s", ent->d_name) */ + memcpy(&pid_name[0], PROC_FS_ROOT, SSTRLEN(PROC_FS_ROOT)); + len = SSTRLEN(PROC_FS_ROOT); + pid_name[len++] = '/'; + + slen = strlen(ent->d_name); + memcpy(&pid_name[len], ent->d_name, slen); + len += slen; + pid_name[len] = '\0'; + + if (stat(pid_name, &sb) < 0) { + continue; + } + if (sb.st_uid != netconn.uid) { + continue; + } + + /* sprintf(fd_name, "%s/fd", pid_name) */ + memcpy(&fd_name[0], pid_name, len); + memcpy(&fd_name[len], "/fd", 3); + fd_name[len+=3] = '\0'; + + if (!(fd_dirp = opendir(fd_name))) { + continue; + } + + while (readdir_r(fd_dirp, &fd_dbuf, &fd_ent) == 0) { + char fd_ent_name[BUFSIZ]; + + if (fd_ent == NULL) { + break; + } + + if (!sigar_isdigit(*fd_ent->d_name)) { + continue; + } + + /* sprintf(fd_ent_name, "%s/%s", fd_name, fd_ent->d_name) */ + slen = strlen(fd_ent->d_name); + memcpy(&fd_ent_name[0], fd_name, len); + fd_ent_name[len] = '/'; + memcpy(&fd_ent_name[len+1], fd_ent->d_name, slen); + fd_ent_name[len+1+slen] = '\0'; + + if (stat(fd_ent_name, &sb) < 0) { + continue; + } + + if (sb.st_ino == netconn.inode) { + closedir(fd_dirp); + closedir(dirp); + *pid = strtoul(ent->d_name, NULL, 10); + return SIGAR_OK; + } + + } + + closedir(fd_dirp); + } + + closedir(dirp); + + return SIGAR_OK; +} diff --git a/src/os/linux/sigar_os.h b/src/os/linux/sigar_os.h new file mode 100644 index 00000000..c5ad9f23 --- /dev/null +++ b/src/os/linux/sigar_os.h @@ -0,0 +1,47 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct { + sigar_pid_t pid; + time_t mtime; + sigar_uint64_t vsize; + sigar_uint64_t rss; + sigar_uint64_t ppid; + int tty; + int priority; + int nice; + sigar_uint64_t start_time; + sigar_uint64_t utime; + sigar_uint64_t stime; + char name[SIGAR_PROC_NAME_LEN]; + char state; +} linux_proc_stat_t; + +struct sigar_t { + SIGAR_T_BASE; + int ram; + int proc_signal_offset; + linux_proc_stat_t last_proc_stat; +}; + +#define HAVE_STRERROR_R +#define HAVE_STRERROR_R_GLIBC +#define HAVE_READDIR_R +#define HAVE_GETPWNAM_R +#define HAVE_GETPWUID_R +#define HAVE_GETGRGID_R + +#endif /* SIGAR_OS_H */ diff --git a/src/os/solaris/hmekstat.h b/src/os/solaris/hmekstat.h new file mode 100644 index 00000000..a3872b32 --- /dev/null +++ b/src/os/solaris/hmekstat.h @@ -0,0 +1,77 @@ +/* + * from sys/hme.h, private to _KERNEL. + * we should be ok provided ksp->ks_data_size == sizeof(struct hmekstat) + * else will need to fallback to using kstat_data_lookup. + */ + +struct hmekstat { + struct kstat_named hk_ipackets; /* packets received */ + struct kstat_named hk_ierrors; /* input errors */ + struct kstat_named hk_opackets; /* packets transmitted */ + struct kstat_named hk_oerrors; /* output errors */ + struct kstat_named hk_coll; /* collisions encountered */ + struct kstat_named hk_defer; /* slots deferred */ + struct kstat_named hk_fram; /* framing errors */ + struct kstat_named hk_crc; /* crc errors */ + struct kstat_named hk_sqerr; /* SQE test errors */ + struct kstat_named hk_cvc; /* code violation errors */ + struct kstat_named hk_lenerr; /* rx len errors */ + struct kstat_named hk_ifspeed; /* interface speed */ + struct kstat_named hk_buff; /* buff errors */ + struct kstat_named hk_oflo; /* overflow errors */ + struct kstat_named hk_uflo; /* underflow errors */ + struct kstat_named hk_missed; /* missed/dropped packets */ + struct kstat_named hk_tlcol; /* late collisions */ + struct kstat_named hk_trtry; /* retry errors */ + struct kstat_named hk_fstcol; /* first collisions */ + struct kstat_named hk_tnocar; /* no carrier */ + struct kstat_named hk_inits; /* initialization */ + struct kstat_named hk_nocanput; /* nocanput errors */ + struct kstat_named hk_allocbfail; /* allocb failures */ + struct kstat_named hk_runt; /* runt errors */ + struct kstat_named hk_jab; /* jabber errors */ + struct kstat_named hk_babl; /* runt errors */ + struct kstat_named hk_tmder; /* tmd errors */ + struct kstat_named hk_txlaterr; /* tx late errors */ + struct kstat_named hk_rxlaterr; /* rx late errors */ + struct kstat_named hk_slvparerr; /* slave parity errors */ + struct kstat_named hk_txparerr; /* tx parity errors */ + struct kstat_named hk_rxparerr; /* rx parity errors */ + struct kstat_named hk_slverrack; /* slave error acks */ + struct kstat_named hk_txerrack; /* tx error acks */ + struct kstat_named hk_rxerrack; /* rx error acks */ + struct kstat_named hk_txtagerr; /* tx tag error */ + struct kstat_named hk_rxtagerr; /* rx tag error */ + struct kstat_named hk_eoperr; /* eop error */ + struct kstat_named hk_notmds; /* tmd errors */ + struct kstat_named hk_notbufs; /* tx buf errors */ + struct kstat_named hk_norbufs; /* rx buf errors */ + struct kstat_named hk_clsn; /* clsn errors */ + + /* + * required by kstat for MIB II objects(RFC 1213) + */ + struct kstat_named hk_rcvbytes; /* # octets received */ + /* MIB - ifInOctets */ + struct kstat_named hk_xmtbytes; /* # octets transmitted */ + /* MIB - ifOutOctets */ + struct kstat_named hk_multircv; /* # multicast packets */ + /* delivered to upper layer */ + /* MIB - ifInNUcastPkts */ + struct kstat_named hk_multixmt; /* # multicast packets */ + /* requested to be sent */ + /* MIB - ifOutNUcastPkts */ + struct kstat_named hk_brdcstrcv; /* # broadcast packets */ + /* delivered to upper layer */ + /* MIB - ifInNUcastPkts */ + struct kstat_named hk_brdcstxmt; /* # broadcast packets */ + /* requested to be sent */ + /* MIB - ifOutNUcastPkts */ + struct kstat_named hk_norcvbuf; /* # rcv packets discarded */ + /* MIB - ifInDiscards */ + struct kstat_named hk_noxmtbuf; /* # xmt packets discarded */ + /* MIB - ifOutDiscards */ + + struct kstat_named hk_phyfail; /* phy failures */ + struct kstat_named hk_link_up; /* Link Status */ +}; diff --git a/src/os/solaris/kstats.c b/src/os/solaris/kstats.c new file mode 100644 index 00000000..095265cb --- /dev/null +++ b/src/os/solaris/kstats.c @@ -0,0 +1,255 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +int sigar_get_multi_kstats(sigar_t *sigar, + kstat_list_t *kl, + const char *name, + kstat_t **retval) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + int i = 0; + int dev; + + name += kl->nlen; /* e.g. "hme0" + 3 */ + dev = atoi(name); + + if ((kl->num == 0) || (kstat_chain_update(kc) > 0)) { + while ((ksp = kstat_lookup(kc, kl->name, i, NULL))) { + if (i+1 > kl->num) { + kl->num = i+1; + kl->ks = realloc(kl->ks, kl->num); + } + kl->ks[i] = ksp; + i++; + } + } + + if (dev >= kl->num) { + return ENXIO; + } + + *retval = kl->ks[dev]; + + return SIGAR_OK; +} + +int sigar_get_kstats(sigar_t *sigar) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + unsigned int i, ncpu = sysconf(_SC_NPROCESSORS_CONF); + + ksp = kstat_lookup(kc, "unix", -1, "vminfo"); + sigar->ks.vminfo = ksp; + + if (ksp) { + kstat_read(kc, ksp, &sigar->vminfo); + + if (!sigar->vminfo_snaptime) { + sigar->vminfo_snaptime = ksp->ks_snaptime; + } + } + + if (ncpu != sigar->ncpu) { + if (!sigar->ks.lcpu) { + /* init */ + sigar->ks.lcpu = ncpu; + sigar->ks.cpu = malloc(sizeof(*(sigar->ks.cpu)) * ncpu); + sigar->ks.cpuid = malloc(sizeof(*(sigar->ks.cpuid)) * ncpu); + } + else { + if (ncpu > sigar->ks.lcpu) { + /* one or more cpus have been added */ + sigar->ks.cpu = realloc(sigar->ks.cpu, + sizeof(*(sigar->ks.cpu)) * ncpu); + sigar->ks.cpuid = realloc(sigar->ks.cpuid, + sizeof(*(sigar->ks.cpuid)) * ncpu); + sigar->ks.lcpu = ncpu; + } + /* else or more cpus have been removed */ + } + + sigar->ncpu = ncpu; + + for (i=0, ksp=kc->kc_chain; iks_next) { + char *id; + + if (!ksp) { + break; + } + if (strncmp(ksp->ks_name, "cpu_stat", 8)) { + continue; + } + + /* from man p_online: + * ``Processor numbers are integers, + * greater than or equal to 0, + * and are defined by the hardware platform. + * Processor numbers are not necessarily contiguous, + * but "not too sparse."`` + * so we maintain our own mapping in ks.cpuid[] + */ + id = ksp->ks_name; + while (!sigar_isdigit(*id)) { + id++; + } + + sigar->ks.cpu[i] = ksp; + sigar->ks.cpuid[i] = atoi(id); + + i++; + } + } + + sigar->ks.system = kstat_lookup(kc, "unix", -1, "system_misc"); + sigar->ks.syspages = kstat_lookup(kc, "unix", -1, "system_pages"); + sigar->ks.mempages = kstat_lookup(kc, "bunyip", -1, "mempages"); + + return SIGAR_OK; +} + +/* + * bincompat is not possible with certain kstat data structures between + * solaris 2.6, 2.7, 2.8, etc. alternative is to use kstat_data_lookup() + * which means everytime we want a stat, must do a linear search + * of ksp->ks_data. eek. so we meet half way and do the search for + * each key once per sigar_t instance. once the initial search has + * been done, we have a table of offsets to quickly access the stats via + * ksp->ks_data + offset. this gives us bincompat without the overhead + * of many kstat_data_lookup calls. + */ +static SIGAR_INLINE int kstat_named_offset(kstat_t *ksp, const char *name) +{ + unsigned int i; + kstat_named_t *kn; + + for (i=0, kn=ksp->ks_data; + iks_ndata; + i++, kn++) + { + if (strEQ(kn->name, name)) { + return i; + } + } + + return -2; /* not found */ +} + +static char *kstat_keys_lo[] = { + "ipackets", /* RX_PACKETS */ + "opackets", /* TX_PACKETS */ + NULL +}; + +static char *kstat_keys_hme[] = { + "ipackets", /* RX_PACKETS */ + "rbytes", /* RX_BYTES */ + "ierrors", /* RX_ERRORS */ + "missed", /* RX_DROPPED */ + "oflo", /* RX_OVERRUNS */ + "framing", /* RX_FRAME */ + "opackets", /* TX_PACKETS */ + "obytes", /* TX_BYTES */ + "oerrors", /* TX_ERRORS */ + "missed", /* TX_DROPPED */ + "oflo", /* TX_OVERRUNS */ + "collisions", /* TX_COLLISIONS */ + "nocarrier", /* TX_CARRIER */ + NULL +}; + +static char *kstat_keys_dmfe[] = { + "ipackets", /* RX_PACKETS */ + "rbytes", /* RX_BYTES */ + "ierrors", /* RX_ERRORS */ + "missed", /* RX_DROPPED */ + "oflo", /* RX_OVERRUNS */ + "framing", /* RX_FRAME */ + "opackets", /* TX_PACKETS */ + "obytes", /* TX_BYTES */ + "oerrors", /* TX_ERRORS */ + "missed", /* TX_DROPPED */ + "oflo", /* TX_OVERRUNS */ + "collisions", /* TX_COLLISIONS */ + "nocarrier", /* TX_CARRIER */ + NULL +}; + +static char *kstat_keys_ge[] = { + "ipackets", /* RX_PACKETS */ + "rbytes", /* RX_BYTES */ + "ierrors", /* RX_ERRORS */ + "drop", /* RX_DROPPED */ + "toolong_errors", /* RX_OVERRUNS */ + "framing", /* RX_FRAME */ + "opackets", /* TX_PACKETS */ + "obytes", /* TX_BYTES */ + "oerrors", /* TX_ERRORS */ + "drop", /* TX_DROPPED */ + "toolong_errors", /* TX_OVERRUNS */ + "collisions", /* TX_COLLISIONS */ + "nocarrier", /* TX_CARRIER */ + NULL +}; + +static char *kstat_keys_eri[] = { + "ipackets", /* RX_PACKETS */ + "rbytes", /* RX_BYTES */ + "ierrors", /* RX_ERRORS */ + "drop", /* RX_DROPPED */ + "rx_overflow", /* RX_OVERRUNS */ + "parity_error", /* RX_FRAME */ + "opackets", /* TX_PACKETS */ + "obytes", /* TX_BYTES */ + "oerrors", /* TX_ERRORS */ + "drop", /* TX_DROPPED */ + "rx_overflow", /* TX_OVERRUNS */ + "collisions", /* TX_COLLISIONS */ + "nocarrier", /* TX_CARRIER */ + NULL +}; + +static char *kstat_keys_system[] = { + "boot_time", + "avenrun_1min", + "avenrun_5min", + "avenrun_15min", + NULL +}; + +static char *kstat_keys_mempages[] = { + "pages_anon", + "pages_exec", + "pages_vnode", + NULL +}; + +static char *kstat_keys_syspages[] = { + "pagesfree", + NULL +}; + +static char **kstat_keys[] = { + kstat_keys_lo, + kstat_keys_hme, + kstat_keys_dmfe, + kstat_keys_ge, + kstat_keys_eri, + kstat_keys_system, + kstat_keys_mempages, + kstat_keys_syspages, +}; + +void sigar_koffsets_lookup(kstat_t *ksp, int *offsets, int kidx) +{ + int i; + char **keys = kstat_keys[kidx]; + + for (i=0; keys[i]; i++) { + offsets[i] = kstat_named_offset(ksp, keys[i]); + } +} + diff --git a/src/os/solaris/procfs.c b/src/os/solaris/procfs.c new file mode 100644 index 00000000..85ca248e --- /dev/null +++ b/src/os/solaris/procfs.c @@ -0,0 +1,101 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#define my_pread(fd, ptr, type, offset) \ + (pread(fd, ptr, sizeof(type), offset) == sizeof(type)) + +int sigar_proc_psinfo_get(sigar_t *sigar, sigar_pid_t pid) +{ + int fd, retval = SIGAR_OK; + char buffer[BUFSIZ]; + time_t timenow = time(NULL); + + if (sigar->pinfo == NULL) { + sigar->pinfo = malloc(sizeof(*sigar->pinfo)); + } + + if (sigar->last_pid == pid) { + if ((timenow - sigar->last_getprocs) < SIGAR_LAST_PROC_EXPIRE) { + return SIGAR_OK; + } + } + + sigar->last_pid = pid; + sigar->last_getprocs = timenow; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/psinfo"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + return ESRCH; + } + + if (!my_pread(fd, sigar->pinfo, psinfo_t, 0)) { + retval = errno; + } + + close(fd); + + return retval; +} + +int sigar_proc_usage_get(sigar_t *sigar, prusage_t *prusage, sigar_pid_t pid) +{ + int fd, retval = SIGAR_OK; + char buffer[BUFSIZ]; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/usage"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + return ESRCH; + } + + if (!my_pread(fd, prusage, prusage_t, 0)) { + retval = errno; + } + + close(fd); + + return retval; +} + +int sigar_get_cred_get(sigar_t *sigar, prcred_t *prcred, sigar_pid_t pid) +{ + int fd, retval = SIGAR_OK; + char buffer[BUFSIZ]; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/cred"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + return ESRCH; + } + + if (!my_pread(fd, prcred, prcred_t, 0)) { + retval = errno; + } + + close(fd); + + return retval; +} + +int sigar_get_proc_status(sigar_t *sigar, pstatus_t *pstatus, sigar_pid_t pid) +{ + int fd, retval = SIGAR_OK; + char buffer[BUFSIZ]; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/status"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + return ESRCH; + } + + if (!my_pread(fd, pstatus, pstatus_t, 0)) { + retval = errno; + } + + close(fd); + + return retval; +} diff --git a/src/os/solaris/sigar_os.h b/src/os/solaris/sigar_os.h new file mode 100644 index 00000000..08b6a3d5 --- /dev/null +++ b/src/os/solaris/sigar_os.h @@ -0,0 +1,249 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +typedef unsigned long long int u_int64_t; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* avoid -Wall warning since solaris doesnt have a prototype for this */ +int getdomainname(char *, int); + +typedef struct { + kstat_t **ks; + int num; + char *name; + int nlen; +} kstat_list_t; + +int sigar_get_kstats(sigar_t *sigar); + +int sigar_get_multi_kstats(sigar_t *sigar, + kstat_list_t *kl, + const char *name, + kstat_t **retval); + +void sigar_koffsets_lookup(kstat_t *ksp, int *offsets, int kidx); + +int sigar_proc_psinfo_get(sigar_t *sigar, sigar_pid_t pid); + +int sigar_proc_usage_get(sigar_t *sigar, prusage_t *prusage, sigar_pid_t pid); + +int sigar_get_cred_get(sigar_t *sigar, prcred_t *prcred, sigar_pid_t pid); + +int sigar_get_proc_status(sigar_t *sigar, pstatus_t *pstatus, sigar_pid_t pid); + +#define CPU_ONLINE(n) \ + (p_online(n, P_STATUS) == P_ONLINE) + +/* loopback interface only has these two metrics */ +typedef enum { + KSTAT_LO_RX_PACKETS, + KSTAT_LO_TX_PACKETS, + KSTAT_LO_MAX +} kstat_lo_off_e; + +/* hme, ge and dmfe network devices provide + * the same metrics, but in some cases with + * different names and in all cases, the + * offsets are different. + */ +typedef enum { + KSTAT_HME_RX_PACKETS, + KSTAT_HME_RX_BYTES, + KSTAT_HME_RX_ERRORS, + KSTAT_HME_RX_DROPPED, + KSTAT_HME_RX_OVERRUNS, + KSTAT_HME_RX_FRAME, + KSTAT_HME_TX_PACKETS, + KSTAT_HME_TX_BYTES, + KSTAT_HME_TX_ERRORS, + KSTAT_HME_TX_DROPPED, + KSTAT_HME_TX_OVERRUNS, + KSTAT_HME_TX_COLLISIONS, + KSTAT_HME_TX_CARRIER, + KSTAT_HME_MAX +} kstat_hme_off_e; + +typedef enum { + KSTAT_DMFE_RX_PACKETS, + KSTAT_DMFE_RX_BYTES, + KSTAT_DMFE_RX_ERRORS, + KSTAT_DMFE_RX_DROPPED, + KSTAT_DMFE_RX_OVERRUNS, + KSTAT_DMFE_RX_FRAME, + KSTAT_DMFE_TX_PACKETS, + KSTAT_DMFE_TX_BYTES, + KSTAT_DMFE_TX_ERRORS, + KSTAT_DMFE_TX_DROPPED, + KSTAT_DMFE_TX_OVERRUNS, + KSTAT_DMFE_TX_COLLISIONS, + KSTAT_DMFE_TX_CARRIER, + KSTAT_DMFE_MAX +} kstat_dmfe_off_e; + +typedef enum { + KSTAT_GE_RX_PACKETS, + KSTAT_GE_RX_BYTES, + KSTAT_GE_RX_ERRORS, + KSTAT_GE_RX_DROPPED, + KSTAT_GE_RX_OVERRUNS, + KSTAT_GE_RX_FRAME, + KSTAT_GE_TX_PACKETS, + KSTAT_GE_TX_BYTES, + KSTAT_GE_TX_ERRORS, + KSTAT_GE_TX_DROPPED, + KSTAT_GE_TX_OVERRUNS, + KSTAT_GE_TX_COLLISIONS, + KSTAT_GE_TX_CARRIER, + KSTAT_GE_MAX +} kstat_ge_off_e; + +typedef enum { + KSTAT_ERI_RX_PACKETS, + KSTAT_ERI_RX_BYTES, + KSTAT_ERI_RX_ERRORS, + KSTAT_ERI_RX_DROPPED, + KSTAT_ERI_RX_OVERRUNS, + KSTAT_ERI_RX_FRAME, + KSTAT_ERI_TX_PACKETS, + KSTAT_ERI_TX_BYTES, + KSTAT_ERI_TX_ERRORS, + KSTAT_ERI_TX_DROPPED, + KSTAT_ERI_TX_OVERRUNS, + KSTAT_ERI_TX_COLLISIONS, + KSTAT_ERI_TX_CARRIER, + KSTAT_ERI_MAX +} kstat_eri_off_e; + +typedef enum { + KSTAT_SYSTEM_BOOT_TIME, + KSTAT_SYSTEM_LOADAVG_1, + KSTAT_SYSTEM_LOADAVG_2, + KSTAT_SYSTEM_LOADAVG_3, + KSTAT_SYSTEM_MAX +} kstat_system_off_e; + +typedef enum { + KSTAT_MEMPAGES_ANON, + KSTAT_MEMPAGES_EXEC, + KSTAT_MEMPAGES_VNODE, + KSTAT_MEMPAGES_MAX +} kstat_mempages_off_e; + +typedef enum { + KSTAT_SYSPAGES_FREE, + KSTAT_SYSPAGES_MAX +} kstat_syspages_off_e; + +enum { + KSTAT_KEYS_lo, + KSTAT_KEYS_hme, + KSTAT_KEYS_dmfe, + KSTAT_KEYS_ge, + KSTAT_KEYS_eri, + KSTAT_KEYS_system, + KSTAT_KEYS_mempages, + KSTAT_KEYS_syspages, +} kstat_keys_e; + +struct sigar_t { + SIGAR_T_BASE; + + kstat_ctl_t *kc; + + /* kstat_lookup() as needed */ + struct { + kstat_t *vminfo; + kstat_t **cpu; + processorid_t *cpuid; + unsigned int lcpu; /* number malloced slots in the cpu array above */ + kstat_t *system; + kstat_t *syspages; + kstat_t *mempages; + kstat_list_t hme; + kstat_list_t dmfe; + kstat_list_t ge; + kstat_list_t eri; + kstat_list_t lo; + } ks; + + struct { + int lo[KSTAT_LO_MAX]; + int hme[KSTAT_HME_MAX]; + int dmfe[KSTAT_DMFE_MAX]; + int ge[KSTAT_GE_MAX]; + int eri[KSTAT_ERI_MAX]; + int system[KSTAT_SYSTEM_MAX]; + int mempages[KSTAT_MEMPAGES_MAX]; + int syspages[KSTAT_SYSPAGES_MAX]; + } koffsets; + + vminfo_t vminfo; + hrtime_t vminfo_snaptime; + + int pagesize; + + time_t last_getprocs; + sigar_pid_t last_pid; + psinfo_t *pinfo; + sigar_cpu_list_t cpulist; +}; + +#define kSTAT_uint(v, type) \ + ((sigar->koffsets.type[v] == -2) ? 0 : \ + ((kstat_named_t *)ksp->ks_data + sigar->koffsets.type[v])->value.ui32) + +#define kSYSTEM(v) kSTAT_uint(v, system) + +#define kMEMPAGES(v) kSTAT_uint(v, mempages) + +#define kSYSPAGES(v) kSTAT_uint(v, syspages) + +#define sigar_koffsets_init(sigar, ksp, type) \ + if (sigar->koffsets.type[0] == -1) \ + sigar_koffsets_lookup(ksp, sigar->koffsets.type, KSTAT_KEYS_##type) + +#define sigar_koffsets_init_lo(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, lo) + +#define sigar_koffsets_init_hme(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, hme) + +#define sigar_koffsets_init_dmfe(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, dmfe) + +#define sigar_koffsets_init_ge(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, ge) + +#define sigar_koffsets_init_eri(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, eri) + +#define sigar_koffsets_init_system(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, system) + +#define sigar_koffsets_init_mempages(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, mempages) + +#define sigar_koffsets_init_syspages(sigar, ksp) \ + sigar_koffsets_init(sigar, ksp, syspages) + +#define HAVE_READDIR_R +#define HAVE_GETPWNAM_R +#define HAVE_GETPWUID_R + +#endif /* SIGAR_OS_H */ + diff --git a/src/os/solaris/solaris_sigar.c b/src/os/solaris/solaris_sigar.c new file mode 100644 index 00000000..5bd58295 --- /dev/null +++ b/src/os/solaris/solaris_sigar.c @@ -0,0 +1,1110 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#include +#include + +#define KSTAT_LIST_INIT(sigar, dev) \ + sigar->koffsets.dev[0] = -1; \ + sigar->ks.dev.num = 0; \ + sigar->ks.dev.ks = NULL; \ + sigar->ks.dev.name = #dev; \ + sigar->ks.dev.nlen = strlen(#dev) + +int sigar_os_open(sigar_t **sig) +{ + kstat_ctl_t *kc; + kstat_t *ksp; + sigar_t *sigar; + int i, status; + + sigar = malloc(sizeof(*sigar)); + *sig = sigar; + + sigar->pagesize = 0; + i = sysconf(_SC_PAGESIZE); + while ((i >>= 1) > 0) { + sigar->pagesize++; + } + + sigar->ticks = sysconf(_SC_CLK_TCK); + sigar->kc = kc = kstat_open(); + + if (!kc) { + return errno; + } + + sigar->ncpu = 0; + sigar->ks.cpu = NULL; + sigar->ks.cpuid = NULL; + sigar->ks.lcpu = 0; + + KSTAT_LIST_INIT(sigar, lo); + KSTAT_LIST_INIT(sigar, hme); + KSTAT_LIST_INIT(sigar, dmfe); + KSTAT_LIST_INIT(sigar, ge); + KSTAT_LIST_INIT(sigar, eri); + + sigar->vminfo_snaptime = 0; + + sigar->koffsets.system[0] = -1; + sigar->koffsets.mempages[0] = -1; + sigar->koffsets.syspages[0] = -1; + + if ((status = sigar_get_kstats(sigar)) != SIGAR_OK) { + fprintf(stderr, "status=%d\n", status); + } + + sigar->boot_time = 0; + + if ((ksp = sigar->ks.system) && + (kstat_read(kc, ksp, NULL) >= 0)) + { + sigar_koffsets_init_system(sigar, ksp); + + sigar->boot_time = kSYSTEM(KSTAT_SYSTEM_BOOT_TIME); + } + + sigar->last_pid = -1; + sigar->pinfo = NULL; + + return SIGAR_OK; +} + +int sigar_os_close(sigar_t *sigar) +{ + kstat_close(sigar->kc); + + if (sigar->ks.lo.num) { + free(sigar->ks.lo.ks); + } + if (sigar->ks.hme.num) { + free(sigar->ks.hme.ks); + } + if (sigar->ks.dmfe.num) { + free(sigar->ks.dmfe.ks); + } + if (sigar->ks.ge.num) { + free(sigar->ks.ge.ks); + } + if (sigar->ks.eri.num) { + free(sigar->ks.eri.ks); + } + if (sigar->ks.lcpu) { + free(sigar->ks.cpu); + free(sigar->ks.cpuid); + } + if (sigar->pinfo) { + free(sigar->pinfo); + } + free(sigar); + return SIGAR_OK; +} + +char *sigar_os_error_string(int err) +{ + return NULL; +} + +static SIGAR_INLINE int sigar_kstat_update(sigar_t *sigar) +{ + switch (kstat_chain_update(sigar->kc)) { + case 0: + break; + case -1: + return -1; /* shouldn't happen */ + default: + sigar_get_kstats(sigar); + } + + return SIGAR_OK; +} + +#define KPAGE_SHIFT(v) \ + ((v) << sigar->pagesize) + +int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + + SIGAR_ZERO(mem); + + /* XXX: is mem hot swappable or can we just do this during open ? */ + mem->total = KPAGE_SHIFT((sigar_uint64_t)sysconf(_SC_PHYS_PAGES)); + + sigar_mem_calc_ram(sigar, mem); + + sigar_kstat_update(sigar); + + if ((ksp = sigar->ks.syspages) && kstat_read(kc, ksp, NULL) >= 0) { + sigar_koffsets_init_syspages(sigar, ksp); + + mem->free = KPAGE_SHIFT(kSYSPAGES(KSTAT_SYSPAGES_FREE)); + + mem->used = mem->total - mem->free; + } + + if ((ksp = sigar->ks.mempages) && kstat_read(kc, ksp, NULL) >= 0) { + sigar_koffsets_init_mempages(sigar, ksp); + + mem->shared = KPAGE_SHIFT(kMEMPAGES(KSTAT_MEMPAGES_EXEC)); + + mem->buffer = KPAGE_SHIFT(kMEMPAGES(KSTAT_MEMPAGES_VNODE)); + + mem->cached = -1; /*XXX*/ + } + + return SIGAR_OK; +} + +int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ +#if defined(SIGAR_SWAP_KSTAT) + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + vminfo_t vminfo; + + sigar_kstat_update(sigar); + + if ((ksp = sigar->ks.vminfo) && kstat_read(kc, ksp, &vminfo) >= 0) { + /* XXX: need some adjustments here */ + swap->total = vminfo.swap_resv + vminfo.swap_avail; + swap->used = vminfo.swap_alloc; + swap->free = swap->total - swap->used; + return SIGAR_OK; + } + + return -1; +#elif defined(SIGAR_SWAP_SC_LIST) + unsigned int i; + size_t num; + swaptbl_t *stab; + char path[256]; + + swap->total = swap->used = swap->free = 0; + + num = swapctl(SC_GETNSWP, 0); + + switch (num) { + case 0: + return SIGAR_OK; + case -1: + return errno; + default: + break; + } + + stab = malloc((num * sizeof(swapent_t)) + sizeof(swaptbl_t)); + + for (i=0; iswt_ent[i].ste_path = &path[0]; /* ignored */ + } + + stab->swt_n = num; + num = swapctl(SC_LIST, stab); + + for (i=0; itotal += stab->swt_ent[i].ste_pages; + swap->free += stab->swt_ent[i].ste_free; + } + + free(stab); + + swap->total <<= sigar->pagesize; + swap->free <<= sigar->pagesize; + swap->used = swap->total - swap->free; + + return SIGAR_OK; +#else + struct anoninfo anon; + + /* XXX vm/anon.h says: + * "The swap data can be aquired more efficiently through the + * kstats interface." + * but cannot find anything that explains howto convert those numbers. + */ + + if (swapctl(SC_AINFO, &anon) == -1) { + return errno; + } + + swap->total = anon.ani_max; + swap->used = anon.ani_resv; + swap->free = anon.ani_max - anon.ani_resv; + + swap->total <<= sigar->pagesize; + swap->free <<= sigar->pagesize; + swap->used <<= sigar->pagesize; + + return SIGAR_OK; +#endif +} + +int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ + int status, i; + + status = sigar_cpu_list_get(sigar, &sigar->cpulist); + + if (status != SIGAR_OK) { + return status; + } + + SIGAR_ZERO(cpu); + + for (i=0; icpulist.number; i++) { + sigar_cpu_t *xcpu = &sigar->cpulist.data[i]; + + cpu->user += xcpu->user; + cpu->sys += xcpu->sys; + cpu->idle += xcpu->idle; + cpu->nice += xcpu->nice; + cpu->total += xcpu->total; + } + + return SIGAR_OK; +} + +int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + ulong cpuinfo[CPU_STATES]; + unsigned int i, n; + sigar_kstat_update(sigar); + + if (cpulist != &sigar->cpulist) { + sigar_cpu_list_create(cpulist); + } + + for (i=0; incpu; i++) { + sigar_cpu_t *cpu; + char *buf; + + if (!CPU_ONLINE(sigar->ks.cpuid[i])) { + continue; + } + if (!((ksp = sigar->ks.cpu[i]) && + (kstat_read(kc, ksp, NULL) >= 0))) + { + continue; /* XXX: shouldnot happen */ + } + + /* + * cpu_stat_t is not binary compatible between solaris versions. + * since cpu_stat is a 'raw' kstat and not 'named' we cannot + * use name based lookups as we do for others. + * the start of the cpu_stat_t structure is binary compatible, + * which looks like so: + * typedef struct cpu_stat { + * kmutex_t cpu_stat_lock; + * cpu_sysinfo_t cpu_sysinfo; + * ... + * typedef struct cpu_sysinfo { + * ulong cpu[CPU_STATES]; + * ... + * we just copy the piece we need below: + */ + buf = ksp->ks_data; + buf += sizeof(kmutex_t); + memcpy(&cpuinfo[0], buf, sizeof(cpuinfo)); + + SIGAR_CPU_LIST_GROW(cpulist); + + cpu = &cpulist->data[cpulist->number++]; + + cpu->user = cpuinfo[CPU_USER]; + cpu->sys = cpuinfo[CPU_KERNEL]; + cpu->idle = cpuinfo[CPU_IDLE]; + cpu->nice = 0; /* no cpu->nice */ + cpu->total = 0; + + for (n=0; ntotal += cpuinfo[n]; + } + } + + return SIGAR_OK; +} + +int sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + uptime->idletime = 0; /*XXX*/ + + if (sigar->boot_time) { + uptime->uptime = time(NULL) - sigar->boot_time; + } + else { + uptime->uptime = 0; /* XXX: shouldn't happen */ + } + + return SIGAR_OK; +} + +static int loadavg_keys[] = { + KSTAT_SYSTEM_LOADAVG_1, + KSTAT_SYSTEM_LOADAVG_2, + KSTAT_SYSTEM_LOADAVG_3 +}; + +int sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + kstat_t *ksp; + int i; + + sigar_kstat_update(sigar); + + if (!(ksp = sigar->ks.system)) { + return -1; + } + + if (kstat_read(sigar->kc, ksp, NULL) < 0) { + return -1; + } + + sigar_koffsets_init_system(sigar, ksp); + + for (i=0; i<3; i++) { + loadavg->loadavg[i] = (double)kSYSTEM(loadavg_keys[i]) / FSCALE; + } + + return SIGAR_OK; +} + +int sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + return sigar_proc_list_procfs_get(sigar, proclist); +} + +int sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + int status = sigar_proc_psinfo_get(sigar, pid); + psinfo_t *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + procmem->size = pinfo->pr_size << 10; + procmem->rss = pinfo->pr_rssize << 10; + procmem->resident = procmem->rss; /*XXX*/ + procmem->vsize = procmem->size; /*XXX*/ + procmem->share = 0; /*XXX*/ + + return SIGAR_OK; +} + +int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + int status = sigar_proc_psinfo_get(sigar, pid); + psinfo_t *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + proccred->uid = pinfo->pr_uid; + proccred->gid = pinfo->pr_gid; + proccred->euid = pinfo->pr_euid; + proccred->egid = pinfo->pr_egid; + + return SIGAR_OK; +} + +#define PRTIME_2SIGAR(t) \ + (t.tv_sec + t.tv_nsec / NANOSEC) + +int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + prusage_t usage; + int status; + + if ((status = sigar_proc_usage_get(sigar, &usage, pid)) != SIGAR_OK) { + return status; + } + + proctime->start_time = usage.pr_create.tv_sec + sigar->boot_time; + proctime->start_time *= 1000; + + proctime->utime = PRTIME_2SIGAR(usage.pr_utime); + proctime->stime = PRTIME_2SIGAR(usage.pr_stime); + + return SIGAR_OK; +} + +int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + int status = sigar_proc_psinfo_get(sigar, pid); + psinfo_t *pinfo = sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + SIGAR_SSTRCPY(procstate->name, pinfo->pr_fname); + procstate->ppid = pinfo->pr_ppid; + procstate->tty = pinfo->pr_ttydev; + procstate->priority = pinfo->pr_lwp.pr_pri; + procstate->nice = pinfo->pr_lwp.pr_nice - NZERO; + + switch (pinfo->pr_lwp.pr_state) { + case SONPROC: + case SRUN: + procstate->state = 'R'; + break; + case SZOMB: + procstate->state = 'Z'; + break; + case SSLEEP: + procstate->state = 'S'; + break; + case SSTOP: + procstate->state = 'T'; + break; + case SIDL: + procstate->state = 'D'; + break; + } + + return SIGAR_OK; +} + +int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + psinfo_t *pinfo; + int fd, status; + char buffer[BUFSIZ]; + char *argvb[56]; + char *arg, **argvp = argvb; + + int n; + size_t nread = 0; + unsigned int argv_size; + + if ((status = sigar_proc_psinfo_get(sigar, pid)) != SIGAR_OK) { + return status; + } + pinfo = sigar->pinfo; + + argv_size = sizeof(*argvp) * pinfo->pr_argc; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/as"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + return ESRCH; + } + + if (argv_size > sizeof(argvb)) { + argvp = malloc(argv_size); + } + + if ((nread = pread(fd, argvp, argv_size, pinfo->pr_argv)) <= 0) { + close(fd); + return errno; + } + + procargs->number = 0; + procargs->size = pinfo->pr_argc; + procargs->data = + (char **)malloc(sizeof(*(procargs->data)) * + procargs->size); + + arg = buffer; + + for (n = 0; n < pinfo->pr_argc; n++) { + int alen; + char *arg; + + if ((nread = pread(fd, buffer, sizeof(buffer), (off_t)argvp[n])) <= 0) { + close(fd); + return errno; + } + + alen = strlen(buffer)+1; + arg = malloc(alen); + memcpy(arg, buffer, alen); + + procargs->data[procargs->number++] = arg; + } + + if (argvp != argvb) { + free(argvp); + } + + close(fd); + + return SIGAR_OK; +} + +int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + psinfo_t *pinfo; + int fd, status; + char buffer[BUFSIZ], *offsets[512]; + size_t nread; + int n=0, max=sizeof(offsets)/sizeof(char *); + + if ((status = sigar_proc_psinfo_get(sigar, pid)) != SIGAR_OK) { + return status; + } + pinfo = sigar->pinfo; + + (void)SIGAR_PROC_FILENAME(buffer, pid, "/as"); + + if ((fd = open(buffer, O_RDONLY)) < 0) { + return ESRCH; + } + + if ((nread = pread(fd, offsets, sizeof(offsets), + pinfo->pr_envp)) <= 0) + { + close(fd); + return errno; + } + + while ((n < max) && offsets[n]) { + char *val; + int klen, vlen, status; + char key[128]; /* XXX is there a max key size? */ + + if ((nread = pread(fd, buffer, sizeof(buffer), + (off_t)offsets[n++])) <= 0) + { + close(fd); + return errno; + } + + val = strchr(buffer, '='); + + if (val == NULL) { + break; /*XXX*/ + } + + klen = val - buffer; + SIGAR_SSTRCPY(key, buffer); + key[klen] = '\0'; + ++val; + + vlen = strlen(val); + + status = procenv->env_getter(procenv->data, + key, klen, val, vlen); + + if (status != SIGAR_OK) { + /* not an error; just stop iterating */ + break; + } + } + + close(fd); + + return SIGAR_OK; +} + +int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + int status = + sigar_proc_fd_count(sigar, pid, &procfd->total); + + return status; +} + +int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + return SIGAR_ENOTIMPL; +} + +#include + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + char *type = fsp->sys_type_name; + + switch (*type) { + case 'u': + if (strEQ(type, "ufs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + /* XXX */ + } + + return fsp->type; +} + +int sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + struct mnttab ent; + sigar_file_system_t *fsp; + FILE *fp = fopen(MNTTAB, "r"); + + if (!fp) { + return errno; + } + + sigar_file_system_list_create(fslist); + + while (getmntent(fp, &ent) == 0) { + SIGAR_FILE_SYSTEM_LIST_GROW(fslist); + + fsp = &fslist->data[fslist->number++]; + + SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_mountp); + SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_special); + SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_fstype); + sigar_fs_type_init(fsp); + } + + fclose(fp); + + return SIGAR_OK; +} + +#include + +#define SIGAR_FS_BLOCKS_TO_BYTES(buf, f) \ + ((buf.f * (buf.f_frsize / 512)) >> 1) + +int sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + struct statvfs buf; + + if (statvfs(dirname, &buf) != 0) { + return errno; + } + + fsusage->total = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_blocks); + fsusage->free = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bfree); + fsusage->avail = SIGAR_FS_BLOCKS_TO_BYTES(buf, f_bavail); + fsusage->files = buf.f_files; + fsusage->free_files = buf.f_files; + fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); + + return SIGAR_OK; +} + +int sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + processor_info_t stats; + unsigned int i; + int status; + + sigar_kstat_update(sigar); /* for sigar->ncpu */ + + /* + * stats we care about will be the same for each + * online processor, so just grab the first. + */ + for (i=0; incpu; i++) { + processorid_t id = sigar->ks.cpuid[i]; + + if ((status = processor_info(id, &stats)) < 0) { + continue; + } + else { + status = SIGAR_OK; + break; + } + } + + if (status != SIGAR_OK) { + /* should never happen */ + return ENOENT; + } + + sigar_cpu_infos_create(cpu_infos); + + for (i=0; incpu; i++) { + sigar_cpu_info_t *info; + + SIGAR_CPU_INFOS_GROW(cpu_infos); + + info = &cpu_infos->data[cpu_infos->number++]; + + SIGAR_SSTRCPY(info->vendor, "Sun Microsystems"); /*XXX?*/ + SIGAR_SSTRCPY(info->model, stats.pi_processor_type); + info->mhz = stats.pi_clock; + info->cache_size = -1; /*XXX*/ + } + + return SIGAR_OK; +} + +int sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) + +{ + return SIGAR_ENOTIMPL; +} + +#define kHME(v) kSTAT_uint(v, hme) + +static int sigar_net_ifstat_get_hme(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + int status; + + status = sigar_get_multi_kstats(sigar, &sigar->ks.hme, + name, &ksp); + + if (status != SIGAR_OK) { + return status; + } + + kstat_read(kc, ksp, NULL); + + sigar_koffsets_init_hme(sigar, ksp); + + ifstat->rx_packets = kHME(KSTAT_HME_RX_PACKETS); + ifstat->rx_bytes = kHME(KSTAT_HME_RX_BYTES); + ifstat->rx_errors = kHME(KSTAT_HME_RX_ERRORS); + ifstat->rx_dropped = kHME(KSTAT_HME_RX_DROPPED); /*XXX*/ + ifstat->rx_overruns = kHME(KSTAT_HME_RX_OVERRUNS); /*XXX*/ + ifstat->rx_frame = kHME(KSTAT_HME_RX_FRAME); + + ifstat->tx_packets = kHME(KSTAT_HME_TX_PACKETS); + ifstat->tx_bytes = kHME(KSTAT_HME_TX_BYTES); + ifstat->tx_errors = kHME(KSTAT_HME_TX_ERRORS); + ifstat->tx_dropped = kHME(KSTAT_HME_TX_DROPPED); /*XXX*/ + ifstat->tx_overruns = kHME(KSTAT_HME_TX_OVERRUNS); /*XXX*/ + ifstat->tx_collisions = kHME(KSTAT_HME_TX_COLLISIONS); + ifstat->tx_carrier = kHME(KSTAT_HME_TX_CARRIER); + + return SIGAR_OK; +} + +#define kDMFE(v) kSTAT_uint(v, dmfe) + +static int sigar_net_ifstat_get_dmfe(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + int status; + + status = sigar_get_multi_kstats(sigar, &sigar->ks.dmfe, + name, &ksp); + + if (status != SIGAR_OK) { + return status; + } + + kstat_read(kc, ksp, NULL); + + sigar_koffsets_init_dmfe(sigar, ksp); + + ifstat->rx_packets = kDMFE(KSTAT_DMFE_RX_PACKETS); + ifstat->rx_bytes = kDMFE(KSTAT_DMFE_RX_BYTES); + ifstat->rx_errors = kDMFE(KSTAT_DMFE_RX_ERRORS); + ifstat->rx_dropped = kDMFE(KSTAT_DMFE_RX_DROPPED); /*XXX*/ + ifstat->rx_overruns = kDMFE(KSTAT_DMFE_RX_OVERRUNS); /*XXX*/ + ifstat->rx_frame = kDMFE(KSTAT_DMFE_RX_FRAME); + + ifstat->tx_packets = kDMFE(KSTAT_DMFE_TX_PACKETS); + ifstat->tx_bytes = kDMFE(KSTAT_DMFE_TX_BYTES); + ifstat->tx_errors = kDMFE(KSTAT_DMFE_TX_ERRORS); + ifstat->tx_dropped = kDMFE(KSTAT_DMFE_TX_DROPPED); /*XXX*/ + ifstat->tx_overruns = kDMFE(KSTAT_DMFE_TX_OVERRUNS); /*XXX*/ + ifstat->tx_collisions = kDMFE(KSTAT_DMFE_TX_COLLISIONS); + ifstat->tx_carrier = kDMFE(KSTAT_DMFE_TX_CARRIER); + + return SIGAR_OK; +} + +#define kGE(v) kSTAT_uint(v, ge) + +static int sigar_net_ifstat_get_ge(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + int status; + + status = sigar_get_multi_kstats(sigar, &sigar->ks.ge, + name, &ksp); + + if (status != SIGAR_OK) { + return status; + } + + kstat_read(kc, ksp, NULL); + + sigar_koffsets_init_ge(sigar, ksp); + + ifstat->rx_packets = kGE(KSTAT_GE_RX_PACKETS); + ifstat->rx_bytes = kGE(KSTAT_GE_RX_BYTES); + ifstat->rx_errors = kGE(KSTAT_GE_RX_ERRORS); + ifstat->rx_dropped = kGE(KSTAT_GE_RX_DROPPED); /*XXX*/ + ifstat->rx_overruns = kGE(KSTAT_GE_RX_OVERRUNS); /*XXX*/ + ifstat->rx_frame = kGE(KSTAT_GE_RX_FRAME); + + ifstat->tx_packets = kGE(KSTAT_GE_TX_PACKETS); + ifstat->tx_bytes = kGE(KSTAT_GE_TX_BYTES); + ifstat->tx_errors = kGE(KSTAT_GE_TX_ERRORS); + ifstat->tx_dropped = kGE(KSTAT_GE_TX_DROPPED); /*XXX*/ + ifstat->tx_overruns = kGE(KSTAT_GE_TX_OVERRUNS); /*XXX*/ + ifstat->tx_collisions = kGE(KSTAT_GE_TX_COLLISIONS); + ifstat->tx_carrier = kGE(KSTAT_GE_TX_CARRIER); + + return SIGAR_OK; +} + +#define kERI(v) kSTAT_uint(v, eri) + +static int sigar_net_ifstat_get_eri(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + int status; + + status = sigar_get_multi_kstats(sigar, &sigar->ks.eri, + name, &ksp); + + if (status != SIGAR_OK) { + return status; + } + + kstat_read(kc, ksp, NULL); + + sigar_koffsets_init_eri(sigar, ksp); + + ifstat->rx_packets = kERI(KSTAT_ERI_RX_PACKETS); + ifstat->rx_bytes = kERI(KSTAT_ERI_RX_BYTES); + ifstat->rx_errors = kERI(KSTAT_ERI_RX_ERRORS); + ifstat->rx_dropped = kERI(KSTAT_ERI_RX_DROPPED); /*XXX*/ + ifstat->rx_overruns = kERI(KSTAT_ERI_RX_OVERRUNS); /*XXX*/ + ifstat->rx_frame = kERI(KSTAT_ERI_RX_FRAME); + + ifstat->tx_packets = kERI(KSTAT_ERI_TX_PACKETS); + ifstat->tx_bytes = kERI(KSTAT_ERI_TX_BYTES); + ifstat->tx_errors = kERI(KSTAT_ERI_TX_ERRORS); + ifstat->tx_dropped = kERI(KSTAT_ERI_TX_DROPPED); /*XXX*/ + ifstat->tx_overruns = kERI(KSTAT_ERI_TX_OVERRUNS); /*XXX*/ + ifstat->tx_collisions = kERI(KSTAT_ERI_TX_COLLISIONS); + ifstat->tx_carrier = kERI(KSTAT_ERI_TX_CARRIER); + + return SIGAR_OK; +} + +#define kLO(v) kSTAT_uint(v, lo) + +#define jLO aHO + +static int sigar_net_ifstat_get_lo(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + int status; + + status = sigar_get_multi_kstats(sigar, &sigar->ks.lo, + name, &ksp); + + if (status != SIGAR_OK) { + return status; + } + + kstat_read(kc, ksp, NULL); + + sigar_koffsets_init_lo(sigar, ksp); + + ifstat->rx_packets = kLO(KSTAT_LO_RX_PACKETS); + ifstat->rx_bytes = -1; + ifstat->rx_errors = -1; + ifstat->rx_dropped = -1; + ifstat->rx_overruns = -1; + ifstat->rx_frame = -1; + + ifstat->tx_packets = kLO(KSTAT_LO_TX_PACKETS); + ifstat->tx_bytes = -1; + ifstat->tx_errors = -1; + ifstat->tx_dropped = -1; + ifstat->tx_overruns = -1; + ifstat->tx_collisions = -1; + ifstat->tx_carrier = -1; + + return SIGAR_OK; +} + +static int sigar_net_ifstat_get_any(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + kstat_ctl_t *kc = sigar->kc; + kstat_t *ksp; + kstat_named_t *data; + char dev[64], *ptr=dev; + int num, i; + + kstat_chain_update(kc); + strncpy(dev, name, sizeof(dev)-1); + dev[sizeof(dev)-1] = '\0'; + + while (!sigar_isdigit(*ptr) && (*ptr != '\0')) { + ptr++; + } + + if (*ptr == '\0') { + return ENXIO; + } + + /* iprb0 -> dev="iprb", num=0 */ + num = atoi(ptr); + *ptr = '\0'; + + if (!(ksp = kstat_lookup(kc, dev, num, NULL))) { + return ENXIO; + } + + if (kstat_read(kc, ksp, NULL) < 0) { + return ENOENT; + } + + SIGAR_ZERO(ifstat); + + data = (kstat_named_t *)ksp->ks_data; + for (i=0; iks_ndata; i++) { + sigar_uint64_t value = data[i].value.ui32; + + ptr = data[i].name; + + switch (*ptr) { + case 'c': + if (strEQ(ptr, "collisions")) { + ifstat->tx_collisions = value; + } + break; + case 'd': + if (strEQ(ptr, "drop")) { + ifstat->rx_dropped = value; + ifstat->tx_dropped = value; + } + break; + case 'i': + if (strEQ(ptr, "ipackets")) { + ifstat->rx_packets = value; + } + else if (strEQ(ptr, "ierrors")) { + ifstat->rx_errors = value; + } + break; + case 'f': + if (strEQ(ptr, "framing")) { + ifstat->rx_frame = value; + } + break; + case 'm': + if (strEQ(ptr, "missed")) { + ifstat->rx_dropped = value; + ifstat->tx_dropped = value; + } + break; + case 'n': + if (strEQ(ptr, "nocarrier")) { + ifstat->tx_carrier = value; + } + break; + case 'o': + if (strEQ(ptr, "obytes")) { + ifstat->tx_bytes = value; + } + else if (strEQ(ptr, "oerrors")) { + ifstat->tx_errors = value; + } + else if (strEQ(ptr, "oflo")) { + ifstat->tx_overruns = value; + } + else if (strEQ(ptr, "opackets")) { + ifstat->tx_packets = value; + } + else if (strEQ(ptr, "toolong_errors")) { + ifstat->tx_overruns = value; + } + break; + case 'r': + if (strEQ(ptr, "rbytes")) { + ifstat->rx_bytes = value; + } + else if (strEQ(ptr, "rx_overflow")) { + ifstat->rx_overruns = value; + } + break; + default: + break; + } + } + + return SIGAR_OK; +} + +int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + switch (*name) { + case 'd': + if (strnEQ(name, "dmfe", 4)) { + return sigar_net_ifstat_get_dmfe(sigar, name, ifstat); + } + break; + case 'e': + if (strnEQ(name, "eri", 3)) { + return sigar_net_ifstat_get_eri(sigar, name, ifstat); + } + break; + case 'g': + if (strnEQ(name, "ge", 2)) { + return sigar_net_ifstat_get_ge(sigar, name, ifstat); + } + break; + case 'h': + if (strnEQ(name, "hme", 3)) { + return sigar_net_ifstat_get_hme(sigar, name, ifstat); + } + break; + case 'l': + if (strnEQ(name, "lo", 2)) { + return sigar_net_ifstat_get_lo(sigar, name, ifstat); + } + break; + default: + return sigar_net_ifstat_get_any(sigar, name, ifstat); + } + + return ENXIO; +} + +int sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + return SIGAR_ENOTIMPL; +} diff --git a/src/os/stub/sigar_os.h b/src/os/stub/sigar_os.h new file mode 100644 index 00000000..068fc3c6 --- /dev/null +++ b/src/os/stub/sigar_os.h @@ -0,0 +1,8 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +struct sigar_t { + SIGAR_T_BASE; +}; + +#endif /* SIGAR_OS_H */ diff --git a/src/os/stub/stub_sigar.c b/src/os/stub/stub_sigar.c new file mode 100644 index 00000000..f5ec0c83 --- /dev/null +++ b/src/os/stub/stub_sigar.c @@ -0,0 +1,248 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" + +int sigar_os_open(sigar_t **sigar) +{ + *sigar = malloc(sizeof(**sigar)); + + return SIGAR_OK; +} + +int sigar_os_close(sigar_t *sigar) +{ + free(sigar); + return SIGAR_OK; +} + +char *sigar_os_error_string(int err) +{ + return NULL; +} + +int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ + mem->total = -1; + mem->ram = -1; + mem->used = -1; + mem->free = -1; + mem->shared = -1; + mem->buffer = -1; + mem->cached = -1; + + return SIGAR_OK; +} + +int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ + swap->total = -1; + swap->used = -1; + swap->free = -1; + + return SIGAR_OK; +} + +int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ + cpu->user = -1; + cpu->nice = -1; + cpu->sys = -1; + cpu->idle = -1; + + cpu->total = cpu->user + cpu->nice + cpu->sys + cpu->idle; + + return SIGAR_OK; +} + +int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + uptime->uptime = -1; + uptime->idletime = -1; + + return SIGAR_OK; +} + +int sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + loadavg->loadavg[0] = -1; + loadavg->loadavg[1] = -1; + loadavg->loadavg[2] = -1; + + return SIGAR_OK; +} + +int sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + procmem->size = -1; + procmem->vsize = -1; + procmem->share = -1; + procmem->rss = -1; + procmem->resident = -1; + + return SIGAR_OK; +} + +int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + proccred->uid = -1; + proccred->gid = -1; + proccred->euid = -1; + proccred->egid = -1; + + return SIGAR_OK; +} + +int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + proctime->start_time = -1; + proctime->utime = -1; + proctime->stime = -1; + + return SIGAR_OK; +} + +int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + SIGAR_SSTRCPY(procstate->name, "java"); + procstate->ppid = -1; + procstate->priority = -1; + procstate->nice = -1; + procstate->tty = -1; + procstate->state = 'R'; + + return SIGAR_OK; +} + +int sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + procfd->total = -1; + return SIGAR_OK; +} + +int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + return SIGAR_ENOTIMPL; +} + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + fsp->type = SIGAR_FSTYPE_UNKNOWN; + + return SIGAR_OK; +} + +int sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + sigar_file_system_list_create(fslist); + + return SIGAR_OK; +} + +int sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + fsusage->total = -1; + fsusage->free = -1; + fsusage->avail = -1; + fsusage->files = -1; + fsusage->free_files = -1; + + return SIGAR_OK; +} + +int sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + sigar_cpu_info_t *info; + + sigar_cpu_infos_create(cpu_infos); + + info = &cpu_infos->data[cpu_infos->number++]; + + SIGAR_SSTRCPY(info->vendor, "vendor"); + SIGAR_SSTRCPY(info->model, "model"); + info->mhz = -1; + info->cache_size = -1; + + return SIGAR_OK; +} + +int sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + sigar_net_route_t *route; + + sigar_net_route_list_create(routelist); + + return SIGAR_OK; +} + +int sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + ifstat->rx_bytes = -1; + ifstat->rx_packets = -1; + ifstat->rx_errors = -1; + ifstat->rx_dropped = -1; + ifstat->rx_overruns = -1; + ifstat->rx_frame = -1; + + ifstat->tx_bytes = -1; + ifstat->tx_packets = -1; + ifstat->tx_errors = -1; + ifstat->tx_dropped = -1; + ifstat->tx_overruns = -1; + ifstat->tx_collisions = -1; + ifstat->tx_carrier = -1; + + return SIGAR_OK; +} + +int sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + return SIGAR_ENOTIMPL; +} diff --git a/src/os/win32/build-cpu.bat b/src/os/win32/build-cpu.bat new file mode 100755 index 00000000..9f813e9e --- /dev/null +++ b/src/os/win32/build-cpu.bat @@ -0,0 +1,2 @@ +@echo off +cl -I..\..\..\include -I. -DWIN32 -DCPU_MAIN cpu.c /link ADVAPI32.LIB diff --git a/src/os/win32/counter_names.txt b/src/os/win32/counter_names.txt new file mode 100644 index 00000000..b125b043 --- /dev/null +++ b/src/os/win32/counter_names.txt @@ -0,0 +1,820 @@ +#counter names from registry key +#SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib +0 UNDEFINED +1 1847 +2 System +4 Memory +6 % Processor Time +10 File Read Operations/sec +12 File Write Operations/sec +14 File Control Operations/sec +16 File Read Bytes/sec +18 File Write Bytes/sec +20 File Control Bytes/sec +24 Available Bytes +26 Committed Bytes +28 Page Faults/sec +30 Commit Limit +32 Write Copies/sec +34 Transition Faults/sec +36 Cache Faults/sec +38 Demand Zero Faults/sec +40 Pages/sec +42 Page Reads/sec +44 Processor Queue Length +46 Thread State +48 Pages Output/sec +50 Page Writes/sec +52 Browser +54 Announcements Server/sec +56 Pool Paged Bytes +58 Pool Nonpaged Bytes +60 Pool Paged Allocs +64 Pool Nonpaged Allocs +66 Pool Paged Resident Bytes +68 System Code Total Bytes +70 System Code Resident Bytes +72 System Driver Total Bytes +74 System Driver Resident Bytes +76 System Cache Resident Bytes +78 Announcements Domain/sec +80 Election Packets/sec +82 Mailslot Writes/sec +84 Server List Requests/sec +86 Cache +88 Data Maps/sec +90 Sync Data Maps/sec +92 Async Data Maps/sec +94 Data Map Hits % +96 Data Map Pins/sec +98 Pin Reads/sec +100 Sync Pin Reads/sec +102 Async Pin Reads/sec +104 Pin Read Hits % +106 Copy Reads/sec +108 Sync Copy Reads/sec +110 Async Copy Reads/sec +112 Copy Read Hits % +114 MDL Reads/sec +116 Sync MDL Reads/sec +118 Async MDL Reads/sec +120 MDL Read Hits % +122 Read Aheads/sec +124 Fast Reads/sec +126 Sync Fast Reads/sec +128 Async Fast Reads/sec +130 Fast Read Resource Misses/sec +132 Fast Read Not Possibles/sec +134 Lazy Write Flushes/sec +136 Lazy Write Pages/sec +138 Data Flushes/sec +140 Data Flush Pages/sec +142 % User Time +144 % Privileged Time +146 Context Switches/sec +148 Interrupts/sec +150 System Calls/sec +152 Level 1 TLB Fills/sec +154 Level 2 TLB Fills/sec +156 Enumerations Server/sec +158 Enumerations Domain/sec +160 Enumerations Other/sec +162 Missed Server Announcements +164 Missed Mailslot Datagrams +166 Missed Server List Requests +168 Server Announce Allocations Failed/sec +170 Mailslot Allocations Failed +172 Virtual Bytes Peak +174 Virtual Bytes +178 Working Set Peak +180 Working Set +182 Page File Bytes Peak +184 Page File Bytes +186 Private Bytes +188 Announcements Total/sec +190 Enumerations Total/sec +198 Current Disk Queue Length +200 % Disk Time +202 % Disk Read Time +204 % Disk Write Time +206 Avg. Disk sec/Transfer +208 Avg. Disk sec/Read +210 Avg. Disk sec/Write +212 Disk Transfers/sec +214 Disk Reads/sec +216 Disk Writes/sec +218 Disk Bytes/sec +220 Disk Read Bytes/sec +222 Disk Write Bytes/sec +224 Avg. Disk Bytes/Transfer +226 Avg. Disk Bytes/Read +228 Avg. Disk Bytes/Write +230 Process +232 Thread +234 PhysicalDisk +236 LogicalDisk +238 Processor +240 % Total Processor Time +242 % Total User Time +244 % Total Privileged Time +246 Total Interrupts/sec +248 Processes +250 Threads +252 Events +254 Semaphores +256 Mutexes +258 Sections +260 Objects +262 Redirector +264 Bytes Received/sec +266 Packets Received/sec +268 Read Bytes Paging/sec +270 Read Bytes Non-Paging/sec +272 Read Bytes Cache/sec +274 Read Bytes Network/sec +276 Bytes Transmitted/sec +278 Packets Transmitted/sec +280 Write Bytes Paging/sec +282 Write Bytes Non-Paging/sec +284 Write Bytes Cache/sec +286 Write Bytes Network/sec +288 Read Operations/sec +290 Read Operations Random/sec +292 Read Packets/sec +294 Reads Large/sec +296 Read Packets Small/sec +298 Write Operations/sec +300 Write Operations Random/sec +302 Write Packets/sec +304 Writes Large/sec +306 Write Packets Small/sec +308 Reads Denied/sec +310 Writes Denied/sec +312 Network Errors/sec +314 Server Sessions +316 Server Reconnects +318 Connects Core +320 Connects Lan Manager 2.0 +322 Connects Lan Manager 2.1 +324 Connects Windows NT +326 Server Disconnects +328 Server Sessions Hung +330 Server +336 Thread Wait Reason +340 Sessions Timed Out +342 Sessions Errored Out +344 Sessions Logged Off +346 Sessions Forced Off +348 Errors Logon +350 Errors Access Permissions +352 Errors Granted Access +354 Errors System +356 Blocking Requests Rejected +358 Work Item Shortages +360 Files Opened Total +362 Files Open +366 File Directory Searches +370 Pool Nonpaged Failures +372 Pool Nonpaged Peak +376 Pool Paged Failures +378 Pool Paged Peak +388 Bytes Total/sec +392 Current Commands +398 NWLink NetBIOS +400 Packets/sec +404 Context Blocks Queued/sec +406 File Data Operations/sec +408 % Free Space +410 Free Megabytes +412 Connections Open +414 Connections No Retries +416 Connections With Retries +418 Disconnects Local +420 Disconnects Remote +422 Failures Link +424 Failures Adapter +426 Connection Session Timeouts +428 Connections Canceled +430 Failures Resource Remote +432 Failures Resource Local +434 Failures Not Found +436 Failures No Listen +438 Datagrams/sec +440 Datagram Bytes/sec +442 Datagrams Sent/sec +444 Datagram Bytes Sent/sec +446 Datagrams Received/sec +448 Datagram Bytes Received/sec +452 Packets Sent/sec +456 Frames/sec +458 Frame Bytes/sec +460 Frames Sent/sec +462 Frame Bytes Sent/sec +464 Frames Received/sec +466 Frame Bytes Received/sec +468 Frames Re-Sent/sec +470 Frame Bytes Re-Sent/sec +472 Frames Rejected/sec +474 Frame Bytes Rejected/sec +476 Expirations Response +478 Expirations Ack +480 Window Send Maximum +482 Window Send Average +484 Piggyback Ack Queued/sec +486 Piggyback Ack Timeouts +488 NWLink IPX +490 NWLink SPX +492 NetBEUI +494 NetBEUI Resource +496 Used Maximum +498 Used Average +500 Times Exhausted +502 NBT Connection +506 Bytes Sent/sec +508 Total Bytes/sec +510 Network Interface +512 Bytes/sec +520 Current Bandwidth +524 Packets Received Unicast/sec +526 Packets Received Non-Unicast/sec +528 Packets Received Discarded +530 Packets Received Errors +532 Packets Received Unknown +536 Packets Sent Unicast/sec +538 Packets Sent Non-Unicast/sec +540 Packets Outbound Discarded +542 Packets Outbound Errors +544 Output Queue Length +546 IP +552 Datagrams Received Header Errors +554 Datagrams Received Address Errors +556 Datagrams Forwarded/sec +558 Datagrams Received Unknown Protocol +560 Datagrams Received Discarded +562 Datagrams Received Delivered/sec +566 Datagrams Outbound Discarded +568 Datagrams Outbound No Route +570 Fragments Received/sec +572 Fragments Re-assembled/sec +574 Fragment Re-assembly Failures +576 Fragmented Datagrams/sec +578 Fragmentation Failures +580 Fragments Created/sec +582 ICMP +584 Messages/sec +586 Messages Received/sec +588 Messages Received Errors +590 Received Dest. Unreachable +592 Received Time Exceeded +594 Received Parameter Problem +596 Received Source Quench +598 Received Redirect/sec +600 Received Echo/sec +602 Received Echo Reply/sec +604 Received Timestamp/sec +606 Received Timestamp Reply/sec +608 Received Address Mask +610 Received Address Mask Reply +612 Messages Sent/sec +614 Messages Outbound Errors +616 Sent Destination Unreachable +618 Sent Time Exceeded +620 Sent Parameter Problem +622 Sent Source Quench +624 Sent Redirect/sec +626 Sent Echo/sec +628 Sent Echo Reply/sec +630 Sent Timestamp/sec +632 Sent Timestamp Reply/sec +634 Sent Address Mask +636 Sent Address Mask Reply +638 TCP +640 Segments/sec +642 Connections Established +644 Connections Active +646 Connections Passive +648 Connection Failures +650 Connections Reset +652 Segments Received/sec +654 Segments Sent/sec +656 Segments Retransmitted/sec +658 UDP +660 % Total DPC Time +662 % Total Interrupt Time +664 Datagrams No Port/sec +666 Datagrams Received Errors +670 Disk Storage Unit +672 Allocation Failures +674 System Up Time +676 System Handle Count +678 Free System Page Table Entries +680 Thread Count +682 Priority Base +684 Elapsed Time +686 Alignment Fixups/sec +688 Exception Dispatches/sec +690 Floating Emulations/sec +692 Logon/sec +694 Priority Current +696 % DPC Time +698 % Interrupt Time +700 Paging File +702 % Usage +704 % Usage Peak +706 Start Address +708 User PC +710 Mapped Space No Access +712 Mapped Space Read Only +714 Mapped Space Read/Write +716 Mapped Space Write Copy +718 Mapped Space Executable +720 Mapped Space Exec Read Only +722 Mapped Space Exec Read/Write +724 Mapped Space Exec Write Copy +726 Reserved Space No Access +728 Reserved Space Read Only +730 Reserved Space Read/Write +732 Reserved Space Write Copy +734 Reserved Space Executable +736 Reserved Space Exec Read Only +738 Reserved Space Exec Read/Write +740 Image +742 Reserved Space Exec Write Copy +744 Unassigned Space No Access +746 Unassigned Space Read Only +748 Unassigned Space Read/Write +750 Unassigned Space Write Copy +752 Unassigned Space Executable +754 Unassigned Space Exec Read Only +756 Unassigned Space Exec Read/Write +758 Unassigned Space Exec Write Copy +760 Image Space No Access +762 Image Space Read Only +764 Image Space Read/Write +766 Image Space Write Copy +768 Image Space Executable +770 Image Space Exec Read Only +772 Image Space Exec Read/Write +774 Image Space Exec Write Copy +776 Bytes Image Reserved +778 Bytes Image Free +780 Bytes Reserved +782 Bytes Free +784 ID Process +786 Process Address Space +788 No Access +790 Read Only +792 Read/Write +794 Write Copy +796 Executable +798 Exec Read Only +800 Exec Read/Write +802 Exec Write Copy +804 ID Thread +806 Mailslot Receives Failed +808 Mailslot Writes Failed +810 Mailslot Opens Failed/sec +812 Duplicate Master Announcements +814 Illegal Datagrams/sec +816 Thread Details +818 Cache Bytes +820 Cache Bytes Peak +822 Pages Input/sec +870 RAS Port +872 Bytes Transmitted +874 Bytes Received +876 Frames Transmitted +878 Frames Received. +880 Percent Compression Out +882 Percent Compression In +884 CRC Errors +886 Timeout Errors +888 Serial Overrun Errors +890 Alignment Errors +892 Buffer Overrun Errors +894 Total Errors +896 Bytes Transmitted/Sec +898 Bytes Received/Sec +900 Frames Transmitted/Sec +902 Frames Received/Sec +904 Total Errors/Sec +906 RAS Total +908 Total Connections +920 WINS Server +922 Unique Registrations/sec +924 Group Registrations/sec +926 Total Number of Registrations/sec +928 Unique Renewals/sec +930 Group Renewals/sec +932 Total Number of Renewals/sec +934 Releases/sec +936 Queries/sec +938 Unique Conflicts/sec +940 Group Conflicts/sec +942 Total Number of Conflicts/sec +944 Successful Releases/sec +946 Failed Releases/sec +948 Successful Queries/sec +950 Failed Queries/sec +952 Handle Count +1000 MacFile Server +1002 Max Paged Memory +1004 Current Paged Memory +1006 Max NonPaged Memory +1008 Current NonPaged memory +1010 Current Sessions +1012 Maximum Sessions +1014 Current Files Open +1016 Maximum Files Open +1018 Failed Logons +1020 Data Read/sec +1022 Data Written/sec +1024 Data Received/sec +1026 Data Transmitted/sec +1028 Current Queue Length +1030 Maximum Queue Length +1032 Current Threads +1034 Maximum Threads +1050 AppleTalk +1052 Packets In/sec +1054 Packets Out/sec +1056 Bytes In/sec +1058 Bytes Out/sec +1060 Average Time/DDP Packet +1062 DDP Packets/sec +1064 Average Time/AARP Packet +1066 AARP Packets/sec +1068 Average Time/ATP Packet +1070 ATP Packets/sec +1072 Average Time/NBP Packet +1074 NBP Packets/sec +1076 Average Time/ZIP Packet +1078 ZIP Packets/sec +1080 Average Time/RTMP Packet +1082 RTMP Packets/sec +1084 ATP Retries Local +1086 ATP Response Timouts +1088 ATP XO Response/Sec +1090 ATP ALO Response/Sec +1092 ATP Recvd Release/Sec +1094 Current NonPaged Pool +1096 Packets Routed In/Sec +1098 Packets dropped +1100 ATP Retries Remote +1102 Packets Routed Out/Sec +1110 Network Segment +1112 Total frames received/second +1114 Total bytes received/second +1116 Broadcast frames received/second +1118 Multicast frames received/second +1120 % Network utilization +1124 % Broadcast Frames +1126 % Multicast Frames +1150 Telephony +1152 Lines +1154 Telephone Devices +1156 Active Lines +1158 Active Telephones +1160 Outgoing Calls/sec +1162 Incoming Calls/sec +1164 Client Apps +1166 Current Outgoing Calls +1168 Current Incoming Calls +1228 Gateway Service For NetWare +1230 Client Service For NetWare +1232 Packet Burst Read NCP Count/sec +1234 Packet Burst Read Timeouts/sec +1236 Packet Burst Write NCP Count/sec +1238 Packet Burst Write Timeouts/sec +1240 Packet Burst IO/sec +1242 Connect NetWare 2.x +1244 Connect NetWare 3.x +1246 Connect NetWare 4.x +1260 Logon Total +1300 Server Work Queues +1302 Queue Length +1304 Active Threads +1306 Available Threads +1308 Available Work Items +1310 Borrowed Work Items +1312 Work Item Shortages +1314 Current Clients +1320 Bytes Transferred/sec +1324 Read Bytes/sec +1328 Write Bytes/sec +1332 Total Operations/sec +1334 DPCs Queued/sec +1336 DPC Rate +1342 Total DPCs Queued/sec +1344 Total DPC Rate +1350 % Registry Quota In Use +1360 VL Memory +1362 VLM % Virtual Size In Use +1364 VLM Virtual Size +1366 VLM Virtual Size Peak +1368 VLM Virtual Size Available +1370 VLM Commit Charge +1372 VLM Commit Charge Peak +1374 System VLM Commit Charge +1376 System VLM Commit Charge Peak +1378 System VLM Shared Commit Charge +1380 Available KBytes +1382 Available MBytes +1400 Avg. Disk Queue Length +1402 Avg. Disk Read Queue Length +1404 Avg. Disk Write Queue Length +1406 % Committed Bytes In Use +1408 Full Image +1410 Creating Process ID +1412 IO Read Operations/sec +1414 IO Write Operations/sec +1416 IO Data Operations/sec +1418 IO Other Operations/sec +1420 IO Read Bytes/sec +1422 IO Write Bytes/sec +1424 IO Data Bytes/sec +1426 IO Other Bytes/sec +1450 Print Queue +1452 Total Jobs Printed +1454 Bytes Printed/sec +1456 Total Pages Printed +1458 Jobs +1460 References +1462 Max References +1464 Jobs Spooling +1466 Max Jobs Spooling +1468 Out of Paper Errors +1470 Not Ready Errors +1472 Job Errors +1474 Enumerate Network Printer Calls +1476 Add Network Printer Calls +1478 Working Set - Private +1480 Working Set - Shared +1482 % Idle Time +1484 Split IO/Sec +1500 Job Object +1502 Current % Processor Time +1504 Current % User Mode Time +1506 Current % Kernel Mode Time +1508 This Period mSec - Processor +1510 This Period mSec - User Mode +1512 This Period mSec - Kernel Mode +1514 Pages/Sec +1516 Process Count - Total +1518 Process Count - Active +1520 Process Count - Terminated +1522 Total mSec - Processor +1524 Total mSec - User Mode +1526 Total mSec - Kernel Mode +1548 Job Object Details +1746 % Idle Time +1748 % C1 Time +1750 % C2 Time +1752 % C3 Time +1754 C1 Transitions/sec +1756 C2 Transitions/sec +1758 C3 Transitions/sec +1760 Heap +1762 Committed Bytes +1764 Reserved Bytes +1766 Virtual Bytes +1768 Free Bytes +1770 Free List Length +1772 Avg. alloc rate +1774 Avg. free rate +1776 Uncommitted Ranges Length +1778 Allocs - Frees +1780 Cached Allocs/sec +1782 Cached Frees/sec +1784 Allocs <1K/sec +1786 Frees <1K/sec +1788 Allocs 1-8K/sec +1790 Frees 1-8K/sec +1792 Allocs over 8K/sec +1794 Frees over 8K/sec +1796 Total Allocs/sec +1798 Total Frees/sec +1800 Blocks in Heap Cache +1802 Largest Cache Depth +1804 % Fragmentation +1806 % VAFragmentation +1808 Heap Lock contention +1846 End Marker +1848 RSVP Service +1850 Network Interfaces +1852 Network sockets +1854 Timers +1856 RSVP sessions +1858 QoS clients +1860 QoS-enabled senders +1862 QoS-enabled receivers +1864 Failed QoS requests +1866 Failed QoS sends +1868 QoS notifications +1870 Bytes in QoS notifications +1872 RSVP Interfaces +1874 Signaling bytes received +1876 Signaling bytes sent +1878 PATH messages received +1880 RESV messages received +1882 PATH ERR messages received +1884 RESV ERR messages received +1886 PATH TEAR messages received +1888 RESV TEAR messages received +1890 RESV CONFIRM messages received +1892 PATH messages sent +1894 RESV messages sent +1896 PATH ERR messages sent +1898 RESV ERR messages sent +1900 PATH TEAR messages sent +1902 RESV TEAR messages sent +1904 RESV CONFIRM messages sent +1906 Resource control failures +1908 Policy control failures +1910 General failures +1912 Blocked RESVs +1914 RESV state block timeouts +1916 PATH state block timeouts +1918 Send messages errors - Big messages +1920 Receive messages errors - Big messages +1922 Send messages errors - No memory +1924 Receive messages errors - No memory +1926 Number of incoming messages dropped +1928 Number of outgoing messages dropped +1930 Number of active flows +1932 Reserved bandwidth +1934 Maximum admitted bandwidth +1936 PSched Flow +1938 PSched Pipe +1940 Packets dropped +1942 Packets scheduled +1944 Packets transmitted +1946 Average packets in shaper +1948 Max packets in shaper +1950 Average packets in sequencer +1952 Max packets in sequencer +1954 Bytes scheduled +1956 Bytes transmitted +1958 Bytes transmitted/sec +1960 Bytes scheduled/sec +1962 Packets transmitted/sec +1964 Packets scheduled/sec +1966 Packets dropped/sec +1968 Nonconforming packets scheduled +1970 Nonconforming packets scheduled/sec +1972 Nonconforming packets transmitted +1974 Nonconforming packets transmitted/sec +1976 Maximum Packets in netcard +1978 Average Packets in netcard +1980 Out of packets +1982 Flows opened +1984 Flows closed +1986 Flows rejected +1988 Flows modified +1990 Flow mods rejected +1992 Max simultaneous flows +1994 Nonconforming packets scheduled +1996 Nonconforming packets scheduled/sec +1998 Nonconforming packets transmitted +2000 Nonconforming packets transmitted/sec +2002 Average packets in shaper +2004 Max packets in shaper +2006 Average packets in sequencer +2008 Max packets in sequencer +2010 Max packets in netcard +2012 Average packets in netcard +2014 RAS Port +2016 Bytes Transmitted +2018 Bytes Received +2020 Frames Transmitted +2022 Frames Received +2024 Percent Compression Out +2026 Percent Compression In +2028 CRC Errors +2030 Timeout Errors +2032 Serial Overrun Errors +2034 Alignment Errors +2036 Buffer Overrun Errors +2038 Total Errors +2040 Bytes Transmitted/Sec +2042 Bytes Received/Sec +2044 Frames Transmitted/Sec +2046 Frames Received/Sec +2048 Total Errors/Sec +2050 RAS Total +2052 Total Connections +2054 Terminal Services Session +2056 Input WdBytes +2058 Input WdFrames +2060 Input WaitForOutBuf +2062 Input Frames +2064 Input Bytes +2066 Input Compressed Bytes +2068 Input Compress Flushes +2070 Input Errors +2072 Input Timeouts +2074 Input Async Frame Error +2076 Input Async Overrun +2078 Input Async Overflow +2080 Input Async Parity Error +2082 Input Transport Errors +2084 Output WdBytes +2086 Output WdFrames +2088 Output WaitForOutBuf +2090 Output Frames +2092 Output Bytes +2094 Output Compressed Bytes +2096 Output Compress Flushes +2098 Output Errors +2100 Output Timeouts +2102 Output Async Frame Error +2104 Output Async Overrun +2106 Output Async Overflow +2108 Output Async Parity Error +2110 Output Transport Errors +2112 Total WdBytes +2114 Total WdFrames +2116 Total WaitForOutBuf +2118 Total Frames +2120 Total Bytes +2122 Total Compressed Bytes +2124 Total Compress Flushes +2126 Total Errors +2128 Total Timeouts +2130 Total Async Frame Error +2132 Total Async Overrun +2134 Total Async Overflow +2136 Total Async Parity Error +2138 Total Transport Errors +2140 Total Protocol Cache Reads +2142 Total Protocol Cache Hits +2144 Total Protocol Cache Hit Ratio +2146 Protocol Bitmap Cache Reads +2148 Protocol Bitmap Cache Hits +2150 Protocol Bitmap Cache Hit Ratio +2152 Protocol Glyph Cache Reads +2154 Protocol Glyph Cache Hits +2156 Protocol Glyph Cache Hit Ratio +2158 Protocol Brush Cache Reads +2160 Protocol Brush Cache Hits +2162 Protocol Brush Cache Hit Ratio +2164 Protocol Save Screen Bitmap Cache Reads +2166 Protocol Save Screen Bitmap Cache Hits +2168 Protocol Save Screen Bitmap Cache Hit Ratio +2170 Input Compression Ratio +2172 Output Compression Ratio +2174 Total Compression Ratio +2176 Terminal Services +2178 Total Sessions +2180 Active Sessions +2182 Inactive Sessions +2184 Distributed Transaction Coordinator +2186 Active Transactions +2188 Committed Transactions +2190 Aborted Transactions +2192 In Doubt Transactions +2194 Active Transactions Maximum +2196 Force Committed Transactions +2198 Force Aborted Transactions +2200 Response Time -- Minimum +2202 Response Time -- Average +2204 Response Time -- Maximum +2206 Transactions/sec +2208 Committed Transactions/sec +2210 Aborted Transactions/sec +2212 Indexing Service +2214 Word lists +2216 Saved indexes +2218 Index size (MB) +2220 Files to be indexed +2222 Unique keys +2224 Running queries +2226 Merge progress +2228 # documents indexed +2230 Total # documents +2232 Total # of queries +2234 Deferred for indexing +2236 Indexing Service Filter +2238 Total indexing speed (MB/hr) +2240 Binding time (msec) +2242 Indexing speed (MB/hr) +2244 Http Indexing Service +2246 Cache items +2248 % Cache hits +2250 Total cache accesses 1 +2252 % Cache misses +2254 Total cache accesses 2 +2256 Active queries +2258 Total queries +2260 Queries per minute +2262 Current requests queued +2264 Total requests rejected +2266 WMI Objects +2268 HiPerf Classes +2270 HiPerf Validity +2272 BatteryStatus +2274 ChargeRate +2276 DischargeRate +2278 RemainingCapacity +2280 Tag +2282 Voltage diff --git a/src/os/win32/cpu.c b/src/os/win32/cpu.c new file mode 100644 index 00000000..89e228f9 --- /dev/null +++ b/src/os/win32/cpu.c @@ -0,0 +1,354 @@ +/* + * code in this file derived from: + * http://www.intel.com/cd/ids/developer/asmo-na/eng/technologies/20438.htm + * license grants use of the source code. + */ +static unsigned int HTSupported(void); +static unsigned char LogicalProcPerPhysicalProc(unsigned int); +static unsigned char GetAPIC_ID(unsigned int); +static unsigned char CPUCount(unsigned char *, unsigned char *); + +// EDX[28] Bit 28 is set if HT is supported +#define HT_BIT 0x10000000 + +// EAX[11:8] Bit 8-11 contains family processor ID. +#define FAMILY_ID 0x0F00 + +#define PENTIUM4_ID 0x0F00 + +// EAX[23:20] Bit 20-23 contains extended family processor ID +#define EXT_FAMILY_ID 0x0F00000 + + +// EBX[23:16] Bit 16-23 in ebx contains the number of logical +// processors per physical processor when execute cpuid with +// eax set to 1 +#define NUM_LOGICAL_BITS 0x00FF0000 + +// EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique +// initial APIC ID for the processor this code is running on. +// Default value = 0xff if HT is not supported +#define INITIAL_APIC_ID_BITS 0xFF000000 + +// Status Flag +#define HT_NOT_CAPABLE 0 +#define HT_ENABLED 1 +#define HT_DISABLED 2 +#define HT_SUPPORTED_NOT_ENABLED 3 +#define HT_CANNOT_DETECT 4 + +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" + +static unsigned int HTSupported(void) +{ + unsigned int + Regedx = 0, + Regeax = 0, + VendorId[3] = {0, 0, 0}; + + __try { + __asm { + xor eax, eax // call cpuid with eax = 0 + cpuid // Get vendor id string + mov VendorId, ebx + mov VendorId + 4, edx + mov VendorId + 8, ecx + + mov eax, 1 // call cpuid with eax = 1 + cpuid + mov Regeax, eax // eax contains family processor type + mov Regedx, edx // edx has info about the availability of hyper-Threading + } + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return 0; // cpuid is unavailable + } + + if (((Regeax & FAMILY_ID) == PENTIUM4_ID) || + (Regeax & EXT_FAMILY_ID)) + { + if ((VendorId[0] == 'uneG') && + (VendorId[1] == 'Ieni') && + (VendorId[2] == 'letn')) + { + // Genuine Intel with hyper-Threading technology + return Regedx & HT_BIT; + } + } + + return 0; // Not genuine Intel processor +} + +static unsigned char LogicalProcPerPhysicalProc(unsigned int ht_supported) +{ + unsigned int Regebx = 0; + if (!ht_supported) { + return (unsigned char) 1; // HT not supported + } + + // Logical processor = 1 + __asm { + mov eax, 1 + cpuid + mov Regebx, ebx + } + + return (unsigned char) ((Regebx & NUM_LOGICAL_BITS) >> 16); +} + +static unsigned char GetAPIC_ID(unsigned int ht_supported) +{ + unsigned int Regebx = 0; + if (!ht_supported) { + return (unsigned char) -1; + } + + // Logical processor = 1 + __asm { + mov eax, 1 + cpuid + mov Regebx, ebx + } + + return (unsigned char) ((Regebx & INITIAL_APIC_ID_BITS) >> 24); +} + +static unsigned char CPUCount(unsigned char *LogicalNum, + unsigned char *PhysicalNum) +{ + unsigned int ht_supported = HTSupported(); + unsigned char StatusFlag = 0; + SYSTEM_INFO info; + + *PhysicalNum = 0; + *LogicalNum = 0; + info.dwNumberOfProcessors = 0; + GetSystemInfo(&info); + + // Number of physical processors in a non-Intel system + // or in a 32-bit Intel system with Hyper-Threading technology disabled + *PhysicalNum = (unsigned char) info.dwNumberOfProcessors; + + if (ht_supported) { + unsigned char HT_Enabled = 0; + + *LogicalNum = LogicalProcPerPhysicalProc(ht_supported); + + if (*LogicalNum >= 1) { // >1 Doesn't mean HT is enabled in the BIOS + HANDLE hCurrentProcessHandle; + DWORD dwProcessAffinity; + DWORD dwSystemAffinity; + DWORD dwAffinityMask; + + // Calculate the appropriate shifts and mask based on the + // number of logical processors. + + unsigned char i = 1, + PHY_ID_MASK = 0xFF, + PHY_ID_SHIFT = 0; + + while (i < *LogicalNum) { + i *= 2; + PHY_ID_MASK <<= 1; + PHY_ID_SHIFT++; + } + + hCurrentProcessHandle = GetCurrentProcess(); + + GetProcessAffinityMask(hCurrentProcessHandle, + &dwProcessAffinity, + &dwSystemAffinity); + + // Check if available process affinity mask is equal to the + // available system affinity mask + if (dwProcessAffinity != dwSystemAffinity) { + StatusFlag = HT_CANNOT_DETECT; + // *PhysicalNum = (unsigned char)-1; + return StatusFlag; + } + + dwAffinityMask = 1; + + while (dwAffinityMask != 0 && dwAffinityMask <= dwProcessAffinity) { + // Check if this CPU is available + if (dwAffinityMask & dwProcessAffinity) { + if (SetProcessAffinityMask(hCurrentProcessHandle, + dwAffinityMask)) + { + unsigned char APIC_ID, + LOG_ID, + PHY_ID; + + Sleep(0); // Give OS time to switch CPU + + APIC_ID = GetAPIC_ID(ht_supported); + LOG_ID = APIC_ID & ~PHY_ID_MASK; + PHY_ID = APIC_ID >> PHY_ID_SHIFT; + + if (LOG_ID != 0) { + HT_Enabled = 1; + } + } + } + + dwAffinityMask = dwAffinityMask << 1; + } + + // Reset the processor affinity + SetProcessAffinityMask(hCurrentProcessHandle, dwProcessAffinity); + + if (*LogicalNum == 1) { // Normal P4 : HT is disabled in hardware + StatusFlag = HT_DISABLED; + } + else { + if (HT_Enabled) { + // Total physical processors in a Hyper-Threading enabled system. + *PhysicalNum /= (*LogicalNum); + StatusFlag = HT_ENABLED; + } + else { + StatusFlag = HT_SUPPORTED_NOT_ENABLED; + } + } + } + + } + else { + // Processors do not have Hyper-Threading technology + StatusFlag = HT_NOT_CAPABLE; + *LogicalNum = 1; + } + + return StatusFlag; +} + +unsigned int sigar_cpu_count(sigar_t *sigar) +{ + unsigned char + status, + LogicalNum = 0, + PhysicalNum = 0; + + if (sigar->ncpu != 0) { + return sigar->ncpu; + } + + status = CPUCount(&LogicalNum, &PhysicalNum); + + sigar->ncpu = (unsigned int)PhysicalNum; + + if (status == HT_ENABLED) { + sigar->ht_enabled = 1; + } + else { + sigar->ht_enabled = 0; + } + + return sigar->ncpu; +} + +/* this function is not part of the intel derived code */ + +int sigar_cpu_info_get(sigar_t *sigar, sigar_cpu_info_t *info) +{ + HKEY key, cpu; + int i = 0; + TCHAR id[MAX_PATH + 1]; + LPBYTE value; + DWORD size = 0, rc; + + RegOpenKey(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor", &key); + + //just lookup the first id, then assume all cpus are the same. + rc = RegEnumKey(key, 0, id, sizeof(id)); + if (rc != ERROR_SUCCESS) { + return rc; + } + + rc = RegOpenKey(key, id, &cpu); + if (rc != ERROR_SUCCESS) { + return rc; + } + + size = sizeof(info->vendor); + if (RegQueryValueEx(cpu, "VendorIdentifier", NULL, NULL, + (LPVOID)&info->vendor, &size)) + { + SIGAR_SSTRCPY(info->vendor, "Intel"); + } + + size = sizeof(info->model); + if (RegQueryValueEx(cpu, "Identifier", NULL, NULL, + (LPVOID)&info->model, &size)) + { + SIGAR_SSTRCPY(info->vendor, "x86"); + } + + size = sizeof(info->mhz); // == sizeof(DWORD) + if (RegQueryValueEx(cpu, "~MHz", NULL, NULL, + (LPVOID)&info->mhz, &size)) + { + info->mhz = -1; + } + + info->cache_size = -1; //XXX + + return SIGAR_OK; +} + +#ifdef CPU_MAIN +void main(void) +{ + // Number of logical CPU per ONE PHYSICAL CPU + unsigned char LogicalNum = 0, + PhysicalNum = 0, // Total number of physical processor + HTStatusFlag = 0; + + sigar_cpu_info_t info; + + sigar_cpu_info_get(NULL, &info); + + printf("Vendor............%s\n", info.vendor); + printf("Model.............%s\n", info.model); + printf("Mhz...............%d\n", info.mhz); + + HTStatusFlag = CPUCount(&LogicalNum, &PhysicalNum); + + switch(HTStatusFlag) + { + case HT_NOT_CAPABLE: + printf("Hyper-threading...not capable\n"); + break; + + case HT_DISABLED: + printf("Hyper-threading...disabled\n"); + break; + + case HT_ENABLED: + printf("Hyper-threading...enabled\n"); + break; + + case HT_SUPPORTED_NOT_ENABLED: + printf("Hyper-threading...capable but not enabled\n"); + break; + + case HT_CANNOT_DETECT: + printf("Hyper-threading...cannot be detected\n"); + break; + } + + printf("Logical ratio.....%d/1\n", + LogicalNum); + + if (PhysicalNum != (unsigned char)-1) { + printf("Physical CPUs.....%d\n", PhysicalNum); + } + else { + printf("Can't determine number of physical processors\n"); + printf("Make sure to enable ALL processors\n"); + } +} +#endif diff --git a/src/os/win32/peb.c b/src/os/win32/peb.c new file mode 100644 index 00000000..5b57cf4f --- /dev/null +++ b/src/os/win32/peb.c @@ -0,0 +1,72 @@ +/* + * functions for getting info from the Process Environment Block + */ +#define UNICODE +#define _UNICODE + +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" + +#define START_ADDRESS (LPVOID)0x00020498 + +static int sigar_peb_get(sigar_t *sigar, HANDLE proc, DWORD *base) +{ + MEMORY_BASIC_INFORMATION mbi; + DWORD bytes; + + if (!sigar->peb) { + sigar->peb = malloc(sigar->pagesize); + } + + if (!VirtualQueryEx(proc, START_ADDRESS, &mbi, sizeof(mbi))) { + return GetLastError(); + } + + if (!ReadProcessMemory(proc, mbi.BaseAddress, sigar->peb, + sigar->pagesize, &bytes)) + { + return GetLastError(); + } + + *base = (DWORD)mbi.BaseAddress; + + return SIGAR_OK; +} + +//point scratch to PATH env var +#define PEB_PATH(scratch, base) \ + scratch = sigar->peb + ((DWORD)START_ADDRESS - base) + +//point scratch to EXE (assumes PEB_PATH) +#define PEB_EXE(scratch) \ + scratch = scratch + (wcslen((LPWSTR)scratch) + 1) * sizeof(WCHAR) + +int sigar_proc_exe_name_get(sigar_t *sigar, HANDLE proc, char *name) +{ + int status; + LPBYTE scratch; + DWORD base; + WCHAR buf[MAX_PATH]; + + if ((status = sigar_peb_get(sigar, proc, &base)) != SIGAR_OK) { + return status; + } + + //skip env PATH + PEB_PATH(scratch, base); + + PEB_EXE(scratch); + + //seems common, reason unknown. + if (*scratch == '\0') { + scratch += sizeof(WCHAR); + } + + wcsncpy(buf, (LPWSTR)scratch, MAX_PATH); + buf[MAX_PATH-1] = L'\0'; + + SIGAR_W2A(buf, name, MAX_PATH); + + return SIGAR_OK; +} diff --git a/src/os/win32/sigar_os.h b/src/os/win32/sigar_os.h new file mode 100644 index 00000000..57e61103 --- /dev/null +++ b/src/os/win32/sigar_os.h @@ -0,0 +1,125 @@ +#ifndef SIGAR_OS_H +#define SIGAR_OS_H + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INT64_C(val) val##i64 + +/* see apr/include/arch/win32/atime.h */ +#define EPOCH_DELTA INT64_C(11644473600000000) + +static __inline sigar_uint64_t FileTimeToTime(FILETIME *ft) +{ + sigar_uint64_t time; + time = ft->dwHighDateTime; + time = time << 32; + time |= ft->dwLowDateTime; + time /= 10; + time -= EPOCH_DELTA; + return time; +} + +/* XXX: support CP_UTF8 ? */ + +#define SIGAR_A2W(lpa, lpw, bytes) \ + (lpw[0] = 0, MultiByteToWideChar(CP_ACP, 0, \ + lpa, -1, lpw, (bytes/sizeof(WCHAR)))) + +#define SIGAR_W2A(lpw, lpa, chars) \ + (lpa[0] = '\0', WideCharToMultiByte(CP_ACP, 0, \ + lpw, -1, (LPSTR)lpa, chars, \ + NULL, NULL)) + +#include + +/* undocumented structures */ +typedef struct { + DWORD dwState; + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwRemoteAddr; + DWORD dwRemotePort; + DWORD dwProcessId; +} MIB_TCPEXROW, *PMIB_TCPEXROW; + +typedef struct { + DWORD dwNumEntries; + MIB_TCPEXROW table[ANY_SIZE]; +} MIB_TCPEXTABLE, *PMIB_TCPEXTABLE; + +typedef struct { + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwProcessId; +} MIB_UDPEXROW, *PMIB_UDPEXROW; + +typedef struct { + DWORD dwNumEntries; + MIB_UDPEXROW table[ANY_SIZE]; +} MIB_UDPEXTABLE, *PMIB_UDPEXTABLE; + +/* end undocumented structures */ + +typedef DWORD (CALLBACK *LPGETIPFORWARDTABLE)(PMIB_IPFORWARDTABLE, PULONG, BOOL); + +typedef DWORD (CALLBACK *LPGETIFTABLE)(PMIB_IFTABLE, PULONG, BOOL); + +typedef DWORD (CALLBACK *LPGETTCPTABLE)(PMIB_TCPTABLE, PDWORD, BOOL); + +typedef DWORD (CALLBACK *LPGETUDPTABLE)(PMIB_UDPTABLE, PDWORD, BOOL); + +typedef DWORD (CALLBACK *LPGETTCPEXTABLE)(PMIB_TCPEXTABLE *, BOOL, HANDLE, + DWORD, DWORD); + +typedef struct { + sigar_pid_t pid; + int ppid; + int priority; + time_t mtime; + sigar_uint64_t vsize; + sigar_uint64_t size; + char name[SIGAR_PROC_NAME_LEN]; + char state; + sigar_uint64_t handles; +} sigar_win32_pinfo_t; + +struct sigar_t { + SIGAR_T_BASE; + char *machine; + int using_wide; + long pagesize; + HKEY handle; + char *perfbuf; + DWORD perfbuf_size; + HINSTANCE ip_handle; + LPGETIFTABLE get_if_table; + LPGETIPFORWARDTABLE get_ipforward_table; + LPGETTCPTABLE get_tcp_table; + LPGETTCPEXTABLE get_tcpx_table; + LPGETUDPTABLE get_udp_table; + sigar_win32_pinfo_t pinfo; + WORD ws_version; + int ws_error; + LPBYTE peb; //scratch pad for getting peb info + int ht_enabled; +}; + +int sigar_wsa_init(sigar_t *sigar); + +int sigar_proc_exe_name_get(sigar_t *sigar, HANDLE proc, char *name); + +unsigned int sigar_cpu_count(sigar_t *sigar); + +int sigar_cpu_info_get(sigar_t *sigar, sigar_cpu_info_t *info); + +#endif /* SIGAR_OS_H */ diff --git a/src/os/win32/sigar_pdh.h b/src/os/win32/sigar_pdh.h new file mode 100644 index 00000000..42a746a1 --- /dev/null +++ b/src/os/win32/sigar_pdh.h @@ -0,0 +1,31 @@ +#ifndef SIGAR_PDH_H +#define SIGAR_PDH_H + +/* performance data helpers */ + +#define PdhFirstObject(block) \ + ((PERF_OBJECT_TYPE *)((BYTE *) block + block->HeaderLength)) + +#define PdhNextObject(object) \ + ((PERF_OBJECT_TYPE *)((BYTE *) object + object->TotalByteLength)) + +#define PdhFirstCounter(object) \ + ((PERF_COUNTER_DEFINITION *)((BYTE *) object + object->HeaderLength)) + +#define PdhNextCounter(counter) \ + ((PERF_COUNTER_DEFINITION *)((BYTE *) counter + counter->ByteLength)) + +#define PdhGetCounterBlock(inst) \ + ((PERF_COUNTER_BLOCK *)((BYTE *) inst + inst->ByteLength)) + +#define PdhFirstInstance(object) \ + ((PERF_INSTANCE_DEFINITION *)((BYTE *) object + object->DefinitionLength)) + +#define PdhNextInstance(inst) \ + ((PERF_INSTANCE_DEFINITION *)((BYTE *)inst + inst->ByteLength + \ + PdhGetCounterBlock(inst)->ByteLength)) + +#define PdhInstanceName(inst) \ + ((wchar_t *)((BYTE *)inst + inst->NameOffset)) + +#endif /* SIGAR_PDH_H */ diff --git a/src/os/win32/win32_sigar.c b/src/os/win32/win32_sigar.c new file mode 100644 index 00000000..8604dea3 --- /dev/null +++ b/src/os/win32/win32_sigar.c @@ -0,0 +1,1833 @@ +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_pdh.h" +#include "sigar_os.h" +#include "sigar_util.h" +#include + +#define USING_WIDE_S(s) (s)->using_wide +#define USING_WIDE() USING_WIDE_S(sigar) + +#define PERFBUF_SIZE 8192 + +#define PERF_TITLE_PROC 230 +#define PERF_TITLE_PROC_KEY "230" +#define PERF_TITLE_CPU_KEY "238" + +#define PERF_TITLE_CPU_USER 142 +#define PERF_TITLE_CPU_IDLE 1746 +#define PERF_TITLE_CPU_SYS 144 + +typedef enum { + PERF_IX_CPU_USER, + PERF_IX_CPU_IDLE, + PERF_IX_CPU_SYS, + PERF_IX_CPU_MAX +} perf_cpu_offsets_t; + +#define PERF_TITLE_CPUTIME 6 +#define PERF_TITLE_MEM_VSIZE 174 +#define PERF_TITLE_MEM_SIZE 180 +#define PERF_TITLE_MEM_PRIV 186 +#define PERF_TITLE_THREAD_CNT 680 +#define PERF_TITLE_HANDLE_CNT 952 +#define PERF_TITLE_PID 784 +#define PERF_TITLE_PPID 1410 +#define PERF_TITLE_PRIORITY 682 +#define PERF_TITLE_START_TIME 684 + +typedef enum { + PERF_IX_CPUTIME, + PERF_IX_MEM_VSIZE, + PERF_IX_MEM_SIZE, + PERF_IX_MEM_PRIV, + PERF_IX_THREAD_CNT, + PERF_IX_HANDLE_CNT, + PERF_IX_PID, + PERF_IX_PPID, + PERF_IX_PRIORITY, + PERF_IX_START_TIME, + PERF_IX_MAX +} perf_proc_offsets_t; + +/* + * diff is: + * ExW -> ExA + * wcounter -> counter + */ +#define MyRegQueryValue() \ + (USING_WIDE() ? \ + RegQueryValueExW(sigar->handle, \ + wcounter_key, NULL, &type, \ + sigar->perfbuf, \ + &sigar->perfbuf_size) : \ + RegQueryValueExA(sigar->handle, \ + counter_key, NULL, &type, \ + sigar->perfbuf, \ + &sigar->perfbuf_size)) + +#define PERF_VAL(ix) \ + perf_offsets[ix] ? \ + *((DWORD *)((BYTE *)counter_block + perf_offsets[ix])) : 0 + +static PERF_OBJECT_TYPE *get_perf_object(sigar_t *sigar, char *counter_key) +{ + DWORD retval, type; + WCHAR wcounter_key[MAX_PATH+1]; + PERF_DATA_BLOCK *block; + + if (!sigar->perfbuf) { + sigar->perfbuf = (char *)malloc(PERFBUF_SIZE); + sigar->perfbuf_size = PERFBUF_SIZE; + } + + if (USING_WIDE()) { + SIGAR_A2W(counter_key, wcounter_key, sizeof(wcounter_key)); + } + + while ((retval = MyRegQueryValue()) != ERROR_SUCCESS) { + if (retval == ERROR_MORE_DATA) { + sigar->perfbuf_size += PERFBUF_SIZE; + + sigar->perfbuf = (char *)realloc(sigar->perfbuf, + sigar->perfbuf_size); + } + else { + printf("RegQueryValueEx failed: %d\n", retval); + return NULL; + } + } + + block = (PERF_DATA_BLOCK *)sigar->perfbuf; + + return PdhFirstObject(block); +} + +static void get_sysinfo(sigar_t *sigar) +{ + SYSTEM_INFO sysinfo; + + GetSystemInfo(&sysinfo); + + sigar->ncpu = sysinfo.dwNumberOfProcessors; + sigar->pagesize = sysinfo.dwPageSize; +} + +/* for C# bindings */ +SIGAR_DECLARE(sigar_t *) sigar_new(void) +{ + sigar_t *sigar; + if (sigar_open(&sigar) != SIGAR_OK) { + return NULL; + } + return sigar; +} + +int sigar_os_open(sigar_t **sigar) +{ + LONG result; + HINSTANCE h; + + *sigar = malloc(sizeof(**sigar)); + (*sigar)->machine = ""; /* local machine */ + (*sigar)->using_wide = 0; /*XXX*/ + + (*sigar)->perfbuf = NULL; + (*sigar)->perfbuf_size = 0; + + if (USING_WIDE_S(*sigar)) { + WCHAR wmachine[MAX_PATH+1]; + + SIGAR_A2W((*sigar)->machine, wmachine, sizeof(wmachine)); + + result = RegConnectRegistryW(wmachine, + HKEY_PERFORMANCE_DATA, + &(*sigar)->handle); + } + else { + result = RegConnectRegistryA((*sigar)->machine, + HKEY_PERFORMANCE_DATA, + &(*sigar)->handle); + } + + get_sysinfo(*sigar); + + if ((h = LoadLibrary("iphlpapi.dll"))) { + (*sigar)->get_if_table = + (LPGETIFTABLE)GetProcAddress(h, "GetIfTable"); + (*sigar)->get_ipforward_table = + (LPGETIPFORWARDTABLE)GetProcAddress(h, "GetIpForwardTable"); + (*sigar)->get_tcp_table = + (LPGETTCPTABLE)GetProcAddress(h, "GetTcpTable"); + (*sigar)->get_tcpx_table = + (LPGETTCPEXTABLE)GetProcAddress(h, + "AllocateAndGet" + "TcpExTableFromStack"); + (*sigar)->get_udp_table = + (LPGETUDPTABLE)GetProcAddress(h, "GetUdpTable"); + } + else { + (*sigar)->get_if_table = NULL; + (*sigar)->get_ipforward_table = NULL; + } + + (*sigar)->ip_handle = h; + (*sigar)->pinfo.pid = -1; + (*sigar)->ws_version = 0; + (*sigar)->ncpu = 0; + (*sigar)->peb = NULL; + + return result; +} + +int sigar_os_close(sigar_t *sigar) +{ + int retval; + + if (sigar->perfbuf) { + free(sigar->perfbuf); + } + + retval = RegCloseKey(sigar->handle); + + if (sigar->ip_handle) { + FreeLibrary(sigar->ip_handle); + } + + if (sigar->ws_version != 0) { + WSACleanup(); + } + + if (sigar->peb) { + free(sigar->peb); + } + + free(sigar); + + return retval; +} + +char *sigar_os_error_string(int err) +{ + return NULL; +} + +int sigar_wsa_init(sigar_t *sigar) +{ + if (sigar->ws_version == 0) { + WSADATA data; + + if (WSAStartup(MAKEWORD(2, 0), &data)) { + sigar->ws_error = WSAGetLastError(); + WSACleanup(); + return sigar->ws_error; + } + + sigar->ws_version = data.wVersion; + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem) +{ + MEMORYSTATUS memstat; + + GlobalMemoryStatus(&memstat); + + mem->total = memstat.dwTotalPhys; + mem->free = memstat.dwAvailPhys; + mem->shared = memstat.dwTotalVirtual - memstat.dwAvailVirtual; + mem->used = mem->total - mem->free; + + sigar_mem_calc_ram(sigar, mem); + + /*XXX*/ + mem->buffer = 0; + mem->cached = 0; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap) +{ + MEMORYSTATUS memstat; + + GlobalMemoryStatus(&memstat); + + swap->total = memstat.dwTotalPageFile; + swap->free = memstat.dwAvailPageFile; + swap->used = swap->total - swap->free; + + return SIGAR_OK; +} + +static PERF_INSTANCE_DEFINITION *get_cpu_instance(sigar_t *sigar, + DWORD *perf_offsets, + DWORD *num) +{ + PERF_OBJECT_TYPE *object = get_perf_object(sigar, "238"); + PERF_INSTANCE_DEFINITION *inst; + PERF_COUNTER_DEFINITION *counter; + DWORD i; + + if (!object) { + return NULL; + } + + for (i=0, counter = PdhFirstCounter(object); + iNumCounters; + i++, counter = PdhNextCounter(counter)) + { + DWORD offset = counter->CounterOffset; + + switch (counter->CounterNameTitleIndex) { + case PERF_TITLE_CPU_SYS: + perf_offsets[PERF_IX_CPU_SYS] = offset; + break; + case PERF_TITLE_CPU_USER: + perf_offsets[PERF_IX_CPU_USER] = offset; + break; + case PERF_TITLE_CPU_IDLE: + perf_offsets[PERF_IX_CPU_IDLE] = offset; + break; + } + } + + if (num) { + *num = object->NumInstances; + } + + return PdhFirstInstance(object); +} + +SIGAR_DECLARE(int) sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu) +{ + PERF_INSTANCE_DEFINITION *inst; + PERF_COUNTER_BLOCK *counter_block; + DWORD perf_offsets[PERF_IX_CPU_MAX]; + + SIGAR_ZERO(cpu); + memset(&perf_offsets, 0, sizeof(perf_offsets)); + + inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, 0); + + if (!inst) { + return GetLastError(); + } + + /* first instance is total, rest are per-cpu */ + counter_block = PdhGetCounterBlock(inst); + + cpu->sys = PERF_VAL(PERF_IX_CPU_SYS); + cpu->user = PERF_VAL(PERF_IX_CPU_USER); + cpu->idle = PERF_VAL(PERF_IX_CPU_IDLE); + cpu->nice = 0; /* no nice here */ + + cpu->total = cpu->sys + cpu->user + cpu->idle; + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist) +{ + int status, i, j, hthread=0; + PERF_INSTANCE_DEFINITION *inst; + DWORD perf_offsets[PERF_IX_CPU_MAX], num; + + memset(&perf_offsets, 0, sizeof(perf_offsets)); + + /* first instance is total, rest are per-cpu */ + inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, &num); + --num; + + if (!inst) { + return GetLastError(); + } + + sigar_cpu_count(sigar); + sigar_cpu_list_create(cpulist); + + /* + * if hyper-threading was detected and ncpu is less than + * the number of counter instances, assume there is a counter + * for each logical processor. + * XXX assuming this is correct until have something to test on. + */ + if (sigar->ht_enabled && ((sigar->ncpu * 2) == num)) { + hthread = 1; + } + + for (i=0; idata[cpulist->number-1]; + } + else { + SIGAR_CPU_LIST_GROW(cpulist); + cpu = &cpulist->data[cpulist->number++]; + SIGAR_ZERO(cpu); + } + + inst = PdhNextInstance(inst); + counter_block = PdhGetCounterBlock(inst); + + cpu->sys += PERF_VAL(PERF_IX_CPU_SYS); + cpu->user += PERF_VAL(PERF_IX_CPU_USER); + cpu->idle += PERF_VAL(PERF_IX_CPU_IDLE); + cpu->nice = 0; /* no nice here */ + + cpu->total += cpu->sys + cpu->user + cpu->idle; + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_uptime_get(sigar_t *sigar, + sigar_uptime_t *uptime) +{ + uptime->idletime = 0; + uptime->uptime = GetTickCount() / 1000; + return SIGAR_OK; +} + +/* + * there is no api for this info. + * closest i've seen is enumerating the entire process table + * and calculating an average based on process times. + */ +SIGAR_DECLARE(int) sigar_loadavg_get(sigar_t *sigar, + sigar_loadavg_t *loadavg) +{ + return SIGAR_ENOTIMPL; +} + +#define get_process_object(sigar) \ + get_perf_object(sigar, PERF_TITLE_PROC_KEY) + +SIGAR_DECLARE(int) sigar_proc_list_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + PERF_OBJECT_TYPE *object; + PERF_INSTANCE_DEFINITION *inst; + PERF_COUNTER_DEFINITION *counter; + DWORD i; + DWORD perf_offsets[PERF_IX_MAX]; + + perf_offsets[PERF_IX_PID] = 0; + + object = get_process_object(sigar); + + if (!object) { + return GetLastError(); + } + + sigar_proc_list_create(proclist); + + /* + * note we assume here: + * block->NumObjectTypes == 1 + * object->ObjectNameTitleIndex == PERF_TITLE_PROC + * + * which should always be the case. + */ + + for (i=0, counter = PdhFirstCounter(object); + iNumCounters; + i++, counter = PdhNextCounter(counter)) + { + DWORD offset = counter->CounterOffset; + + switch (counter->CounterNameTitleIndex) { + case PERF_TITLE_PID: + perf_offsets[PERF_IX_PID] = offset; + break; + } + } + + for (i=0, inst = PdhFirstInstance(object); + iNumInstances; + i++, inst = PdhNextInstance(inst)) + { + PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst); + SIGAR_PROC_LIST_GROW(proclist); + + proclist->data[proclist->number++] = + PERF_VAL(PERF_IX_PID); + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_stat_get(sigar_t *sigar, + sigar_proc_stat_t *procstat) +{ + int status = /* XXX optimize */ + sigar_proc_count(sigar, &procstat->total); + + return status; +} + +#define PROCESS_DAC (PROCESS_QUERY_INFORMATION|PROCESS_VM_READ) + +static HANDLE open_process(sigar_pid_t pid) +{ + return OpenProcess(PROCESS_DAC, 0, (DWORD)pid); +} + +SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_mem_t *procmem) +{ + int status = get_proc_info(sigar, pid); + sigar_win32_pinfo_t *pinfo = &sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + procmem->vsize = pinfo->vsize; + procmem->size = pinfo->size; + procmem->resident = -1; + procmem->share = -1; + procmem->rss = -1; + + return SIGAR_OK; +} + +#define TOKEN_DAC (STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY) + +SIGAR_DECLARE(int) +sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_name_t *proccredname) +{ + HANDLE proc, token; + DWORD len; + int success; + TOKEN_USER *user = NULL; + TOKEN_PRIMARY_GROUP *group = NULL; + SID_NAME_USE type; + char domain[SIGAR_CRED_NAME_MAX]; + + /* XXX cache lookup */ + + if (!(proc = open_process(pid))) { + return GetLastError(); + } + + if (!OpenProcessToken(proc, TOKEN_DAC, &token)) { + CloseHandle(proc); + return GetLastError(); + } + + CloseHandle(proc); + + success = + !GetTokenInformation(token, TokenUser, NULL, 0, &len) && + (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && + (user = malloc(len)) && + GetTokenInformation(token, TokenUser, user, len, &len); + + if (success) { + DWORD domain_len = sizeof(domain); + DWORD user_len = sizeof(proccredname->user); + + success = LookupAccountSid(NULL, user->User.Sid, + proccredname->user, &user_len, + domain, &domain_len, &type); + } + + if (user != NULL) { + free(user); + } + if (!success) { + CloseHandle(token); + return GetLastError(); + } + + success = + !GetTokenInformation(token, TokenPrimaryGroup, NULL, 0, &len) && + (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && + (group = malloc(len)) && + GetTokenInformation(token, TokenPrimaryGroup, group, len, &len); + + if (success) { + DWORD domain_len = sizeof(domain); + DWORD group_len = sizeof(proccredname->group); + + success = LookupAccountSid(NULL, group->PrimaryGroup, + proccredname->group, &group_len, + domain, &domain_len, &type); + } + + if (group != NULL) { + free(group); + } + + CloseHandle(token); + + if (!success) { + return GetLastError(); + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_t *proccred) +{ + return SIGAR_ENOTIMPL; +} + +#define FILETIME2SEC(ft) \ + (((ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10000000) + +SIGAR_DECLARE(int) sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_time_t *proctime) +{ + HANDLE proc = open_process(pid); + FILETIME start_time, exit_time, system_time, user_time; + + if (!proc) { + return GetLastError(); + } + + if (!GetProcessTimes(proc, + &start_time, &exit_time, + &system_time, &user_time)) + { + return GetLastError(); + } + + CloseHandle(proc); + + if (start_time.dwHighDateTime) { + proctime->start_time = FileTimeToTime(&start_time) / 1000; + } + else { + proctime->start_time = 0; + } + + proctime->utime = FILETIME2SEC(user_time); + proctime->stime = FILETIME2SEC(system_time); + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_state_t *procstate) +{ + int status = get_proc_info(sigar, pid); + sigar_win32_pinfo_t *pinfo = &sigar->pinfo; + + if (status != SIGAR_OK) { + return status; + } + + memcpy(procstate->name, pinfo->name, sizeof(procstate->name)); + procstate->state = pinfo->state; + procstate->ppid = pinfo->ppid; + procstate->priority = pinfo->priority; + procstate->nice = -1; + procstate->tty = -1; + + return SIGAR_OK; +} + +static int get_proc_info(sigar_t *sigar, sigar_pid_t pid) +{ + PERF_OBJECT_TYPE *object; + PERF_INSTANCE_DEFINITION *inst; + PERF_COUNTER_DEFINITION *counter; + DWORD i; + DWORD perf_offsets[PERF_IX_MAX]; + sigar_win32_pinfo_t *pinfo = &sigar->pinfo; + time_t timenow = time(NULL); + + if (pinfo->pid == pid) { + if ((timenow - pinfo->mtime) < SIGAR_LAST_PROC_EXPIRE) { + return SIGAR_OK; + } + } + + pinfo->pid = pid; + pinfo->mtime = timenow; + + memset(&perf_offsets, 0, sizeof(perf_offsets)); + + object = get_process_object(sigar); + + /* + * note we assume here: + * block->NumObjectTypes == 1 + * object->ObjectNameTitleIndex == PERF_TITLE_PROC + * + * which should always be the case. + */ + + for (i=0, counter = PdhFirstCounter(object); + iNumCounters; + i++, counter = PdhNextCounter(counter)) + { + DWORD offset = counter->CounterOffset; + + switch (counter->CounterNameTitleIndex) { + case PERF_TITLE_CPUTIME: + perf_offsets[PERF_IX_CPUTIME] = offset; + break; + case PERF_TITLE_MEM_VSIZE: + perf_offsets[PERF_IX_MEM_VSIZE] = offset; + break; + case PERF_TITLE_MEM_SIZE: + perf_offsets[PERF_IX_MEM_SIZE] = offset; + break; + case PERF_TITLE_MEM_PRIV: + perf_offsets[PERF_IX_MEM_PRIV] = offset; + break; + case PERF_TITLE_THREAD_CNT: + perf_offsets[PERF_IX_THREAD_CNT] = offset; + break; + case PERF_TITLE_HANDLE_CNT: + perf_offsets[PERF_IX_HANDLE_CNT] = offset; + break; + case PERF_TITLE_PID: + perf_offsets[PERF_IX_PID] = offset; + break; + case PERF_TITLE_PPID: + perf_offsets[PERF_IX_PPID] = offset; + break; + case PERF_TITLE_PRIORITY: + perf_offsets[PERF_IX_PRIORITY] = offset; + break; + case PERF_TITLE_START_TIME: + perf_offsets[PERF_IX_START_TIME] = offset; + break; + } + } + + for (i=0, inst = PdhFirstInstance(object); + iNumInstances; + i++, inst = PdhNextInstance(inst)) + { + PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst); + sigar_pid_t this_pid = PERF_VAL(PERF_IX_PID); + + if (this_pid != pid) { + continue; + } + + pinfo->state = 'R'; /* XXX? */ + SIGAR_W2A(PdhInstanceName(inst), + pinfo->name, sizeof(pinfo->name)); + + pinfo->size = PERF_VAL(PERF_IX_MEM_SIZE); + pinfo->vsize = PERF_VAL(PERF_IX_MEM_VSIZE); + pinfo->ppid = PERF_VAL(PERF_IX_PPID); + pinfo->priority = PERF_VAL(PERF_IX_PRIORITY); + pinfo->handles = PERF_VAL(PERF_IX_HANDLE_CNT); + + return SIGAR_OK; + } + + return -1; /*XXX*/ +} + +static char *getarg(char **line) +{ + char *str = *line, *end; + char *res; + int len; + + while (*str && isspace(*str)) { + ++str; + } + + if (!*str) { + *line = str; + return NULL; + } + + if (*str == '"') { + end = str + 1; + + while (*end && (*end != '"')) { + ++end; + } + + len = end - str - 1; + res = malloc(len+1); + memcpy(res, str+1, len); + res[len] = '\0'; + end++; + } + else { + end = str; + while (*end && !isspace(*end)) { + ++end; + } + + len = end - str; + res = malloc(len+1); + memcpy(res, str, len); + res[len] = '\0'; + } + + while (*end && isspace(*end)) { + ++end; + } + + *line = end; + + return res; +} + +/* + * this is ugly, but there is no alternative. + * we spawn a remote thread within the process + * to call GetCommandLine() and parse it ourselves. + */ +static int sigar_remote_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + FARPROC rcmdline, fstrlen; + HANDLE proc, thread, kdll; + PVOID data=NULL; + char *cmdline; + DWORD rv, thrid, bytes, datalen=0; + + if (!(kdll = GetModuleHandle("kernel32.dll"))) { + return GetLastError(); + } + + if (!(rcmdline = GetProcAddress(kdll, "GetCommandLineA"))) { + return GetLastError(); + } + + if (!(fstrlen = GetProcAddress(kdll, "lstrlenA"))) { + return GetLastError(); + } + + if (!(proc = OpenProcess(MAXIMUM_ALLOWED, 0, (DWORD)pid))) { + return GetLastError(); + } + + thread = CreateRemoteThread(proc, NULL, 0, + (LPTHREAD_START_ROUTINE)rcmdline, + 0, 0, &thrid); + if (!thread) { + CloseHandle(proc); + return GetLastError(); + } + + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, (LPDWORD)(&data)); + CloseHandle(thread); + + if (!data) { + CloseHandle(proc); + return GetLastError(); + } + + thread = CreateRemoteThread(proc, NULL, 0, + (LPTHREAD_START_ROUTINE)fstrlen, + data, 0, &thrid); + if (!thread) { + CloseHandle(proc); + return GetLastError(); + } + + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, &datalen); + CloseHandle(thread); + + if (!datalen) { + CloseHandle(proc); + return GetLastError(); + } + + cmdline = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + datalen); + + if (!cmdline) { + CloseHandle(proc); + return GetLastError(); + } + + if (ReadProcessMemory(proc, data, cmdline, + datalen+1, &bytes)) + { + char *arg, *ptr = cmdline; + + sigar_proc_args_create(procargs); + + while (*ptr && (arg = getarg(&ptr))) { + SIGAR_PROC_ARGS_GROW(procargs); + procargs->data[procargs->number++] = arg; + } + + HeapFree(GetProcessHeap(), 0, cmdline); + } + else { + CloseHandle(proc); + return GetLastError(); + } + + CloseHandle(proc); + return SIGAR_OK; +} + +static int sigar_local_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + LPTSTR cmdline = GetCommandLine(); + char *arg, *ptr = cmdline; + + sigar_proc_args_create(procargs); + + while (*ptr && (arg = getarg(&ptr))) { + SIGAR_PROC_ARGS_GROW(procargs); + procargs->data[procargs->number++] = arg; + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_args_t *procargs) +{ + if (pid == sigar->pid) { + return sigar_local_proc_args_get(sigar, pid, procargs); + } + else { + return sigar_remote_proc_args_get(sigar, pid, procargs); + } +} + +static int sigar_local_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + UCHAR *ptr, *env; + + env = ptr = (UCHAR*)GetEnvironmentStrings(); + + while (*ptr) { + char *val; + int klen, vlen, status; + char key[128]; /* XXX is there a max key size? */ + + if (*ptr == '=') { + ptr += strlen(ptr)+1; + continue; + } + + val = strchr(ptr, '='); + + if (val == NULL) { + break; /*XXX*/ + } + + klen = val - ptr; + SIGAR_SSTRCPY(key, ptr); + key[klen] = '\0'; + ++val; + + vlen = strlen(val); + + status = procenv->env_getter(procenv->data, + key, klen, val, vlen); + + if (status != SIGAR_OK) { + /* not an error; just stop iterating */ + break; + } + + ptr += klen + 1 + vlen + 1; + } + + FreeEnvironmentStrings(env); + + return SIGAR_OK; +} + +static int sigar_remote_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + FARPROC rgetenv, fstrlen; + HANDLE proc, thread, kdll; + PVOID data=NULL; + const char *key; + char *value; + DWORD rv, thrid, bytes, datalen=0, size; + LPVOID addr; + + if (!(kdll = GetModuleHandle("msvcrt.dll"))) { + return GetLastError(); + } + + if (!(rgetenv = GetProcAddress(kdll, "getenv"))) { + return GetLastError(); + } + + if (!(kdll = GetModuleHandle("kernel32.dll"))) { + return GetLastError(); + } + + if (!(fstrlen = GetProcAddress(kdll, "lstrlenA"))) { + return GetLastError(); + } + + if (!(proc = OpenProcess(MAXIMUM_ALLOWED, 0, (DWORD)pid))) { + return GetLastError(); + } + + key = procenv->key; + size = procenv->klen+1; + addr = VirtualAllocEx(proc, NULL, size, + MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (!addr) { + CloseHandle(proc); + return GetLastError(); + } + + if (!WriteProcessMemory(proc, addr, (char*)&key[0], size, 0)) { + VirtualFreeEx(proc, addr, size, 0); + CloseHandle(proc); + return GetLastError(); + } + + thread = CreateRemoteThread(proc, NULL, 0, + (LPTHREAD_START_ROUTINE)rgetenv, + addr, 0, &thrid); + if (!thread) { + VirtualFreeEx(proc, addr, size, 0); + CloseHandle(proc); + return GetLastError(); + } + + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, (LPDWORD)(&data)); + CloseHandle(thread); + VirtualFreeEx(proc, addr, size, 0); + + if (!data) { + CloseHandle(proc); + return SIGAR_OK; + } + + thread = CreateRemoteThread(proc, NULL, 0, + (LPTHREAD_START_ROUTINE)fstrlen, + data, 0, &thrid); + if (!thread) { + CloseHandle(proc); + return GetLastError(); + } + + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, &datalen); + CloseHandle(thread); + + if (!datalen) { + CloseHandle(proc); + return GetLastError(); + } + + value = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + datalen); + + if (!value) { + CloseHandle(proc); + return GetLastError(); + } + + if (ReadProcessMemory(proc, data, value, + datalen+1, &bytes)) + { + procenv->env_getter(procenv->data, + key, strlen(key), + value, bytes-1); + + HeapFree(GetProcessHeap(), 0, value); + } + else { + CloseHandle(proc); + return GetLastError(); + } + + CloseHandle(proc); + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_env_t *procenv) +{ + if (pid == sigar->pid) { + if (procenv->type == SIGAR_PROC_ENV_KEY) { + char value[32767]; /* max size from msdn docs */ + DWORD retval = + GetEnvironmentVariable(procenv->key, value, sizeof(value)); + + if (retval == 0) { + if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + return SIGAR_OK; + } + return GetLastError(); + } + else if (retval > sizeof(value)) { + /* XXX shouldnt happen */ + return GetLastError(); + } + + procenv->env_getter(procenv->data, + procenv->key, procenv->klen, + value, retval); + return SIGAR_OK; + } + else { + return sigar_local_proc_env_get(sigar, pid, procenv); + } + } + else { + if (procenv->type == SIGAR_PROC_ENV_KEY) { + return sigar_remote_proc_env_get(sigar, pid, procenv); + } + else { + return SIGAR_ENOTIMPL; + } + } +} + +SIGAR_DECLARE(int) sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_fd_t *procfd) +{ + int status; + sigar_win32_pinfo_t *pinfo = &sigar->pinfo; + + pinfo->pid = -1; /* force update */ + if ((status = get_proc_info(sigar, pid)) != SIGAR_OK) { + return status; + } + + procfd->total = pinfo->handles; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_exe_t *procexe) +{ + int status = SIGAR_OK; + HANDLE proc = open_process(pid); + + if (!proc) { + return GetLastError(); + } + + procexe->cwd[0] = '\0'; + procexe->root[0] = '\0'; + + status = sigar_proc_exe_name_get(sigar, proc, procexe->name); + + return status; +} + +int sigar_os_fs_type_get(sigar_file_system_t *fsp) +{ + return fsp->type; +} + +SIGAR_DECLARE(int) sigar_file_system_list_get(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + sigar_file_system_t *fsp; + char name[256]; + char *ptr = name; + /* XXX: hmm, Find{First,Next}Volume not available in my sdk */ + DWORD len = GetLogicalDriveStringsA(sizeof(name), name); + + if (len == 0) { + return GetLastError(); + } + + sigar_file_system_list_create(fslist); + + while (*ptr) { + DWORD flags, serialnum=0; + char fsname[1024]; + UINT type; + + fsname[0] = '\0'; + + GetVolumeInformation(ptr, NULL, 0, &serialnum, NULL, + &flags, fsname, sizeof(fsname)); + + type = GetDriveType(ptr); + + if (!serialnum && (type == DRIVE_FIXED)) { + ptr += strlen(ptr)+1; + continue; /* ignore unformatted partitions */ + } + + SIGAR_FILE_SYSTEM_LIST_GROW(fslist); + + fsp = &fslist->data[fslist->number++]; + + SIGAR_SSTRCPY(fsp->dir_name, ptr); + SIGAR_SSTRCPY(fsp->dev_name, ptr); + + switch (type) { + case DRIVE_FIXED: + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + break; + case DRIVE_REMOTE: + fsp->type = SIGAR_FSTYPE_NETWORK; + break; + case DRIVE_CDROM: + fsp->type = SIGAR_FSTYPE_CDROM; + break; + case DRIVE_RAMDISK: + fsp->type = SIGAR_FSTYPE_RAM_DISK; + break; + case DRIVE_REMOVABLE: + /* XXX */ + default: + fsp->type = SIGAR_FSTYPE_NONE; + break; + } + + /* we set fsp->type, just looking up sigar.c:fstype_names[type] */ + sigar_fs_type_get(fsp); + + if (*fsname == '\0') { + SIGAR_SSTRCPY(fsp->sys_type_name, fsp->type_name); + } + else { + SIGAR_SSTRCPY(fsp->sys_type_name, fsname); /* CDFS, NTFS, etc */ + } + + ptr += strlen(ptr)+1; + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) +sigar_file_system_usage_get(sigar_t *sigar, + const char *dirname, + sigar_file_system_usage_t *fsusage) +{ + BOOL retval; + ULARGE_INTEGER avail, total, free; + + /* prevent dialog box if A:\ drive is empty */ + UINT errmode = SetErrorMode(SEM_FAILCRITICALERRORS); + + retval = GetDiskFreeSpaceEx(dirname, + &avail, &total, &free); + + /* restore previous error mode */ + SetErrorMode(errmode); + + if (!retval) { + return GetLastError(); + } + + fsusage->total = total.QuadPart / 1024; + fsusage->free = free.QuadPart / 1024; + fsusage->avail = avail.QuadPart / 1024; + + fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage); + + /* XXX */ + fsusage->files = 0; + fsusage->free_files = 0; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_cpu_infos_get(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + int i, status; + sigar_cpu_info_t *info; + + sigar_cpu_count(sigar); + + sigar_cpu_infos_create(cpu_infos); + + info = &cpu_infos->data[cpu_infos->number++]; + + status = sigar_cpu_info_get(sigar, info); + + if (status != SIGAR_OK) { + return status; + } + + if (sigar->ncpu > 1) { + for (i=1; incpu; i++) { + SIGAR_CPU_INFOS_GROW(cpu_infos); + + memcpy(&cpu_infos->data[cpu_infos->number++], + info, sizeof(*info)); + } + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_net_route_list_get(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + char *buffer = NULL; + ULONG bufsize = 0; + DWORD rc, i; + MIB_IPFORWARDTABLE *ipt; + sigar_net_route_t *route; + + if (!sigar->get_ipforward_table) { + return SIGAR_ENOTIMPL; + } + + rc = (*(sigar->get_ipforward_table))((PMIB_IPFORWARDTABLE)buffer, + &bufsize, FALSE); + if (rc != ERROR_INSUFFICIENT_BUFFER) { + return GetLastError(); + } + + buffer = malloc(bufsize); + rc = (*(sigar->get_ipforward_table))((PMIB_IPFORWARDTABLE)buffer, + &bufsize, FALSE); + if (rc != NO_ERROR) { + free(buffer); + return GetLastError(); + } + + sigar_net_route_list_create(routelist); + routelist->size = routelist->number = 0; + + ipt = (MIB_IPFORWARDTABLE *)buffer; + + for (i=0; idwNumEntries; i++) { + MIB_IPFORWARDROW *ipr = ipt->table + i; + + SIGAR_NET_ROUTE_LIST_GROW(routelist); + + route = &routelist->data[routelist->number++]; + SIGAR_ZERO(route); /* XXX: other fields */ + + route->destination = ipr->dwForwardDest; + route->mask = ipr->dwForwardMask; + route->gateway = ipr->dwForwardNextHop; + } + + free(buffer); + + return SIGAR_OK; +} + +#define IFTYPE_LO 2 +#define IFTYPE_ETH 3 + +static int get_iftype(const char *name, int *type, int *inst) +{ + if (strnEQ(name, "eth", IFTYPE_ETH)) { + *type = IFTYPE_ETH; + } + else if (strnEQ(name, "lo", IFTYPE_LO)) { + *type = IFTYPE_LO; + } + else { + return EINVAL; + } + + if (isdigit(*(name + *type))) { + *inst = atoi(name + *type); + } + else { + return EINVAL; + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) +sigar_net_interface_stat_get(sigar_t *sigar, const char *name, + sigar_net_interface_stat_t *ifstat) +{ + char *buffer = NULL; + ULONG buf_size = 0; + DWORD rc, i; + MIB_IFTABLE *ift; + MIB_IFROW *ifr; + DWORD lo=0, eth=0; + int status, type, inst; + + if ((status = get_iftype(name, &type, &inst)) != SIGAR_OK) { + return status; + } + + if (!sigar->get_if_table) { + return SIGAR_ENOTIMPL; + } + + rc = (*(sigar->get_if_table))((PMIB_IFTABLE)buffer, &buf_size, FALSE); + if (rc != ERROR_INSUFFICIENT_BUFFER) { + return GetLastError(); + } + + buffer = malloc(buf_size); + rc = (*(sigar->get_if_table))((PMIB_IFTABLE)buffer, &buf_size, FALSE); + if (rc != NO_ERROR) { + free(buffer); + return GetLastError(); + } + + ift = (MIB_IFTABLE *)buffer; + + for (i=0; idwNumEntries; i++) { + ifr = ift->table + i; + + if (!(ifr->dwOperStatus & MIB_IF_OPER_STATUS_OPERATIONAL)) { + continue; + } + + if (ifr->dwType == MIB_IF_TYPE_LOOPBACK) { + if ((type == IFTYPE_LO) && (inst == lo)) { + break; + } + ++lo; + } + else if (ifr->dwType == MIB_IF_TYPE_ETHERNET) { + if ((type == IFTYPE_ETH) && (inst == eth)) { + break; + } + ++eth; + } + + ifr = NULL; + } + + if (!ifr) { + free(buffer); + return ENOENT; + } + + ifstat->rx_bytes = ifr->dwInOctets; + ifstat->rx_packets = ifr->dwInUcastPkts + ifr->dwInNUcastPkts; + ifstat->rx_errors = ifr->dwInErrors; + ifstat->rx_dropped = ifr->dwInDiscards; + ifstat->rx_overruns = 0; /*XXX*/ + ifstat->rx_frame = 0; /*XXX*/ + + ifstat->tx_bytes = ifr->dwOutOctets; + ifstat->tx_packets = ifr->dwOutUcastPkts + ifr->dwOutNUcastPkts; + ifstat->tx_errors = ifr->dwOutErrors; + ifstat->tx_dropped = ifr->dwOutDiscards; + ifstat->tx_overruns = 0; /*XXX*/ + ifstat->tx_collisions = 0; /*XXX*/ + ifstat->tx_carrier = 0; /*XXX*/ + + free(buffer); + + return SIGAR_OK; +} + +static int get_iflist(sigar_t *sigar, char *buffer, DWORD buflen, DWORD *bytes) +{ + SOCKET sock = INVALID_SOCKET; + DWORD rc; + int status = sigar_wsa_init(sigar); + + if (status != SIGAR_OK) { + return status; + } + + sock = WSASocket(PF_INET, SOCK_RAW, AF_INET, 0, 0, 0); + if (sock == INVALID_SOCKET) { + return WSAGetLastError(); + } + + rc = WSAIoctl(sock, + SIO_GET_INTERFACE_LIST, + NULL, + 0, + (void *)buffer, + buflen, + bytes, + 0, + 0); + + status = rc ? WSAGetLastError() : SIGAR_OK; + + closesocket(sock); + + return status; +} + +#include + +static void hwaddr_lookup(sigar_net_interface_config_t *ifconfig, int num) +{ + NCB ncb; + UCHAR rc; + struct { + ADAPTER_STATUS status; + NAME_BUFFER name[30]; + } adapter; + + memset(&ncb, 0, sizeof(ncb)); + ncb.ncb_command = NCBRESET; + ncb.ncb_lana_num = num; + Netbios(&ncb); + + memset(&ncb, 0, sizeof(ncb)); + ncb.ncb_command = NCBASTAT; + ncb.ncb_lana_num = num; + + /* + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netbios/netbios_1l82.asp + * mdsn docs claim this needs to be padded with spaces and + * suggest the following silly code: + * strcpy(ncb.ncb_callname, "* "); + */ + ncb.ncb_callname[0] = '*'; + memset(&ncb.ncb_callname[1], ' ', + sizeof(ncb.ncb_callname)-1); + + ncb.ncb_callname[sizeof(ncb.ncb_callname)] = '\0'; + + ncb.ncb_buffer = (unsigned char *)&adapter; + ncb.ncb_length = sizeof(adapter); + if ((rc = Netbios(&ncb)) == 0) { + sigar_hwaddr_format(ifconfig->hwaddr, + adapter.status.adapter_address); + } + else { + sigar_hwaddr_set_null(ifconfig); + } +} + +SIGAR_DECLARE(int) +sigar_net_interface_config_get(sigar_t *sigar, + const char *name, + sigar_net_interface_config_t *ifconfig) +{ + char buffer[8192]; + DWORD i, num, bytes, inst; + DWORD lo=0, eth=0; + int status, type; + INTERFACE_INFO *if_info = NULL; + u_long flags; + + /* win32 lacks socket ioctls to query given interface. + * so we loop through the list to find our made up ifname. + */ + status = get_iflist(sigar, buffer, sizeof(buffer), &bytes); + if (status != SIGAR_OK) { + return status; + } + + num = bytes / sizeof(INTERFACE_INFO); + + if ((status = get_iftype(name, &type, &inst)) != SIGAR_OK) { + return status; + } + + for (i=0; iiiFlags & IFF_LOOPBACK) { + if ((type == IFTYPE_LO) && (inst == lo)) { + break; + } + ++lo; + } + else { + if ((type == IFTYPE_ETH) && (inst == eth)) { + break; + } + ++eth; + } + + if_info = NULL; + } + + if (!if_info) { + return ENOENT; + } + + SIGAR_ZERO(ifconfig); + + SIGAR_SSTRCPY(ifconfig->name, name); + +#define if_s_addr(a) \ + ((struct sockaddr_in *)&a)->sin_addr.s_addr + + ifconfig->address = if_s_addr(if_info->iiAddress); + ifconfig->broadcast = if_s_addr(if_info->iiBroadcastAddress); + ifconfig->netmask = if_s_addr(if_info->iiNetmask); + + flags = if_info->iiFlags; + + if (flags & IFF_UP) { + ifconfig->flags |= SIGAR_IFF_UP|SIGAR_IFF_RUNNING; + } + if (flags & IFF_BROADCAST) { + ifconfig->flags |= SIGAR_IFF_BROADCAST; + } + if (flags & IFF_LOOPBACK) { + ifconfig->flags |= SIGAR_IFF_LOOPBACK; + ifconfig->destination = ifconfig->address; + ifconfig->broadcast = 0; + sigar_hwaddr_set_null(ifconfig); + } + else { + hwaddr_lookup(ifconfig, i); + } + if (flags & IFF_POINTTOPOINT) { + ifconfig->flags |= SIGAR_IFF_POINTOPOINT; + } + if (flags & IFF_MULTICAST) { + ifconfig->flags |= SIGAR_IFF_MULTICAST; + } + + return SIGAR_OK; +} + +/* + * win32 interface list does not include a name. + * and the name from GetIfList() is the name of card + * including vendor name, etc. so we use 'eth' for ethernet + * interfaces and 'lo' for loopback. + */ + +#define ETH "eth" +#define LO "lo" + +SIGAR_DECLARE(int) +sigar_net_interface_list_get(sigar_t *sigar, + sigar_net_interface_list_t *iflist) +{ + char eth[56], lo[56]; + int ethcnt=0, locnt=0; + char buffer[8192]; + DWORD i, num, bytes; + int status; + + status = get_iflist(sigar, buffer, sizeof(buffer), &bytes); + if (status != SIGAR_OK) { + return status; + } + + num = bytes / sizeof(INTERFACE_INFO); + + iflist->number = 0; + iflist->size = num; + iflist->data = + (char **)malloc(sizeof(*(iflist->data)) * + iflist->size); + + for (i=0; iiiFlags & IFF_LOOPBACK) { + sprintf(lo, LO "%d", locnt++); + name = strdup(lo); + } + else { + /* XXX: assuming ethernet here */ + sprintf(eth, ETH "%d", ethcnt++); + name = strdup(eth); + } + + iflist->data[iflist->number++] = name; + } + + return SIGAR_OK; +} + +static void ip_format(char *buffer, int buflen, UINT addr) +{ + UINT ip = htonl(addr); + + sprintf(buffer, "%d.%d.%d.%d", + ((ip >> 24) & 0xFF), + ((ip >> 16) & 0xFF), + ((ip >> 8) & 0xFF), + ((ip) & 0xFF)); +} + +static int net_conn_get_tcp(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + int status; + DWORD rc, size, i; + PMIB_TCPTABLE tcp; + + size = 0; + rc = sigar->get_tcp_table(NULL, &size, FALSE); + if (rc != ERROR_INSUFFICIENT_BUFFER) { + return GetLastError(); + } + tcp = (PMIB_TCPTABLE)malloc(size); + rc = sigar->get_tcp_table(tcp, &size, FALSE); + if (rc) { + free(tcp); + return GetLastError(); + } + + for (i = 0; i < tcp->dwNumEntries; i++) { + sigar_net_connection_t conn; + DWORD state = tcp->table[i].dwState; + + if (flags & SIGAR_NETCONN_SERVER) { + if (state != MIB_TCP_STATE_LISTEN) { + continue; + } + } + else if (flags & SIGAR_NETCONN_CLIENT) { + if (state == MIB_TCP_STATE_LISTEN) { + continue; + } + } + + conn.local_port = htons((WORD)tcp->table[i].dwLocalPort); + conn.remote_port = htons((WORD)tcp->table[i].dwRemotePort); + + conn.type = SIGAR_NETCONN_TCP; + + ip_format(conn.local_address, + sizeof(conn.local_address), + tcp->table[i].dwLocalAddr); + + ip_format(conn.remote_address, + sizeof(conn.remote_address), + tcp->table[i].dwRemoteAddr); + + SIGAR_NET_CONNLIST_GROW(connlist); + memcpy(&connlist->data[connlist->number++], + &conn, sizeof(conn)); + } + + free(tcp); + return SIGAR_OK; +} + +static int net_conn_get_udp(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + int status; + DWORD rc, size, i; + PMIB_UDPTABLE udp; + + size = 0; + rc = sigar->get_udp_table(NULL, &size, FALSE); + if (rc != ERROR_INSUFFICIENT_BUFFER) { + return GetLastError(); + } + udp = (PMIB_UDPTABLE)malloc(size); + rc = sigar->get_udp_table(udp, &size, FALSE); + if (rc) { + free(udp); + return GetLastError(); + } + + for (i = 0; i < udp->dwNumEntries; i++) { + sigar_net_connection_t conn; + + if (!((conn.remote_port && (flags & SIGAR_NETCONN_CLIENT)) || + (!conn.remote_port && (flags & SIGAR_NETCONN_SERVER)))) + { + continue; + } + + conn.local_port = htons((WORD)udp->table[i].dwLocalPort); + conn.remote_port = conn.local_port; + + conn.type = SIGAR_NETCONN_UDP; + + ip_format(conn.local_address, + sizeof(conn.local_address), + udp->table[i].dwLocalAddr); + + ip_format(conn.remote_address, + sizeof(conn.remote_address), + udp->table[i].dwLocalAddr); + + SIGAR_NET_CONNLIST_GROW(connlist); + memcpy(&connlist->data[connlist->number++], + &conn, sizeof(conn)); + } + + free(udp); + return SIGAR_OK; +} + +SIGAR_DECLARE(int) +sigar_net_connection_list_get(sigar_t *sigar, + sigar_net_connection_list_t *connlist, + int flags) +{ + int status; + + sigar_net_connection_list_create(connlist); + + if (flags & SIGAR_NETCONN_TCP) { + status = net_conn_get_tcp(sigar, connlist, flags); + + if (status != SIGAR_OK) { + return status; + } + } + + if (flags & SIGAR_NETCONN_UDP) { + status = net_conn_get_udp(sigar, connlist, flags); + + if (status != SIGAR_OK) { + return status; + } + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_port_get(sigar_t *sigar, + unsigned long port, + sigar_pid_t *pid) +{ + int status; + DWORD rc, i; + PMIB_TCPEXTABLE tcp; + + if (!sigar->get_tcpx_table) { + return SIGAR_ENOTIMPL; + } + + rc = sigar->get_tcpx_table(&tcp, FALSE, GetProcessHeap(), + 2, 2); + + if (rc) { + return GetLastError(); + } + + for (i=0; idwNumEntries; i++) { + if (tcp->table[i].dwState != MIB_TCP_STATE_LISTEN) { + continue; + } + + if (htons((WORD)tcp->table[i].dwLocalPort) != port) { + continue; + } + + *pid = tcp->table[i].dwProcessId; + + return SIGAR_OK; + } + + return ENOENT; +} diff --git a/src/sigar.c b/src/sigar.c new file mode 100644 index 00000000..32c01f42 --- /dev/null +++ b/src/sigar.c @@ -0,0 +1,1240 @@ + +#include + +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#ifndef WIN32 +#include +#endif + +SIGAR_DECLARE(int) sigar_open(sigar_t **sigar) +{ + int status = sigar_os_open(sigar); + + if (status == SIGAR_OK) { + (*sigar)->pid = 0; + (*sigar)->ifconf_buf = NULL; + (*sigar)->ifconf_len = 0; + (*sigar)->log_level = -1; /* log nothing by default */ + (*sigar)->log_impl = NULL; + (*sigar)->log_data = NULL; + } + + return status; +} + +SIGAR_DECLARE(int) sigar_close(sigar_t *sigar) +{ + if (sigar->ifconf_buf) { + free(sigar->ifconf_buf); + } + + return sigar_os_close(sigar); +} + +#ifndef __linux__ /* linux has a special case */ +SIGAR_DECLARE(sigar_pid_t) sigar_pid_get(sigar_t *sigar) +{ + if (!sigar->pid) { + sigar->pid = getpid(); + } + + return sigar->pid; +} +#endif + +SIGAR_DECLARE(int) sigar_proc_kill(sigar_pid_t pid, int signum) +{ +#ifdef WIN32 + int status = -1; + HANDLE proc = + OpenProcess(PROCESS_ALL_ACCESS, + TRUE, (DWORD)pid); + + if (proc) { + switch (signum) { + case 0: + status = SIGAR_OK; + break; + default: + if (TerminateProcess(proc, signum)) { + status = SIGAR_OK; + } + break; + } + + CloseHandle(proc); + + if (status == SIGAR_OK) { + return SIGAR_OK; + } + } + return GetLastError(); +#else + if (kill(pid, signum) == -1) { + return errno; + } + return SIGAR_OK; +#endif +} + +static char *sigar_error_string(int err) +{ + switch (err) { + case SIGAR_ENOTIMPL: + return "This function has not been implemented on this platform"; + default: + return "Error string not specified yet"; + } +} + +SIGAR_DECLARE(char *) sigar_strerror(sigar_t *sigar, int err) +{ + char *buf = NULL; +#ifdef WIN32 + DWORD len; +#endif + + if (err > SIGAR_OS_START_ERROR) { + if ((buf = sigar_os_error_string(err)) != NULL) { + return buf; + } + return "Unknown OS Error"; /* should never happen */ + } + + if (err > SIGAR_START_ERROR) { + return sigar_error_string(err); + } + +#ifdef WIN32 + len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + 0, /* default language */ + (LPTSTR)sigar->errbuf, + (DWORD)sizeof(sigar->errbuf), + NULL); +#else + +#if defined(HAVE_STRERROR_R) && defined(HAVE_STRERROR_R_GLIBC) + /* + * strerror_r man page says: + * "The GNU version may, but need not, use the user supplied buffer" + */ + buf = strerror_r(err, sigar->errbuf, sizeof(sigar->errbuf)); +#elif defined(HAVE_STRERROR_R) + if (strerror_r(err, sigar->errbuf, sizeof(sigar->errbuf)) < 0) { + buf = "Unknown Error"; + } +#else + /* strerror() is thread safe on solaris and hpux */ + buf = strerror(err); +#endif + + if (buf != NULL) { + SIGAR_SSTRCPY(sigar->errbuf, buf); + } + +#endif + return sigar->errbuf; +} + +#include /* for sprintf */ + +SIGAR_DECLARE(int) sigar_uptime_string(sigar_t *sigar, + sigar_uptime_t *uptime, + char *buffer, + int buflen) +{ + char *ptr = buffer; + int minutes, hours, days, offset = 0; + + /* XXX: get rid of sprintf and/or check for overflow */ + days = uptime->uptime / (60*60*24); + + if (days) { + offset += sprintf(ptr + offset, "%d day%s, ", + days, (days > 1) ? "s" : ""); + } + + minutes = (int)uptime->uptime / 60; + hours = minutes / 60; + hours = hours % 24; + minutes = minutes % 60; + + if (hours) { + offset += sprintf(ptr + offset, "%2d:%02d", + hours, minutes); + } + else { + offset += sprintf(ptr + offset, "%d min", minutes); + } + + return SIGAR_OK; +} + +/* copy apr_strfsize */ +SIGAR_DECLARE(char *) sigar_format_size(sigar_uint64_t size, char *buf) +{ + const char ord[] = "KMGTPE"; + const char *o = ord; + int remain; + + if (size < 973) { + sprintf(buf, "%3d ", (int) size); + return buf; + } + + do { + remain = (int)(size & 1023); + size >>= 10; + + if (size >= 973) { + ++o; + continue; + } + + if (size < 9 || (size == 9 && remain < 973)) { + if ((remain = ((remain * 5) + 256) / 512) >= 10) { + ++size; + remain = 0; + } + sprintf(buf, "%d.%d%c", (int) size, remain, *o); + return buf; + } + + if (remain >= 512) { + ++size; + } + + sprintf(buf, "%3d%c", (int) size, *o); + + return buf; + } while (1); +} + +#ifndef WIN32 +#include +#include + +int sigar_user_name_get(sigar_t *sigar, int uid, char *buf, int buflen) +{ + struct passwd *pw; + /* XXX cache lookup */ + +# ifdef HAVE_GETPWUID_R + struct passwd pwbuf; + char buffer[512]; + + if (getpwuid_r(uid, &pwbuf, buffer, sizeof(buffer), &pw) != 0) { + return errno; + } +# else + if ((pw = getpwuid(uid)) == NULL) { + return errno; + } +# endif + + strncpy(buf, pw->pw_name, buflen); + buf[buflen-1] = '\0'; + + return SIGAR_OK; +} + +int sigar_group_name_get(sigar_t *sigar, int gid, char *buf, int buflen) +{ + struct group *gr; + /* XXX cache lookup */ + +# ifdef HAVE_GETGRGID_R + struct group grbuf; + char buffer[512]; + + if (getgrgid_r(gid, &grbuf, buffer, sizeof(buffer), &gr) != 0) { + return errno; + } +# else + if ((gr = getgrgid(gid)) == NULL) { + return errno; + } +# endif + + strncpy(buf, gr->gr_name, buflen); + buf[buflen-1] = '\0'; + + return SIGAR_OK; +} + +int sigar_user_id_get(sigar_t *sigar, const char *name, int *uid) +{ + /* XXX cache lookup */ + struct passwd *pw; + +# ifdef HAVE_GETPWNAM_R + struct passwd pwbuf; + char buf[512]; + + if (getpwnam_r(name, &pwbuf, buf, sizeof(buf), &pw) != 0) { + return errno; + } +# else + if (!(pw = getpwnam(name))) { + return errno; + } +# endif + + *uid = (int)pw->pw_uid; + return SIGAR_OK; +} + +SIGAR_DECLARE(int) +sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid, + sigar_proc_cred_name_t *proccredname) +{ + sigar_proc_cred_t cred; + + int status = sigar_proc_cred_get(sigar, pid, &cred); + + if (status != SIGAR_OK) { + return status; + } + + status = sigar_user_name_get(sigar, cred.uid, + proccredname->user, + sizeof(proccredname->user)); + + if (status != SIGAR_OK) { + return status; + } + + status = sigar_group_name_get(sigar, cred.gid, + proccredname->group, + sizeof(proccredname->group)); + + return status; +} + +#endif /* WIN32 */ + +int sigar_proc_list_create(sigar_proc_list_t *proclist) +{ + proclist->number = 0; + proclist->size = SIGAR_PROC_LIST_MAX; + proclist->data = malloc(sizeof(*(proclist->data)) * + proclist->size); + return SIGAR_OK; +} + +int sigar_proc_list_grow(sigar_proc_list_t *proclist) +{ + proclist->data = realloc(proclist->data, + sizeof(*(proclist->data)) * + (proclist->size + SIGAR_PROC_LIST_MAX)); + proclist->size += SIGAR_PROC_LIST_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_list_destroy(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + if (proclist->size) { + free(proclist->data); + proclist->number = proclist->size = 0; + } + + return SIGAR_OK; +} + +int sigar_proc_args_create(sigar_proc_args_t *procargs) +{ + procargs->number = 0; + procargs->size = SIGAR_PROC_ARGS_MAX; + procargs->data = malloc(sizeof(*(procargs->data)) * + procargs->size); + return SIGAR_OK; +} + +int sigar_proc_args_grow(sigar_proc_args_t *procargs) +{ + procargs->data = realloc(procargs->data, + sizeof(*(procargs->data)) * + (procargs->size + SIGAR_PROC_ARGS_MAX)); + procargs->size += SIGAR_PROC_ARGS_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_proc_args_destroy(sigar_t *sigar, + sigar_proc_args_t *procargs) +{ + unsigned int i; + + if (procargs->size) { + for (i=0; inumber; i++) { + free(procargs->data[i]); + } + free(procargs->data); + procargs->number = procargs->size = 0; + } + + return SIGAR_OK; +} + +int sigar_file_system_list_create(sigar_file_system_list_t *fslist) +{ + fslist->number = 0; + fslist->size = SIGAR_FS_MAX; + fslist->data = malloc(sizeof(*(fslist->data)) * + fslist->size); + return SIGAR_OK; +} + +int sigar_file_system_list_grow(sigar_file_system_list_t *fslist) +{ + fslist->data = realloc(fslist->data, + sizeof(*(fslist->data)) * + (fslist->size + SIGAR_FS_MAX)); + fslist->size += SIGAR_FS_MAX; + + return SIGAR_OK; +} + +/* indexed with sigar_file_system_type_e */ +static const char *fstype_names[] = { + "unknown", "none", "local", "remote", "ram", "cdrom", "swap" +}; + +static int sigar_common_fs_type_get(sigar_file_system_t *fsp) +{ + char *type = fsp->sys_type_name; + + switch (*type) { + case 'n': + if (strEQ(type, "nfs")) { + fsp->type = SIGAR_FSTYPE_NETWORK; + } + break; + case 's': + if (strEQ(type, "smbfs")) { /* samba */ + fsp->type = SIGAR_FSTYPE_NETWORK; + } + else if (strEQ(type, "swap")) { + fsp->type = SIGAR_FSTYPE_SWAP; + } + break; + case 'a': + if (strEQ(type, "afs")) { + fsp->type = SIGAR_FSTYPE_NETWORK; + } + break; + case 'i': + if (strEQ(type, "iso9660")) { + fsp->type = SIGAR_FSTYPE_CDROM; + } + break; + case 'm': + if (strEQ(type, "msdos") || strEQ(type, "minix")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'h': + if (strEQ(type, "hpfs")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + case 'v': + if (strEQ(type, "vfat")) { + fsp->type = SIGAR_FSTYPE_LOCAL_DISK; + } + break; + } + + return fsp->type; +} + +void sigar_fs_type_get(sigar_file_system_t *fsp) +{ + if (!(fsp->type || /* already set */ + sigar_os_fs_type_get(fsp) || /* try os specifics first */ + sigar_common_fs_type_get(fsp))) /* try common ones last */ + { + fsp->type = SIGAR_FSTYPE_NONE; + } + + if (fsp->type >= SIGAR_FSTYPE_MAX) { + fsp->type = SIGAR_FSTYPE_NONE; + } + + strcpy(fsp->type_name, fstype_names[fsp->type]); +} + + +SIGAR_DECLARE(int) +sigar_file_system_list_destroy(sigar_t *sigar, + sigar_file_system_list_t *fslist) +{ + if (fslist->size) { + free(fslist->data); + fslist->number = fslist->size = 0; + } + + return SIGAR_OK; +} + +int sigar_cpu_infos_create(sigar_cpu_infos_t *cpu_infos) +{ + cpu_infos->number = 0; + cpu_infos->size = SIGAR_CPU_INFO_MAX; + cpu_infos->data = malloc(sizeof(*(cpu_infos->data)) * + cpu_infos->size); + return SIGAR_OK; +} + +int sigar_cpu_infos_grow(sigar_cpu_infos_t *cpu_infos) +{ + cpu_infos->data = realloc(cpu_infos->data, + sizeof(*(cpu_infos->data)) * + (cpu_infos->size + SIGAR_CPU_INFO_MAX)); + cpu_infos->size += SIGAR_CPU_INFO_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_cpu_infos_destroy(sigar_t *sigar, + sigar_cpu_infos_t *cpu_infos) +{ + if (cpu_infos->size) { + free(cpu_infos->data); + cpu_infos->number = cpu_infos->size = 0; + } + + return SIGAR_OK; +} + +int sigar_cpu_list_create(sigar_cpu_list_t *cpulist) +{ + cpulist->number = 0; + cpulist->size = SIGAR_CPU_INFO_MAX; + cpulist->data = malloc(sizeof(*(cpulist->data)) * + cpulist->size); + return SIGAR_OK; +} + +int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist) +{ + cpulist->data = realloc(cpulist->data, + sizeof(*(cpulist->data)) * + (cpulist->size + SIGAR_CPU_INFO_MAX)); + cpulist->size += SIGAR_CPU_INFO_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_cpu_list_destroy(sigar_t *sigar, + sigar_cpu_list_t *cpulist) +{ + if (cpulist->size) { + free(cpulist->data); + cpulist->number = cpulist->size = 0; + } + + return SIGAR_OK; +} + +int sigar_net_route_list_create(sigar_net_route_list_t *routelist) +{ + routelist->number = 0; + routelist->size = SIGAR_NET_ROUTE_LIST_MAX; + routelist->data = malloc(sizeof(*(routelist->data)) * + routelist->size); + return SIGAR_OK; +} + +int sigar_net_route_list_grow(sigar_net_route_list_t *routelist) +{ + routelist->data = + realloc(routelist->data, + sizeof(*(routelist->data)) * + (routelist->size + SIGAR_NET_ROUTE_LIST_MAX)); + routelist->size += SIGAR_NET_ROUTE_LIST_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_net_route_list_destroy(sigar_t *sigar, + sigar_net_route_list_t *routelist) +{ + if (routelist->size) { + free(routelist->data); + routelist->number = routelist->size = 0; + } + + return SIGAR_OK; +} + +int sigar_net_interface_list_create(sigar_net_interface_list_t *iflist) +{ + iflist->number = 0; + iflist->size = SIGAR_NET_IFLIST_MAX; + iflist->data = malloc(sizeof(*(iflist->data)) * + iflist->size); + return SIGAR_OK; +} + +int sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist) +{ + iflist->data = realloc(iflist->data, + sizeof(*(iflist->data)) * + (iflist->size + SIGAR_NET_IFLIST_MAX)); + iflist->size += SIGAR_NET_IFLIST_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) +sigar_net_interface_list_destroy(sigar_t *sigar, + sigar_net_interface_list_t *iflist) +{ + unsigned int i; + + if (iflist->size) { + for (i=0; inumber; i++) { + free(iflist->data[i]); + } + free(iflist->data); + iflist->number = iflist->size = 0; + } + + return SIGAR_OK; +} + +int sigar_net_connection_list_create(sigar_net_connection_list_t *connlist) +{ + connlist->number = 0; + connlist->size = SIGAR_NET_CONNLIST_MAX; + connlist->data = malloc(sizeof(*(connlist->data)) * + connlist->size); + return SIGAR_OK; +} + +int sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist) +{ + connlist->data = + realloc(connlist->data, + sizeof(*(connlist->data)) * + (connlist->size + SIGAR_NET_CONNLIST_MAX)); + connlist->size += SIGAR_NET_CONNLIST_MAX; + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) +sigar_net_connection_list_destroy(sigar_t *sigar, + sigar_net_connection_list_t *connlist) +{ + if (connlist->size) { + free(connlist->data); + connlist->number = connlist->size = 0; + } + + return SIGAR_OK; +} + +SIGAR_DECLARE(const char *)sigar_net_connection_type_get(int type) +{ + switch (type) { + case SIGAR_NETCONN_TCP: + return "tcp"; + case SIGAR_NETCONN_UDP: + return "udp"; + case SIGAR_NETCONN_RAW: + return "raw"; + case SIGAR_NETCONN_UNIX: + return "unix"; + default: + return "unknown"; + } +} + +void sigar_hwaddr_format(char *buff, unsigned char *ptr) +{ + sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", + (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377), + (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)); +} + +#if !defined(WIN32) && !defined(DARWIN) && !defined(__FreeBSD__) + +/* XXX: prolly will be moving these stuffs into os_net.c */ +#include +#include +#include +#include + +#ifndef SIOCGIFCONF +#include +#endif + +#if defined(_AIX) + +#include + +static void hwaddr_aix_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig) +{ + char *ent, *end; + struct ifreq *ifr; + + /* XXX: assumes sigar_net_interface_list_get has been called */ + end = sigar->ifconf_buf + sigar->ifconf_len; + + for (ent = sigar->ifconf_buf; + ent < end; + ent += sizeof(*ifr)) + { + ifr = (struct ifreq *)ent; + + if (ifr->ifr_addr.sa_family != AF_LINK) { + continue; + } + + if (strEQ(ifr->ifr_name, ifconfig->name)) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr; + + sigar_hwaddr_format(ifconfig->hwaddr, + (unsigned char *)LLADDR(sdl)); + return; + } + } + + sigar_hwaddr_set_null(ifconfig); +} + +#elif !defined(SIOCGIFHWADDR) + +#include + +static void hwaddr_arp_lookup(sigar_net_interface_config_t *ifconfig, int sock) +{ + struct arpreq areq; + struct sockaddr_in *sa; + + memset(&areq, 0, sizeof(areq)); + sa = (struct sockaddr_in *)&areq.arp_pa; + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = ifconfig->address; + + if (ioctl(sock, SIOCGARP, &areq) < 0) { + /* ho-hum */ + memset(&areq.arp_ha.sa_data, '\0', sizeof(areq.arp_ha.sa_data)); + } + + sigar_hwaddr_format(ifconfig->hwaddr, + (unsigned char *)areq.arp_ha.sa_data); +} + +#endif + +int sigar_net_interface_config_get(sigar_t *sigar, const char *name, + sigar_net_interface_config_t *ifconfig) +{ + int sock; + struct ifreq ifr; + + SIGAR_ZERO(ifconfig); + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return errno; + } + + SIGAR_SSTRCPY(ifconfig->name, name); + SIGAR_SSTRCPY(ifr.ifr_name, name); + +#define ifr_s_addr(ifr) \ + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr + + if (!ioctl(sock, SIOCGIFADDR, &ifr)) { + ifconfig->address = ifr_s_addr(ifr); + } + else { + /* if this one failed, so will everything else */ + close(sock); + return errno; + } + + if (!ioctl(sock, SIOCGIFNETMASK, &ifr)) { + ifconfig->netmask = ifr_s_addr(ifr); + } + + if (!ioctl(sock, SIOCGIFFLAGS, &ifr)) { + ifconfig->flags = ifr.ifr_flags; +#ifdef __linux__ + /* + * XXX: should just define SIGAR_IFF_* + * and test IFF_* bits on given platform. + * this is the only diff between solaris/hpux/linux + * for the flags we care about. + * + */ + if (ifconfig->flags & IFF_MULTICAST) { + ifconfig->flags |= SIGAR_IFF_MULTICAST; + } + else { + /* 0x800 == IFF_SLAVE on linux */ + ifconfig->flags &= ~SIGAR_IFF_MULTICAST; + } +#endif + } + else { + /* should always be able to get flags for existing device */ + /* other ioctls may fail if device is not enabled: ok */ + close(sock); + return errno; + } + + if (ifconfig->flags & IFF_LOOPBACK) { + ifconfig->destination = ifconfig->address; + ifconfig->broadcast = 0; + sigar_hwaddr_set_null(ifconfig); + } + else { + if (!ioctl(sock, SIOCGIFDSTADDR, &ifr)) { + ifconfig->destination = ifr_s_addr(ifr); + } + + if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) { + ifconfig->broadcast = ifr_s_addr(ifr); + } + +#if defined(SIOCGIFHWADDR) + if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) { + sigar_hwaddr_format(ifconfig->hwaddr, ifr.ifr_hwaddr.sa_data); + } +#elif defined(_AIX) + hwaddr_aix_lookup(sigar, ifconfig); +#else + hwaddr_arp_lookup(ifconfig, sock); +#endif + } + +#ifdef __linux__ + if (!ioctl(sock, SIOCGIFMTU, &ifr)) { + ifconfig->mtu = ifr.ifr_mtu; + } +#else + ifconfig->mtu = 0; /*XXX*/ +#endif + + if (!ioctl(sock, SIOCGIFMETRIC, &ifr)) { + ifconfig->metric = ifr.ifr_metric ? ifr.ifr_metric : 1; + } + + close(sock); + + return SIGAR_OK; +} + +#ifdef _AIX +# define MY_SIOCGIFCONF CSIOCGIFCONF +#else +# define MY_SIOCGIFCONF SIOCGIFCONF +#endif + +int sigar_net_interface_list_get(sigar_t *sigar, + sigar_net_interface_list_t *iflist) +{ + int n, lastlen=0; + struct ifreq *ifr; + struct ifconf ifc; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) { + return errno; + } + + for (;;) { + if (!sigar->ifconf_buf || lastlen) { + sigar->ifconf_len += sizeof(struct ifreq) * SIGAR_NET_IFLIST_MAX; + sigar->ifconf_buf = realloc(sigar->ifconf_buf, sigar->ifconf_len); + } + + ifc.ifc_len = sigar->ifconf_len; + ifc.ifc_buf = sigar->ifconf_buf; + + if (ioctl(sock, MY_SIOCGIFCONF, &ifc) < 0) { + /* EINVAL should mean num_interfaces > ifc.ifc_len */ + if ((errno != EINVAL) || + (lastlen == ifc.ifc_len)) + { + free(ifc.ifc_buf); + return errno; + } + } + + if (ifc.ifc_len < sigar->ifconf_len) { + break; /* got em all */ + } + + if (ifc.ifc_len != lastlen) { + /* might be more */ + lastlen = ifc.ifc_len; + continue; + } + + break; + } + + close(sock); + + iflist->number = 0; + iflist->size = ifc.ifc_len; + iflist->data = malloc(sizeof(*(iflist->data)) * + iflist->size); + + ifr = ifc.ifc_req; + for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq), ifr++) { +#ifdef _AIX + if (ifr->ifr_addr.sa_family != AF_LINK) { + /* XXX: dunno if this is right. + * otherwise end up with two 'en0' and three 'lo0' + * with the same ip address. + */ + continue; + } +#endif + iflist->data[iflist->number++] = strdup(ifr->ifr_name); + } + + return SIGAR_OK; +} + +#endif /* WIN32 */ + +#ifndef WIN32 +#include +#endif + +/* threadsafe alternative to inet_ntoa (inet_ntop4 from apr) */ +SIGAR_DECLARE(int) sigar_inet_ntoa(sigar_t *sigar, + sigar_uint64_t address, + char *addr_str) +{ + char *next=addr_str; + int n=0; + const unsigned char *src; + struct in_addr addr; + + addr.s_addr = address; + + src = (const unsigned char *)&addr.s_addr; + + do { + unsigned char u = *src++; + if (u > 99) { + *next++ = '0' + u/100; + u %= 100; + *next++ = '0' + u/10; + u %= 10; + } + else if (u > 9) { + *next++ = '0' + u/10; + u %= 10; + } + *next++ = '0' + u; + *next++ = '.'; + n++; + } while (n < 4); + + *--next = 0; + + return SIGAR_OK; +} + +static int fqdn_ip_get(sigar_t *sigar, char *name) +{ + int i, status; + sigar_net_interface_list_t iflist; + + if ((status = sigar_net_interface_list_get(sigar, &iflist)) != SIGAR_OK) { + return status; + } + + for (i=0; i +#endif + +#define H_ALIAS_MATCH(alias, name) \ + (strchr(alias, '.') && strnEQ(alias, name, strlen(name))) + +#define FQDN_SET(fqdn) \ + SIGAR_STRNCPY(name, fqdn, namelen) + +SIGAR_DECLARE(int) sigar_fqdn_get(sigar_t *sigar, char *name, int namelen) +{ + struct hostent *p; + char domain[SIGAR_FQDN_LEN + 1]; +#ifdef WIN32 + int status = sigar_wsa_init(sigar); + + if (status != SIGAR_OK) { + return status; + } +#endif + + if (gethostname(name, namelen - 1) != 0) { + return ENOENT; + } + + /* XXX use _r versions of these functions. */ + if (!(p = gethostbyname(name))) { + if (!strchr(name, '.')) { + fqdn_ip_get(sigar, name); + } + return SIGAR_OK; + } + + if (strchr(p->h_name, '.')) { + FQDN_SET(p->h_name); + return SIGAR_OK; + } + + if (p->h_aliases) { + int i; + + for (i=0; p->h_aliases[i]; i++) { + if (H_ALIAS_MATCH(p->h_aliases[i], p->h_name)) { + FQDN_SET(p->h_aliases[i]); + return SIGAR_OK; + } + } + } + + if (p->h_addr_list) { + int i,j; + + for (i=0; p->h_addr_list[i]; i++) { + struct hostent *q = + gethostbyaddr(p->h_addr_list[i], + p->h_length, + p->h_addrtype); + + if (strchr(q->h_name, '.')) { + FQDN_SET(q->h_name); + return SIGAR_OK; + } + else { + for (j=0; q->h_aliases[j]; j++) { + if (H_ALIAS_MATCH(q->h_aliases[j], q->h_name)) { + FQDN_SET(q->h_aliases[j]); + return SIGAR_OK; + } + } + } + } + } + +#ifndef WIN32 + if (!strchr(name, '.') && /* e.g. aix gethostname is already fqdn */ + (getdomainname(domain, sizeof(domain) - 1) == 0) && + (domain[0] != '\0') && + (domain[0] != '(')) /* linux default is "(none)" */ + { + /* sprintf(name, "%s.%s", name, domain); */ + char *ptr = name; + int len = strlen(name); + ptr += len; + *ptr++ = '.'; + namelen -= (len+1); + SIGAR_STRNCPY(ptr, domain, namelen); + } +#endif + + return SIGAR_OK; +} + +#ifndef MAX_STRING_LEN +#define MAX_STRING_LEN 8192 +#endif + +#ifdef WIN32 +/* The windows version of getPasswordNative was lifted from apr */ +SIGAR_DECLARE(char *) sigar_password_get(const char *prompt) +{ + static char password[MAX_STRING_LEN]; + int n = 0; + int ch; + + fputs(prompt, stderr); + fflush(stderr); + + while ((ch = _getch()) != '\r') { + if (ch == EOF) /* EOF */ { + return NULL; + } + else if (ch == 0 || ch == 0xE0) { + /* FN Keys (0 or E0) are a sentinal for a FN code */ + ch = (ch << 4) | _getch(); + /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ + if ((ch == 0xE53 || ch == 0xE4B || ch == 0x053 || ch == 0x04b) && n) { + password[--n] = '\0'; + fputs("\b \b", stderr); + fflush(stderr); + } + else { + fputc('\a', stderr); + fflush(stderr); + } + } + else if ((ch == '\b' || ch == 127) && n) /* BS/DEL */ { + password[--n] = '\0'; + fputs("\b \b", stderr); + fflush(stderr); + } + else if (ch == 3) /* CTRL+C */ { + /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ + fputs("^C\n", stderr); + fflush(stderr); + exit(-1); + } + else if (ch == 26) /* CTRL+Z */ { + fputs("^Z\n", stderr); + fflush(stderr); + return NULL; + } + else if (ch == 27) /* ESC */ { + fputc('\n', stderr); + fputs(prompt, stderr); + fflush(stderr); + n = 0; + } + else if ((n < sizeof(password) - 1) && !iscntrl(ch)) { + password[n++] = ch; + fputc(' ', stderr); + fflush(stderr); + } + else { + fputc('\a', stderr); + fflush(stderr); + } + } + + fputc('\n', stderr); + fflush(stderr); + password[n] = '\0'; + + return password; +} + +#else + +/* linux/hpux/solaris getpass() prototype lives here */ +#include + +#include + +/* from apr_getpass.c */ + +#if defined(SIGAR_HPUX) +# define getpass termios_getpass +#elif defined(SIGAR_SOLARIS) +# define getpass getpassphrase +#endif + +#ifdef SIGAR_HPUX +static char *termios_getpass(const char *prompt) +{ + struct termios attr; + static char password[MAX_STRING_LEN]; + unsigned int n=0; + + fputs(prompt, stderr); + fflush(stderr); + + if (tcgetattr(STDIN_FILENO, &attr) != 0) { + return NULL; + } + + attr.c_lflag &= ~(ECHO); + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) != 0) { + return NULL; + } + + while ((password[n] = getchar()) != '\n') { + if (n < (sizeof(password) - 1) && + (password[n] >= ' ') && + (password[n] <= '~')) + { + n++; + } + else { + fprintf(stderr, "\n"); + fputs(prompt, stderr); + fflush(stderr); + n = 0; + } + } + + password[n] = '\0'; + printf("\n"); + + if (n > (MAX_STRING_LEN - 1)) { + password[MAX_STRING_LEN - 1] = '\0'; + } + + attr.c_lflag |= ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &attr); + + return (char *)&password; +} +#endif + +SIGAR_DECLARE(char *) sigar_password_get(const char *prompt) +{ + char *buf = NULL; + + /* the linux version of getpass prints the prompt to the tty; ok. + * the solaris version prints the prompt to stderr; not ok. + * so print the prompt to /dev/tty ourselves if possible (always should be) + */ + + FILE *tty = NULL; + + if ((tty = fopen("/dev/tty", "w"))) { + fprintf(tty, "%s", prompt); + fflush(tty); + + buf = getpass(tty ? "" : prompt); + fclose(tty); + } + + return buf; +} + +#endif /* WIN32 */ diff --git a/src/sigar_fileinfo.c b/src/sigar_fileinfo.c new file mode 100644 index 00000000..177e1ae7 --- /dev/null +++ b/src/sigar_fileinfo.c @@ -0,0 +1,540 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +/* + * whittled down version of apr/file_info/{unix,win32}/filestat.c + * to fillin sigar_fileattrs_t + */ +#include "sigar_fileinfo.h" +#include "sigar_private.h" +#include "sigar_util.h" +#include "sigar_os.h" + +static const char* types[] = { + "none", + "regular", + "directory", + "character device", + "block device", + "pipe", + "symbolic link", + "socket", + "unknown" +}; + +SIGAR_DECLARE(const char *) +sigar_file_attrs_type_string_get(sigar_file_type_e type) +{ + if ((type < SIGAR_FILETYPE_NOFILE) || + (type > SIGAR_FILETYPE_UNKFILE)) + { + type = SIGAR_FILETYPE_UNKFILE; + } + + return types[type]; +} + +static const sigar_uint64_t perm_modes[] = { + SIGAR_UREAD, SIGAR_UWRITE, SIGAR_UEXECUTE, + SIGAR_GREAD, SIGAR_GWRITE, SIGAR_GEXECUTE, + SIGAR_WREAD, SIGAR_WWRITE, SIGAR_WEXECUTE +}; + +static const char perm_chars[] = "rwx"; + +SIGAR_DECLARE(char *) +sigar_file_attrs_permissions_string_get(sigar_uint64_t permissions, + char *str) +{ + char *ptr = str; + int i=0, j=0; + + for (i=0; i<9; i+=3) { + for (j=0; j<3; j++) { + if (permissions & perm_modes[i+j]) { + *ptr = perm_chars[j]; + } + else { + *ptr = '-'; + } + ptr++; + } + } + + *ptr = '\0'; + return str; +} + +static const int perm_int[] = { + 400, 200, 100, + 40, 20, 10, + 4, 2, 1 +}; + +SIGAR_DECLARE(int)sigar_file_attrs_mode_get(sigar_uint64_t permissions) +{ + int i=0; + int perms = 0; + + /* no doubt there is some fancy bitshifting + * to convert, but this works fine. + */ + for (i=0; i<9; i++) { + if (permissions & perm_modes[i]) { + perms += perm_int[i]; + } + } + + return perms; +} + +#define IS_DOTDIR(dir) \ + ((dir[0] == '.') && (!dir[1] || ((dir[1] == '.') && !dir[2]))) + +#ifdef WIN32 + +static void fillin_fileattrs(sigar_file_attrs_t *finfo, + WIN32_FILE_ATTRIBUTE_DATA *wininfo, + int linkinfo) +{ + DWORD *sizes = &wininfo->nFileSizeHigh; + + finfo->atime = FileTimeToTime(&wininfo->ftLastAccessTime); + finfo->ctime = FileTimeToTime(&wininfo->ftCreationTime); + finfo->mtime = FileTimeToTime(&wininfo->ftLastWriteTime); + + finfo->size = (sigar_uint64_t)sizes[1]; + if (finfo->size < 0 || sizes[0]) { + finfo->size = 0x7fffffff; + } + + if (linkinfo && + (wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + finfo->type = SIGAR_FILETYPE_LNK; + } + else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + finfo->type = SIGAR_FILETYPE_DIR; + } + else { + finfo->type = SIGAR_FILETYPE_REG; + } +} + +static int fileattrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs, + int linkinfo) +{ + WIN32_FILE_ATTRIBUTE_DATA attrs; + + SIGAR_ZERO(fileattrs); + + if (!GetFileAttributesExA(file, + GetFileExInfoStandard, + &attrs)) + { + return GetLastError(); + } + + fillin_fileattrs(fileattrs, &attrs, linkinfo); + + return SIGAR_OK; +} + +SIGAR_DECLARE(int) sigar_file_attrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs) +{ + return fileattrs_get(sigar, file, fileattrs, 0); +} + +SIGAR_DECLARE(int) sigar_link_attrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs) +{ + return fileattrs_get(sigar, file, fileattrs, 1); +} + +static __inline int file_type(char *file) +{ + WIN32_FILE_ATTRIBUTE_DATA attrs; + + if (!GetFileAttributesExA(file, + GetFileExInfoStandard, + &attrs)) + { + return -1; + } + + if (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + return SIGAR_FILETYPE_LNK; + } + else if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + return SIGAR_FILETYPE_DIR; + } + else { + return SIGAR_FILETYPE_REG; + } +} + +SIGAR_DECLARE(int) sigar_dir_stat_get(sigar_t *sigar, + const char *dir, + sigar_dir_stat_t *dirstats) +{ + char name[SIGAR_PATH_MAX+1]; + int len = strlen(dir); + int max = sizeof(name)-len-1; + char *ptr = name; + WIN32_FIND_DATA data; + HANDLE handle; + DWORD error; + char delim; + + SIGAR_ZERO(dirstats); + if (file_type((char *)dir) != SIGAR_FILETYPE_DIR) { + return ERROR_NO_MORE_FILES; + } + + strncpy(name, dir, sizeof(name)); + ptr += len; + if (strchr(dir, '/')) { + delim = '/'; + } + else { + delim = '\\'; + } + if (name[len] != delim) { + *ptr++ = delim; + len++; + max--; + } + + /* e.g. "C:\sigar\*" */ + name[len] = '*'; + name[len+1] = '\0'; + + handle = FindFirstFile(name, &data); + if (handle == INVALID_HANDLE_VALUE) { + return GetLastError(); + } + + while (FindNextFile(handle, &data)) { + /* skip '.' and '..' */ + if (IS_DOTDIR(data.cFileName)) { + continue; + } + + /* e.g. "C:\sigar\lib" */ + strncpy(ptr, data.cFileName, max); + ptr[max] = '\0'; + + switch (file_type(name)) { + case -1: + break; + case SIGAR_FILETYPE_REG: + ++dirstats->files; + break; + case SIGAR_FILETYPE_DIR: + ++dirstats->subdirs; + break; + case SIGAR_FILETYPE_LNK: + ++dirstats->symlinks; + break; + case SIGAR_FILETYPE_CHR: + ++dirstats->chrdevs; + break; + case SIGAR_FILETYPE_BLK: + ++dirstats->blkdevs; + break; + case SIGAR_FILETYPE_SOCK: + ++dirstats->sockets; + break; + default: + ++dirstats->total; + } + } + + error = GetLastError(); + + FindClose(handle); + + if (error != ERROR_NO_MORE_FILES) { + return error; + } + + dirstats->total += + dirstats->files + + dirstats->subdirs + + dirstats->symlinks + + dirstats->chrdevs + + dirstats->blkdevs + + dirstats->sockets; + + return SIGAR_OK; +} + +#else + +#include +#include +#include +#include + +static sigar_file_type_e filetype_from_mode(mode_t mode) +{ + sigar_file_type_e type; + + switch (mode & S_IFMT) { + case S_IFREG: + type = SIGAR_FILETYPE_REG; break; + case S_IFDIR: + type = SIGAR_FILETYPE_DIR; break; + case S_IFLNK: + type = SIGAR_FILETYPE_LNK; break; + case S_IFCHR: + type = SIGAR_FILETYPE_CHR; break; + case S_IFBLK: + type = SIGAR_FILETYPE_BLK; break; +#if defined(S_IFFIFO) + case S_IFFIFO: + type = SIGAR_FILETYPE_PIPE; break; +#endif +#if !defined(BEOS) && defined(S_IFSOCK) + case S_IFSOCK: + type = SIGAR_FILETYPE_SOCK; break; +#endif + + default: + /* Work around missing S_IFxxx values above + * for Linux et al. + */ +#if !defined(S_IFFIFO) && defined(S_ISFIFO) + if (S_ISFIFO(mode)) { + type = SIGAR_FILETYPE_PIPE; + } else +#endif +#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK) + if (S_ISSOCK(mode)) { + type = SIGAR_FILETYPE_SOCK; + } else +#endif + type = SIGAR_FILETYPE_UNKFILE; + } + return type; +} + +static sigar_uint64_t sigar_unix_mode2perms(mode_t mode) +{ + sigar_uint64_t perms = 0; + + if (mode & S_IRUSR) + perms |= SIGAR_UREAD; + if (mode & S_IWUSR) + perms |= SIGAR_UWRITE; + if (mode & S_IXUSR) + perms |= SIGAR_UEXECUTE; + + if (mode & S_IRGRP) + perms |= SIGAR_GREAD; + if (mode & S_IWGRP) + perms |= SIGAR_GWRITE; + if (mode & S_IXGRP) + perms |= SIGAR_GEXECUTE; + + if (mode & S_IROTH) + perms |= SIGAR_WREAD; + if (mode & S_IWOTH) + perms |= SIGAR_WWRITE; + if (mode & S_IXOTH) + perms |= SIGAR_WEXECUTE; + + return perms; +} + +static SIGAR_INLINE void copy_stat_info(sigar_file_attrs_t *fileattrs, + struct stat *info) +{ + fileattrs->permissions = sigar_unix_mode2perms(info->st_mode); + fileattrs->type = filetype_from_mode(info->st_mode); + fileattrs->uid = info->st_uid; + fileattrs->gid = info->st_gid; + fileattrs->size = info->st_size; + fileattrs->inode = info->st_ino; + fileattrs->device = info->st_dev; + fileattrs->nlink = info->st_nlink; + fileattrs->atime = info->st_atime; + fileattrs->mtime = info->st_mtime; + fileattrs->ctime = info->st_ctime; + fileattrs->atime *= 1000; + fileattrs->mtime *= 1000; + fileattrs->ctime *= 1000; +} + +int sigar_file_attrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs) +{ + struct stat info; + + if (stat(file, &info) == 0) { + copy_stat_info(fileattrs, &info); + return SIGAR_OK; + } + else { + return errno; + } +} + +int sigar_link_attrs_get(sigar_t *sigar, + const char *file, + sigar_file_attrs_t *fileattrs) +{ + struct stat info; + + if (lstat(file, &info) == 0) { + copy_stat_info(fileattrs, &info); + return SIGAR_OK; + } + else { + return errno; + } +} + +int sigar_dir_stat_get(sigar_t *sigar, + const char *dir, + sigar_dir_stat_t *dirstats) +{ + char name[SIGAR_PATH_MAX+1]; + int len = strlen(dir); + int max = sizeof(name)-len-1; + char *ptr = name; + DIR *dirp = opendir(dir); + struct dirent *ent; + struct stat info; +#ifdef HAVE_READDIR_R + struct dirent dbuf; +#endif + + if (!dirp) { + return errno; + } + + SIGAR_ZERO(dirstats); + + strncpy(name, dir, sizeof(name)); + ptr += len; + if (name[len] != '/') { + *ptr++ = '/'; + len++; + max--; + } + +#ifdef HAVE_READDIR_R + while (readdir_r(dirp, &dbuf, &ent) == 0) { + if (ent == NULL) { + break; + } +#else + while ((ent = readdir(dirp))) { +#endif + /* skip '.' and '..' */ + if (IS_DOTDIR(ent->d_name)) { + continue; + } + + strncpy(ptr, ent->d_name, max); + ptr[max] = '\0'; + + if (lstat(name, &info) != 0) { + continue; + } + + switch (filetype_from_mode(info.st_mode)) { + case SIGAR_FILETYPE_REG: + ++dirstats->files; + break; + case SIGAR_FILETYPE_DIR: + ++dirstats->subdirs; + break; + case SIGAR_FILETYPE_LNK: + ++dirstats->symlinks; + break; + case SIGAR_FILETYPE_CHR: + ++dirstats->chrdevs; + break; + case SIGAR_FILETYPE_BLK: + ++dirstats->blkdevs; + break; + case SIGAR_FILETYPE_SOCK: + ++dirstats->sockets; + break; + default: + ++dirstats->total; + } + } + + dirstats->total += + dirstats->files + + dirstats->subdirs + + dirstats->symlinks + + dirstats->chrdevs + + dirstats->blkdevs + + dirstats->sockets; + + closedir(dirp); + + return SIGAR_OK; +} + +#endif diff --git a/src/sigar_getline.c b/src/sigar_getline.c new file mode 100644 index 00000000..1e004891 --- /dev/null +++ b/src/sigar_getline.c @@ -0,0 +1,1836 @@ +/* + * Copyright (C) 1991, 1992 by Chris Thewalt (thewalt@ce.berkeley.edu) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted, provided + * that the above copyright notices appear in all copies and that both the + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + */ +/* +*************************** Motivation ********************************** + +Many interactive programs read input line by line, but would like to +provide line editing and history functionality to the end-user that +runs the program. + +The input-edit package provides that functionality. As far as the +programmer is concerned, the program only asks for the next line +of input. However, until the user presses the RETURN key they can use +emacs-style line editing commands and can traverse the history of lines +previously typed. + +Other packages, such as GNU's readline, have greater capability but are +also substantially larger. Input-edit is small, since it uses neither +stdio nor any termcap features, and is also quite portable. It only uses +\b to backspace and \007 to ring the bell on errors. Since it cannot +edit multiple lines it scrolls long lines left and right on the same line. + +Input edit uses classic (not ANSI) C, and should run on any Unix +system (BSD or SYSV), PC's with the MSC compiler, or Vax/VMS (untested by me). +Porting the package to new systems basicaly requires code to read a +character when it is typed without echoing it, everything else should be OK. + +I have run the package on: + + DECstation 5000, Ultrix 4.2 with cc and gcc + Sun Sparc 2, SunOS 4.1.1, with cc + SGI Iris, IRIX System V.3, with cc + PC, DRDOS 5.0, with MSC 6.0 + +The description below is broken into two parts, the end-user (editing) +interface and the programmer interface. Send bug reports, fixes and +enhancements to: + +Chris Thewalt (thewalt@ce.berkeley.edu) +2/4/92 + +PS: I don't have, and don't want to add, a vi mode, sorry. + +************************** End-User Interface *************************** + +Entering printable keys generally inserts new text into the buffer (unless +in overwrite mode, see below). Other special keys can be used to modify +the text in the buffer. In the description of the keys below, ^n means +Control-n, or holding the CONTROL key down while pressing "n". M-B means +Meta-B (or Alt-B). Errors will ring the terminal bell. + +^A/^E : Move cursor to beginning/end of the line. +^F/^B : Move cursor forward/backward one character. +^D : Delete the character under the cursor. +^H, DEL : Delete the character to the left of the cursor. +^K : Kill from the cursor to the end of line. +^L : Redraw current line. +^O : Toggle overwrite/insert mode. Initially in insert mode. Text + added in overwrite mode (including yanks) overwrite + existing text, while insert mode does not overwrite. +^P/^N : Move to previous/next item on history list. +^R/^S : Perform incremental reverse/forward search for string on + the history list. Typing normal characters adds to the current + search string and searches for a match. Typing ^R/^S marks + the start of a new search, and moves on to the next match. + Typing ^H or DEL deletes the last character from the search + string, and searches from the starting location of the last search. + Therefore, repeated DEL's appear to unwind to the match nearest + the point at which the last ^R or ^S was typed. If DEL is + repeated until the search string is empty the search location + begins from the start of the history list. Typing ESC or + any other editing character accepts the current match and + loads it into the buffer, terminating the search. +^T : Toggle the characters under and to the left of the cursor. +^U : Kill from beginning to the end of the line. +^Y : Yank previously killed text back at current location. Note that + this will overwrite or insert, depending on the current mode. +M-F/M-B : Move cursor forward/backward one word. +M-D : Delete the word under the cursor. +^SPC : Set mark. +^W : Kill from mark to point. +^X : Exchange mark and point. +TAB : By default adds spaces to buffer to get to next TAB stop + (just after every 8th column), although this may be rebound by the + programmer, as described below. +NL, CR : returns current buffer to the program. + +DOS and ANSI terminal arrow key sequences are recognized, and act like: + + up : same as ^P + down : same as ^N + left : same as ^B + right : same as ^F + +************************** Programmer Interface *************************** + +The programmer accesses input-edit through five functions, and optionally +through three additional function pointer hooks. The five functions are: + +char *Getline(char *prompt) + + Prints the prompt and allows the user to edit the current line. A + pointer to the line is returned when the user finishes by + typing a newline or a return. Unlike GNU readline, the returned + pointer points to a static buffer, so it should not be free'd, and + the buffer contains the newline character. The user enters an + end-of-file by typing ^D on an empty line, in which case the + first character of the returned buffer is '\0'. Getline never + returns a NULL pointer. The getline function sets terminal modes + needed to make it work, and resets them before returning to the + caller. The getline function also looks for characters that would + generate a signal, and resets the terminal modes before raising the + signal condition. If the signal handler returns to getline, + the screen is automatically redrawn and editing can continue. + Getline now requires both the input and output stream be connected + to the terminal (not redirected) so the main program should check + to make sure this is true. If input or output have been redirected + the main program should use buffered IO (stdio) rather than + the slow 1 character read()s that getline uses (note: this limitation + has been removed). + +char *Getlinem(int mode, char *prompt) + + mode: -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup + + More specialized version of the previous function. Depending on + the mode, it behaves differently. Its main use is to allow + character by character input from the input stream (useful when + in an X eventloop). It will return NULL as long as no newline + has been received. Its use is typically as follows: + 1) In the program initialization part one calls: Getlinem(-1,"prompt>") + 2) In the X inputhandler: if ((line = Getlinem(1,NULL))) { + 3) In the termination routine: Getlinem(2,NULL) + With mode=0 the function behaves exactly like the previous function. + +void Gl_config(const char *which, int value) + + Set some config options. Which can be: + "noecho": do not echo characters (used for passwd input) + "erase": do erase line after return (used for text scrollers) + +void Gl_setwidth(int width) + + Set the width of the terminal to the specified width. The default + width is 80 characters, so this function need only be called if the + width of the terminal is not 80. Since horizontal scrolling is + controlled by this parameter it is important to get it right. + +void Gl_histinit(char *file) + + This function reads a history list from file. So lines from a + previous session can be used again. + +void Gl_histadd(char *buf) + + The Gl_histadd function checks to see if the buf is not empty or + whitespace, and also checks to make sure it is different than + the last saved buffer to avoid repeats on the history list. + If the buf is a new non-blank string a copy is made and saved on + the history list, so the caller can re-use the specified buf. + +The main loop in testgl.c, included in this directory, shows how the +input-edit package can be used: + +extern char *Getline(); +extern void Gl_histadd(); +main() +{ + char *p; + Gl_histinit(".hist"); + do { + p = Getline("PROMPT>>>> "); + Gl_histadd(p); + fputs(p, stdout); + } while (*p != 0); +} + +In order to allow the main program to have additional access to the buffer, +to implement things such as completion or auto-indent modes, three +function pointers can be bound to user functions to modify the buffer as +described below. By default gl_in_hook and gl_out_hook are set to NULL, +and gl_tab_hook is bound to a function that inserts spaces until the next +logical tab stop is reached. The user can reassign any of these pointers +to other functions. Each of the functions bound to these hooks receives +the current buffer as the first argument, and must return the location of +the leftmost change made in the buffer. If the buffer isn't modified the +functions should return -1. When the hook function returns the screen is +updated to reflect any changes made by the user function. + +int (*gl_in_hook)(char *buf) + + If gl_in_hook is non-NULL the function is called each time a new + buffer is loaded. It is called when getline is entered, with an + empty buffer, it is called each time a new buffer is loaded from + the history with ^P or ^N, and it is called when an incremental + search string is accepted (when the search is terminated). The + buffer can be modified and will be redrawn upon return to Getline(). + +int (*gl_out_hook)(char *buf) + + If gl_out_hook is non-NULL it is called when a line has been + completed by the user entering a newline or return. The buffer + handed to the hook does not yet have the newline appended. If the + buffer is modified the screen is redrawn before getline returns the + buffer to the caller. + +int (*gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc) + + If gl_tab_hook is non-NULL, it is called whenever a tab is typed. + In addition to receiving the buffer, the current prompt width is + given (needed to do tabbing right) and a pointer to the cursor + offset is given, where a 0 offset means the first character in the + line. Not only does the cursor_loc tell the programmer where the + TAB was received, but it can be reset so that the cursor will end + up at the specified location after the screen is redrawn. +*/ + +/* forward reference needed for gl_tab_hook */ +static int gl_tab(char *buf, int offset, int *loc); + +/********************* exported interface ********************************/ + +static int (*gl_in_hook)(char *buf) = 0; +static int (*gl_out_hook)(char *buf) = 0; +static int (*gl_tab_hook)(char *buf, int prompt_width, int *loc) = gl_tab; + +/******************** imported interface *********************************/ + +#include "sigar_getline.h" +#include "sigar_private.h" +#include "sigar_util.h" +#include +#include +#include +#include +#include +#include + +/******************** internal interface *********************************/ + +static char *sigar_getlinem(int mode, char *prompt); /* allows reading char by char */ + +static void sigar_getline_config(const char *which, int value); /* set some options */ + +static void sigar_getline_clear_screen(void); + +#define BUF_SIZE 1024 + +static int gl_init_done = -1; /* terminal mode flag */ +static int gl_notty = 0; /* 1 when not a tty */ +static int gl_eof = 0; /* 1 when not a tty and read() == -1 */ +static int gl_termw = 80; /* actual terminal width */ +static int gl_scroll = 27; /* width of EOL scrolling region */ +static int gl_width = 0; /* net size available for input */ +static int gl_extent = 0; /* how far to redraw, 0 means all */ +static int gl_overwrite = 0; /* overwrite mode */ +static int gl_no_echo = 0; /* do not echo input characters */ +static int gl_passwd = 0; /* do not echo input characters */ +static int gl_erase_line = 0; /* erase line before returning */ +static int gl_pos, gl_cnt = 0; /* position and size of input */ +static char gl_buf[BUF_SIZE]; /* input buffer */ +static char gl_killbuf[BUF_SIZE]=""; /* killed text */ +static char *gl_prompt; /* to save the prompt string */ +static char gl_intrc = 0; /* keyboard SIGINT char */ +static char gl_quitc = 0; /* keyboard SIGQUIT char */ +static char gl_suspc = 0; /* keyboard SIGTSTP char */ +static char gl_dsuspc = 0; /* delayed SIGTSTP char */ +static int gl_search_mode = 0; /* search mode flag */ +static int gl_savehist = 0; /* # of lines to save in hist file */ +static char gl_histfile[256]; /* name of history file */ + +static void gl_init(); /* prepare to edit a line */ +static void gl_cleanup(); /* to undo gl_init */ +static void gl_char_init(); /* get ready for no echo input */ +static void gl_char_cleanup(); /* undo gl_char_init */ + +static void gl_addchar(int c); /* install specified char */ +static void gl_del(int loc); /* del, either left (-1) or cur (0) */ +static void gl_error(char *buf); /* write error msg and die */ +static void gl_fixup(char *p, int c, int cur); /* fixup state variables and screen */ +static int gl_getc(); /* read one char from terminal */ +static void gl_kill(); /* delete to EOL */ +static void gl_newline(); /* handle \n or \r */ +static void gl_putc(int c); /* write one char to terminal */ +static void gl_puts(char *buf); /* write a line to terminal */ +static void gl_transpose(); /* transpose two chars */ +static void gl_yank(); /* yank killed text */ + +static int is_whitespace(char c); /* "whitespace" very loosely interpreted */ +static void gl_back_1_word(); /* move cursor back one word */ +static void gl_kill_1_word(); /* kill to end of word */ +static void gl_kill_region(int i, int j); /* kills from i to j */ +static void gl_fwd_1_word(); /* move cursor forward one word */ +static void gl_set_mark(); /* sets mark to be at point */ +static void gl_exch(); /* exchanges point and mark */ +static void gl_wipe(); /* kills from mark to point */ +static int gl_mark = -1; /* position of mark. gl_mark<0 if not set */ + +static void hist_init(); /* initializes hist pointers */ +static char *hist_next(); /* return ptr to next item */ +static char *hist_prev(); /* return ptr to prev item */ +static char *hist_save(char *p); /* makes copy of a string, without NL */ + +static void search_addchar(int c); /* increment search string */ +static void search_term(); /* reset with current contents */ +static void search_back(int s); /* look back for current string */ +static void search_forw(int s); /* look forw for current string */ + +/************************ nonportable part *********************************/ + +#ifdef MSDOS +#include +#endif + +#ifdef WIN32 +# define MSDOS +# include +# include +#endif /* WIN32 */ + +#ifdef __MWERKS__ +#define R__MWERKS +#endif + +#ifdef R__MWERKS +# include +#endif + +#if defined(_AIX) || defined(__Lynx__) || defined(__APPLE__) +#define unix +#endif + +#if defined(__hpux) || defined(__osf__) /* W.Karig@gsi.de */ +#ifndef unix +#define unix +#endif +#endif + +#ifdef unix +#include +#if !defined(__osf__) && !defined(_AIX) /* W.Karig@gsi.de */ +#include +#endif + +#if defined(__linux__) && defined(__powerpc__) +# define R__MKLINUX // = linux on PowerMac +#endif +#if defined(__linux__) && defined(__alpha__) +# define R__ALPHALINUX // = linux on Alpha +#endif + +#if defined(TIOCGETP) && !defined(__sgi) && !defined(R__MKLINUX) && \ + !defined(R__ALPHALINUX) /* use BSD interface if possible */ +#include +static struct sgttyb new_tty, old_tty; +static struct tchars tch; +static struct ltchars ltch; +#else +#ifdef SIGTSTP /* need POSIX interface to handle SUSP */ +#include +#if defined(__sun) || defined(__sgi) || defined(R__MKLINUX) || \ + defined(R__ALPHALINUX) +#undef TIOCGETP /* Solaris and SGI define TIOCGETP in */ +#undef TIOCSETP +#endif +static struct termios new_termios, old_termios; +#else /* use SYSV interface */ +#include +static struct termio new_termio, old_termio; +#endif +#endif +#endif /* unix */ + +#ifdef VMS +#include +#include +#include +#include +#include +#include unixio + +static int setbuff[2]; /* buffer to set terminal attributes */ +static short chan = -1; /* channel to terminal */ +struct dsc$descriptor_s descrip; /* VMS descriptor */ +#endif + +static void +sigar_getline_config(const char *which, int value) +{ + if (strcmp(which, "noecho") == 0) + gl_no_echo = value; + else if (strcmp(which, "erase") == 0) + gl_erase_line = value; + else + printf("gl_config: %s ?\n", which); +} + +static void +gl_char_init() /* turn off input echo */ +{ + if (gl_notty) return; +#ifdef unix +#ifdef TIOCGETP /* BSD */ + ioctl(0, TIOCGETC, &tch); + ioctl(0, TIOCGLTC, <ch); + gl_intrc = tch.t_intrc; + gl_quitc = tch.t_quitc; + gl_suspc = ltch.t_suspc; + gl_dsuspc = ltch.t_dsuspc; + ioctl(0, TIOCGETP, &old_tty); + new_tty = old_tty; + new_tty.sg_flags |= RAW; + new_tty.sg_flags &= ~ECHO; + ioctl(0, TIOCSETP, &new_tty); +#else +#ifdef SIGTSTP /* POSIX */ + tcgetattr(0, &old_termios); + gl_intrc = old_termios.c_cc[VINTR]; + gl_quitc = old_termios.c_cc[VQUIT]; +#ifdef VSUSP + gl_suspc = old_termios.c_cc[VSUSP]; +#endif +#ifdef VDSUSP + gl_dsuspc = old_termios.c_cc[VDSUSP]; +#endif + new_termios = old_termios; + new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF); + new_termios.c_iflag |= (IGNBRK|IGNPAR); + new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &new_termios); +#else /* SYSV */ + ioctl(0, TCGETA, &old_termio); + gl_intrc = old_termio.c_cc[VINTR]; + gl_quitc = old_termio.c_cc[VQUIT]; + new_termio = old_termio; + new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF); + new_termio.c_iflag |= (IGNBRK|IGNPAR); + new_termio.c_lflag &= ~(ICANON|ISIG|ECHO); + new_termio.c_cc[VMIN] = 1; + new_termio.c_cc[VTIME] = 0; + ioctl(0, TCSETA, &new_termio); +#endif +#endif +#endif /* unix */ + +#ifdef MSDOS + gl_intrc = 'C' - '@'; + gl_quitc = 'Q' - '@'; +// gl_suspc = ltch.t_suspc; +#endif /* MSDOS */ + +#ifdef R__MWERKS + gl_intrc = 'C' - '@'; + gl_quitc = 'Q' - '@'; +#endif + +#ifdef vms + descrip.dsc$w_length = strlen("tt:"); + descrip.dsc$b_dtype = DSC$K_DTYPE_T; + descrip.dsc$b_class = DSC$K_CLASS_S; + descrip.dsc$a_pointer = "tt:"; + (void)sys$assign(&descrip,&chan,0,0); + (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0); + setbuff[1] |= TT$M_NOECHO; + (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0); +#endif /* vms */ +} + +static void +gl_char_cleanup() /* undo effects of gl_char_init */ +{ + if (gl_notty) return; +#ifdef unix +#ifdef TIOCSETP /* BSD */ + ioctl(0, TIOCSETP, &old_tty); +#else +#ifdef SIGTSTP /* POSIX */ + tcsetattr(0, TCSANOW, &old_termios); +#else /* SYSV */ + ioctl(0, TCSETA, &old_termio); +#endif +#endif +#endif /* unix */ + +#ifdef vms + setbuff[1] &= ~TT$M_NOECHO; + (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0); + sys$dassgn(chan); + chan = -1; +#endif +} + +#if defined(MSDOS) && !defined(WIN32) +// +DECK, PAUSE, T=XCC, IF=WINNT. (from KERNDOS.CAR ) +# include + int pause_() + { + int first_char; + first_char = _getch(); + if (first_char == 0 || first_char == 0xE0) first_char = -_getch(); + return first_char; + } +#endif + +#if defined(MSDOS) && defined(WIN32) +//______________________________________________________________________________ +int pause_() +{ + static HANDLE hConsoleInput = NULL; + static iCharCount = 0; + static int chLastChar = 0; + + DWORD cRead; + + INPUT_RECORD pirBuffer; + KEY_EVENT_RECORD *KeyEvent= (KEY_EVENT_RECORD *)&(pirBuffer.Event); + + if (!hConsoleInput) hConsoleInput = GetStdHandle(STD_INPUT_HANDLE); + + if (iCharCount) iCharCount--; // Whether several symbols had been read + else { + chLastChar = 0; + while (chLastChar == 0) { + if (!ReadConsoleInput(hConsoleInput, // handle of a console input buffer + &pirBuffer, // address of the buffer for read data + 1, // number of records to read + &cRead // address of number of records read + )) return 0; + + if (pirBuffer.EventType == KEY_EVENT && KeyEvent->bKeyDown == TRUE){ + iCharCount = KeyEvent->wRepeatCount - 1; + chLastChar = ((int) (KeyEvent->uChar).AsciiChar & 0xffff); + if (chLastChar) + OemToCharBuff((char const *)&chLastChar,(char *)&chLastChar,1); + else + chLastChar = - (KeyEvent->wVirtualScanCode); +// chLastChar = - (KeyEvent->wVirtualKeyCode); + } + } + } + return chLastChar; + +} +#endif + +static int +gl_getc() +/* get a character without echoing it to screen */ +{ +#ifdef MSDOS +# define k_ctrl_C 3 +# define k_ctrl_Z 26 +# define k_ctrl_Q 17 +# define k_ctrl_K 11 +# define k_rt_arr -77 +# define k_lt_arr -75 +# define k_up_arr -72 +# define k_dn_arr -80 +# define k_PGUP -73 +# define k_PGDW -81 +# define k_HOME -71 +# define k_END -79 +# define k_INS -82 +# define k_DEL -83 +# define k_ENTER 13 +# define k_CR 13 +# define k_BS 8 +# define k_ESC 27 +# define k_alt_H -35 +# define k_beep 7 +# ifndef WIN32 + int get_cursor__(int *,int *); + int display_off__(int *); + int display_on__(); + int locate_(int *,int *); + int ixc, iyc; +# endif + int pause_(); +#endif + + int c; + +#if defined(unix) + unsigned char ch; + while ((c = (read(0, &ch, 1) > 0) ? ch : -1) == -1 && errno == EINTR) + errno = 0; +#endif + +#if defined(R__MWERKS) + c = getchar(); +#endif + +#ifdef MSDOS + c = pause_(); + if (c < 0) { + switch (c) { + case k_up_arr: c = 'P' - '@'; /* up -> ^P = 16 */ + break; + case k_dn_arr: c = 'N' - '@'; /* down -> ^N = 14 */ + break; + case k_lt_arr: c = 'B' - '@'; /* left -> ^B =2 */ + break; + case k_rt_arr: c = 'F' - '@'; /* right -> ^F = 6*/ + break; + case k_INS: c = 'O' - '@'; /* right -> ^O = 15*/ + break; + case k_DEL: c = 'D' - '@'; /* Delete character under cursor = 4*/ + break; + case k_END: c = 'E' - '@'; /* Moves cursor to end of line * = 5 */ + break; + case k_HOME: c = 'A' - '@'; /* Moves cursor to beginning of line = 1*/ + break; + default: c = 0; /* make it garbage */ + } + } + else { + switch(c) { + case k_ESC: c = 'U' - '@'; /* Clear full line -> ^U */ + break; + default: + break; + } + } +#endif + +#ifdef vms + if(chan < 0) { + c='\0'; + } + (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0); + c &= 0177; /* get a char */ +#endif + return c; +} + +static void +gl_putc(int c) +{ + char ch = c; + + if (gl_notty) return; + + if ( !gl_passwd || !isgraph(c)) + { +#ifdef WIN32 + CharToOemBuff((char const *)&c,&ch,1); +#endif + + write(1, &ch, 1); + } +#if defined(unix) || defined(MSDOS) || defined(WIN32) || defined(R__MWERKS) +#ifdef TIOCSETP /* BSD in RAW mode, map NL to NL,CR */ + if (ch == '\n') { + ch = '\r'; + write(1, &ch, 1); + } +#endif +#endif +} + +/******************** fairly portable part *********************************/ + +static void +gl_puts(char *buf) +{ + int len = strlen(buf); + + if (gl_notty) return; +#ifdef WIN32 + { + char *OemBuf = (char *)malloc(2*len); + CharToOemBuff(buf,OemBuf,len); + write(1, OemBuf, len); + free(OemBuf); + } +#else + write(1, buf, len); +#endif +} + +static void +gl_error(char *buf) +{ + int len = strlen(buf); + + gl_cleanup(); +#ifdef WIN32 + { + char *OemBuf = (char *)malloc(2*len); + CharToOemBuff(buf,OemBuf,len); + write(2, OemBuf, len); + free(OemBuf); + } +#else + write(2, buf, len); +#endif + exit(1); +} + +static void +gl_init() +/* set up variables and terminal */ +{ + if (gl_init_done < 0) { /* -1 only on startup */ + hist_init(); + } + if (isatty(0) == 0 || isatty(1) == 0) + gl_notty = 1; + gl_char_init(); + gl_init_done = 1; +} + +static void +gl_cleanup() +/* undo effects of gl_init, as necessary */ +{ + if (gl_init_done > 0) + gl_char_cleanup(); + gl_init_done = 0; +} + +SIGAR_DECLARE(void) +sigar_getline_setwidth(int w) +{ + if (w > 20) { + gl_termw = w; + gl_scroll = w / 3; + } else { + gl_error("\n*** Error: minimum screen width is 21\n"); + } +} + +SIGAR_DECLARE(void) +sigar_getline_windowchanged() +{ +#ifdef TIOCGWINSZ + if (isatty(0)) { + static char lenv[32], cenv[32]; + struct winsize wins; + ioctl(0, TIOCGWINSZ, &wins); + + if (wins.ws_col == 0) wins.ws_col = 80; + if (wins.ws_row == 0) wins.ws_row = 24; + + sigar_getline_setwidth(wins.ws_col); + + sprintf(lenv, "LINES=%d", wins.ws_row); + putenv(lenv); + sprintf(cenv, "COLUMNS=%d", wins.ws_col); + putenv(cenv); + } +#endif +} + +/* -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup */ + +static char * +sigar_getlinem(int mode, char *prompt) +{ + int c, loc, tmp; + int sig; + + if (mode == 2) { + gl_cleanup(); + return NULL; + } + + if (mode < 1) { + if (mode == -1) { + sigar_getline_config("noecho", 0); + sigar_getline_config("erase", 0); + } + gl_init(); + gl_prompt = (prompt)? prompt : (char*)""; + gl_buf[0] = 0; + if (gl_in_hook) + gl_in_hook(gl_buf); + gl_fixup(gl_prompt, -2, BUF_SIZE); + if (mode == -1) return NULL; + } + while ((c = gl_getc()) >= 0) { + gl_extent = 0; /* reset to full extent */ +#ifndef WIN32 + if (isprint(c)) { +#else + if (c >= ' ') { +#endif + if (gl_search_mode) + search_addchar(c); + else + gl_addchar(c); + } else { + if (gl_search_mode) { + if (c == '\033' || c == '\016' || c == '\020') { + search_term(); + c = 0; /* ignore the character */ + } else if (c == '\010' || c == '\177') { + search_addchar(-1); /* unwind search string */ + c = 0; + } else if (c != '\022' && c != '\023') { + search_term(); /* terminate and handle char */ + } + } + /* NOTE: + * sometimes M-x turns on bit 8 ( M-x --> 'x' + 128 ) + * sometimes M-x prepends an escape character ( M-x --> '\033','x' ) + * both cases are handled ... + */ + switch (c) + { + case 'b'+128: /* M-b */ + case 'B'+128: /* M-B */ + gl_back_1_word(); + break; + case 'd'+128: /* M-d */ + case 'D'+128: /* M-D */ + gl_kill_1_word(); + break; + case 'f'+128: /* M-f */ + case 'F'+128: /* M-F */ + gl_fwd_1_word(); + break; + case '\000': /* ^SPC */ + gl_set_mark(); + break; + case '\027': /* ^W */ + gl_wipe(); + break; + case '\030': /* ^X */ + gl_exch(); + break; + case '\n': /* newline */ + case '\r': + gl_newline(); + gl_cleanup(); + return gl_buf; + /*NOTREACHED*/ + break; + case '\001': gl_fixup(gl_prompt, -1, 0); /* ^A */ + break; + case '\002': gl_fixup(gl_prompt, -1, gl_pos-1); /* ^B */ + break; + case '\004': /* ^D */ + if (gl_cnt == 0) { + gl_buf[0] = 0; + gl_cleanup(); + gl_putc('\n'); + return gl_buf; + } else { + gl_del(0); + } + break; + case '\005': gl_fixup(gl_prompt, -1, gl_cnt); /* ^E */ + break; + case '\006': gl_fixup(gl_prompt, -1, gl_pos+1); /* ^F */ + break; + case '\010': case '\177': gl_del(-1); /* ^H and DEL */ + break; + case '\t': /* TAB */ + if (gl_tab_hook) { + tmp = gl_pos; + loc = gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp); + if (loc >= 0 || tmp != gl_pos || loc == -2) + gl_fixup(gl_prompt, loc, tmp); + } + break; + case '\013': gl_kill(); /* ^K */ + break; + case '\014': sigar_getline_clear_screen(); /* ^L */ + break; + case '\016': /* ^N */ + strcpy(gl_buf, hist_next()); + if (gl_in_hook) + gl_in_hook(gl_buf); + gl_fixup(gl_prompt, 0, BUF_SIZE); + break; + case '\017': gl_overwrite = !gl_overwrite; /* ^O */ + break; + case '\020': /* ^P */ + strcpy(gl_buf, hist_prev()); + if (gl_in_hook) + gl_in_hook(gl_buf); + gl_fixup(gl_prompt, 0, BUF_SIZE); + break; + case '\022': search_back(1); /* ^R */ + break; + case '\023': search_forw(1); /* ^S */ + break; + case '\024': gl_transpose(); /* ^T */ + break; + case '\025': gl_fixup(gl_prompt,-1,0); gl_kill(); /* ^U */ + break; + case '\031': gl_yank(); /* ^Y */ + break; + case '\033': + switch(c = gl_getc()) + { + case 'b': /* M-b */ + case 'B': /* M-B */ + gl_back_1_word(); + break; + case 'd': /* M-d */ + case 'D': /* M-D */ + gl_kill_1_word(); + break; + case 'f': /* M-f */ + case 'F': /* M-F */ + gl_fwd_1_word(); + break; + case '[': /* ansi arrow keys */ + case 'O': /* xterm arrow keys */ + switch(c = gl_getc()) + { + case 'A': /* up */ + strcpy(gl_buf, hist_prev()); + if (gl_in_hook) + gl_in_hook(gl_buf); + gl_fixup(gl_prompt, 0, BUF_SIZE); + break; + case 'B': /* down */ + strcpy(gl_buf, hist_next()); + if (gl_in_hook) + gl_in_hook(gl_buf); + gl_fixup(gl_prompt, 0, BUF_SIZE); + break; + case 'C': gl_fixup(gl_prompt, -1, gl_pos+1); /* right */ + break; + case 'D': gl_fixup(gl_prompt, -1, gl_pos-1); /* left */ + break; + default: /* who knows */ + gl_putc('\007'); + break; + } + break; + default: + gl_putc('\007'); + } + break; + default: /* check for a terminal signal */ + +#if defined(unix) || defined(WIN32) || defined(R__MWERKS) + if (c > 0) { /* ignore 0 (reset above) */ + sig = 0; +#ifdef SIGINT + if (c == gl_intrc) + sig = SIGINT; +#endif +#ifdef SIGQUIT + if (c == gl_quitc) + sig = SIGQUIT; +#endif +#ifdef SIGTSTP + if (c == gl_suspc || c == gl_dsuspc) + sig = SIGTSTP; +#endif + if (sig != 0) { + gl_cleanup(); +#if !defined(WIN32) + raise(sig); +#endif +#ifdef WIN32 + if (sig == SIGINT) GenerateConsoleCtrlEvent(CTRL_C_EVENT,0); + else raise(sig); +#endif + gl_init(); + sigar_getline_redraw(); + c = 0; + } + } +#endif /* unix */ + if (c > 0) + gl_putc('\007'); + break; + } + } + if (mode == 1) return NULL; + } + if (c == -1 && gl_notty) + gl_eof = 1; + else + gl_eof = 0; + + gl_cleanup(); + gl_buf[0] = 0; + return gl_buf; +} + +SIGAR_DECLARE(int) +sigar_getline_eof() +{ + return gl_eof; +} + +SIGAR_DECLARE(char *) +sigar_getline(char *prompt) +{ + return sigar_getlinem(0, prompt); +} + +static void +gl_addchar(int c) +/* adds the character c to the input buffer at current location */ +{ + int i; + + if (gl_cnt >= BUF_SIZE - 1) + gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); + if (gl_overwrite == 0 || gl_pos == gl_cnt) { + for (i=gl_cnt; i >= gl_pos; i--) + gl_buf[i+1] = gl_buf[i]; + gl_buf[gl_pos] = c; + gl_fixup(gl_prompt, gl_pos, gl_pos+1); + } else { + gl_buf[gl_pos] = c; + gl_extent = 1; + gl_fixup(gl_prompt, gl_pos, gl_pos+1); + } +} + +static void +gl_yank() +/* adds the kill buffer to the input buffer at current location */ +{ + int i, len; + + len = strlen(gl_killbuf); + if (len > 0) { + gl_mark = gl_pos; + if (gl_overwrite == 0) { + if (gl_cnt + len >= BUF_SIZE - 1) + gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); + for (i=gl_cnt; i >= gl_pos; i--) + gl_buf[i+len] = gl_buf[i]; + for (i=0; i < len; i++) + gl_buf[gl_pos+i] = gl_killbuf[i]; + gl_fixup(gl_prompt, gl_pos, gl_pos+len); + } else { + if (gl_pos + len > gl_cnt) { + if (gl_pos + len >= BUF_SIZE - 1) + gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); + gl_buf[gl_pos + len] = 0; + } + for (i=0; i < len; i++) + gl_buf[gl_pos+i] = gl_killbuf[i]; + gl_extent = len; + gl_fixup(gl_prompt, gl_pos, gl_pos+len); + } + } else + gl_putc('\007'); +} + +static void +gl_transpose() +/* switch character under cursor and to left of cursor */ +{ + int c; + + if (gl_pos > 0 && gl_cnt > gl_pos) { + c = gl_buf[gl_pos-1]; + gl_buf[gl_pos-1] = gl_buf[gl_pos]; + gl_buf[gl_pos] = c; + gl_extent = 2; + gl_fixup(gl_prompt, gl_pos-1, gl_pos); + } else + gl_putc('\007'); +} + +static void +gl_newline() +/* + * Cleans up entire line before returning to caller. A \n is appended. + * If line longer than screen, we redraw starting at beginning + */ +{ + int change = gl_cnt; + int len = gl_cnt; + int loc = gl_width - 5; /* shifts line back to start position */ + + if (gl_cnt >= BUF_SIZE - 1) + gl_error("\n*** Error: sigar_getline(): input buffer overflow\n"); + if (gl_out_hook) { + change = gl_out_hook(gl_buf); + len = strlen(gl_buf); + } + if (gl_erase_line) { + char gl_buf0 = gl_buf[0]; + gl_buf[0] = '\0'; + gl_fixup("", 0, 0); + gl_buf[0] = gl_buf0; + } + else { + if (loc > len) + loc = len; + gl_fixup(gl_prompt, change, loc); /* must do this before appending \n */ + gl_putc('\n'); + } +#if 0 + gl_buf[len] = '\n'; + gl_buf[len+1] = '\0'; +#endif + gl_mark = -1; +} + +static void +gl_del(int loc) +/* + * Delete a character. The loc variable can be: + * -1 : delete character to left of cursor + * 0 : delete character under cursor + */ +{ + int i; + + if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) { + for (i=gl_pos+loc; i < gl_cnt; i++) + gl_buf[i] = gl_buf[i+1]; + gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc); + } else + gl_putc('\007'); +} + +static void +gl_kill() +/* delete from current position to the end of line */ +{ + if (gl_pos < gl_cnt) { + strcpy(gl_killbuf, gl_buf + gl_pos); + gl_buf[gl_pos] = '\0'; + gl_fixup(gl_prompt, gl_pos, gl_pos); + } else + gl_putc('\007'); +} + +SIGAR_DECLARE(void) sigar_getline_redraw(void) +/* emit a newline, reset and redraw prompt and current input line */ +{ + if (gl_init_done > 0) { + gl_putc('\n'); + gl_fixup(gl_prompt, -2, gl_pos); + } +} + +#define CLEAR_SCREEN "\033[2J" + +static void sigar_getline_clear_screen(void) +{ + if (gl_init_done > 0) { + gl_putc('\n'); + /* XXX what to do for non-ansi term? */ + gl_puts(CLEAR_SCREEN); + gl_fixup(gl_prompt, -2, gl_pos); + } +} + +SIGAR_DECLARE(void) sigar_getline_reset(void) +{ + gl_fixup(gl_prompt,-1,0); + gl_kill(); +} + +static void +gl_fixup(char *prompt, int change, int cursor) +/* + * This function is used both for redrawing when input changes or for + * moving within the input line. The parameters are: + * prompt: compared to last_prompt[] for changes; + * change : the index of the start of changes in the input buffer, + * with -1 indicating no changes, -2 indicating we're on + * a new line, redraw everything. + * cursor : the desired location of the cursor after the call. + * A value of BUF_SIZE can be used to indicate the cursor should + * move just past the end of the input line. + */ +{ + static int gl_shift; /* index of first on screen character */ + static int off_right; /* true if more text right of screen */ + static int off_left; /* true if more text left of screen */ + static char last_prompt[80] = ""; + int left = 0, right = -1; /* bounds for redraw */ + int padl; /* how much to erase at end of line */ + int backup; /* how far to backup before fixing */ + int new_shift; /* value of shift based on cursor */ + int extra; /* adjusts when shift (scroll) happens */ + int i; + int new_right = -1; /* alternate right bound, using gl_extent */ + int l1, l2; + + if (change == -2) { /* reset */ + gl_pos = gl_cnt = gl_shift = off_right = off_left = 0; + gl_passwd = 0; + gl_puts(prompt); + gl_passwd = gl_no_echo; + strcpy(last_prompt, prompt); + change = 0; + gl_width = gl_termw - strlen(prompt); + } else if (strcmp(prompt, last_prompt) != 0) { + l1 = strlen(last_prompt); + l2 = strlen(prompt); + gl_cnt = gl_cnt + l1 - l2; + strcpy(last_prompt, prompt); + backup = gl_pos - gl_shift + l1; + for (i=0; i < backup; i++) + gl_putc('\b'); + gl_passwd = 0; + gl_puts(prompt); + gl_passwd = gl_no_echo; + gl_pos = gl_shift; + gl_width = gl_termw - l2; + change = 0; + } + padl = (off_right)? gl_width - 1 : gl_cnt - gl_shift; /* old length */ + backup = gl_pos - gl_shift; + if (change >= 0) { + gl_cnt = strlen(gl_buf); + if (change > gl_cnt) + change = gl_cnt; + } + if (cursor > gl_cnt) { + if (cursor != BUF_SIZE) /* BUF_SIZE means end of line */ + gl_putc('\007'); + cursor = gl_cnt; + } + if (cursor < 0) { + gl_putc('\007'); + cursor = 0; + } + if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2)) + extra = 2; /* shift the scrolling boundary */ + else + extra = 0; + new_shift = cursor + extra + gl_scroll - gl_width; + if (new_shift > 0) { + new_shift /= gl_scroll; + new_shift *= gl_scroll; + } else + new_shift = 0; + if (new_shift != gl_shift) { /* scroll occurs */ + gl_shift = new_shift; + off_left = (gl_shift)? 1 : 0; + off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; + left = gl_shift; + new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt; + } else if (change >= 0) { /* no scroll, but text changed */ + if (change < gl_shift + off_left) { + left = gl_shift; + } else { + left = change; + backup = gl_pos - change; + } + off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0; + right = (off_right)? gl_shift + gl_width - 2 : gl_cnt; + new_right = (gl_extent && (right > left + gl_extent))? + left + gl_extent : right; + } + padl -= (off_right)? gl_width - 1 : gl_cnt - gl_shift; + padl = (padl < 0)? 0 : padl; + if (left <= right) { /* clean up screen */ + for (i=0; i < backup; i++) + gl_putc('\b'); + if (left == gl_shift && off_left) { + gl_putc('$'); + left++; + } + for (i=left; i < new_right; i++) + gl_putc(gl_buf[i]); + gl_pos = new_right; + if (off_right && new_right == right) { + gl_putc('$'); + gl_pos++; + } else { + for (i=0; i < padl; i++) /* erase remains of prev line */ + gl_putc(' '); + gl_pos += padl; + } + } + i = gl_pos - cursor; /* move to final cursor location */ + if (i > 0) { + while (i--) + gl_putc('\b'); + } else { + for (i=gl_pos; i < cursor; i++) + gl_putc(gl_buf[i]); + } + gl_pos = cursor; +} + +static int +gl_tab(char *buf, int offset, int *loc) +/* default tab handler, acts like tabstops every 8 cols */ +{ + int i, count, len; + + len = strlen(buf); + count = 8 - (offset + *loc) % 8; + for (i=len; i >= *loc; i--) + buf[i+count] = buf[i]; + for (i=0; i < count; i++) + buf[*loc+i] = ' '; + i = *loc; + *loc = i + count; + return i; +} + +/******************* History stuff **************************************/ + +#ifndef HIST_SIZE +#define HIST_SIZE 100 +#endif + +static int hist_pos = 0, hist_last = 0; +static char *hist_buf[HIST_SIZE]; + +static void +hist_init() +{ + int i; + + if (gl_savehist) return; + + hist_buf[0] = ""; + for (i=1; i < HIST_SIZE; i++) + hist_buf[i] = (char *)0; +} + +SIGAR_DECLARE(void) sigar_getline_completer_set(sigar_getline_completer_t func) +{ + if (func) { + gl_tab_hook = func; + } + else { + gl_tab_hook = gl_tab; + } +} + +SIGAR_DECLARE(void) +sigar_getline_histinit(char *file) +{ + char line[256]; + FILE *fp; + int nline = 1; /* prevent from becoming 0 */ + + gl_savehist = 0; + + hist_init(); + + if (!strcmp(file, "-")) return; + + sprintf(gl_histfile, "%s", file); + + fp = fopen(gl_histfile, "r"); + if (fp) + while (fgets(line, 256, fp)) { + nline++; + sigar_getline_histadd(line); + } + else + fp = fopen(gl_histfile, "w"); + + if (fp) fclose(fp); + + gl_savehist = nline; +} + +SIGAR_DECLARE(void) +sigar_getline_histadd(char *buf) +{ + static char *prev = 0; + char *p = buf; + int len; + + while (*p == ' ' || *p == '\t' || *p == '\n') + p++; + if (*p) { + len = strlen(buf); + if (strchr(p, '\n')) /* previously line already has NL stripped */ + len--; + if (prev == 0 || strlen(prev) != len || + strncmp(prev, buf, len) != 0) { + hist_buf[hist_last] = hist_save(buf); + prev = hist_buf[hist_last]; + hist_last = (hist_last + 1) % HIST_SIZE; + if (hist_buf[hist_last] && *hist_buf[hist_last]) { + free(hist_buf[hist_last]); + } + hist_buf[hist_last] = ""; + + /* append command to history file */ + if (gl_savehist) { + FILE *fp; + fp = fopen(gl_histfile, "a+"); + if (fp) { + fprintf(fp, "%s\n", prev); + gl_savehist++; + fclose(fp); + } + + /* if more than HIST_SIZE lines, safe last 60 command and delete rest */ + if (gl_savehist > HIST_SIZE) { + FILE *ftmp; + char tname[L_tmpnam]; + char line[BUFSIZ]; + + fp = fopen(gl_histfile, "r"); + tmpnam(tname); + ftmp = fopen(tname, "w"); + if (fp && ftmp) { + int nline = 0; + while (fgets(line, BUFSIZ, fp)) { + nline++; + gl_savehist = 1; /* prevent from becoming 0 */ + if (nline > HIST_SIZE-60) { + gl_savehist++; + fprintf(ftmp, "%s", line); + } + } + } + if (fp) fclose(fp); + if (ftmp) fclose(ftmp); + + /* copy back to history file */ + fp = fopen(gl_histfile, "w"); + ftmp = fopen(tname, "r"); + if (fp && ftmp) + while (fgets(line, BUFSIZ, ftmp)) + fprintf(fp, "%s", line); + + if (fp) fclose(fp); + if (ftmp) fclose(ftmp); + remove(tname); + } + } + } + } + hist_pos = hist_last; +} + +static char * +hist_prev() +/* loads previous hist entry into input buffer, sticks on first */ +{ + char *p = 0; + int next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE; + + if (hist_buf[hist_pos] != 0 && next != hist_last) { + hist_pos = next; + p = hist_buf[hist_pos]; + } + if (p == 0) { + p = ""; + gl_putc('\007'); + } + return p; +} + +static char * +hist_next() +/* loads next hist entry into input buffer, clears on last */ +{ + char *p = 0; + + if (hist_pos != hist_last) { + hist_pos = (hist_pos+1) % HIST_SIZE; + p = hist_buf[hist_pos]; + } + if (p == 0) { + p = ""; + gl_putc('\007'); + } + return p; +} + +static char * +hist_save(char *p) +/* makes a copy of the string */ +{ + char *s = 0; + int len = strlen(p); + char *nl = strchr(p, '\n'); + + if (nl) { + if ((s = (char *)malloc(len)) != 0) { + strncpy(s, p, len-1); + s[len-1] = 0; + } + } else { + if ((s = (char *)malloc(len+1)) != 0) { + strcpy(s, p); + } + } + if (s == 0) + gl_error("\n*** Error: hist_save() failed on malloc\n"); + return s; +} + +/******************* Search stuff **************************************/ + +static char search_prompt[101]; /* prompt includes search string */ +static char search_string[100]; +static int search_pos = 0; /* current location in search_string */ +static int search_forw_flg = 0; /* search direction flag */ +static int search_last = 0; /* last match found */ + +static void +search_update(int c) +{ + if (c == 0) { + search_pos = 0; + search_string[0] = 0; + search_prompt[0] = '?'; + search_prompt[1] = ' '; + search_prompt[2] = 0; + } else if (c > 0) { + search_string[search_pos] = c; + search_string[search_pos+1] = 0; + search_prompt[search_pos] = c; + search_prompt[search_pos+1] = '?'; + search_prompt[search_pos+2] = ' '; + search_prompt[search_pos+3] = 0; + search_pos++; + } else { + if (search_pos > 0) { + search_pos--; + search_string[search_pos] = 0; + search_prompt[search_pos] = '?'; + search_prompt[search_pos+1] = ' '; + search_prompt[search_pos+2] = 0; + } else { + gl_putc('\007'); + hist_pos = hist_last; + } + } +} + +static void +search_addchar(int c) +{ + char *loc; + + search_update(c); + if (c < 0) { + if (search_pos > 0) { + hist_pos = search_last; + } else { + gl_buf[0] = 0; + hist_pos = hist_last; + } + strcpy(gl_buf, hist_buf[hist_pos]); + } + if ((loc = strstr(gl_buf, search_string)) != 0) { + gl_fixup(search_prompt, 0, loc - gl_buf); + } else if (search_pos > 0) { + if (search_forw_flg) { + search_forw(0); + } else { + search_back(0); + } + } else { + gl_fixup(search_prompt, 0, 0); + } +} + +static void +search_term() +{ + gl_search_mode = 0; + if (gl_buf[0] == 0) /* not found, reset hist list */ + hist_pos = hist_last; + if (gl_in_hook) + gl_in_hook(gl_buf); + gl_fixup(gl_prompt, 0, gl_pos); +} + +static void +search_back(int new_search) +{ + int found = 0; + char *p, *loc; + + search_forw_flg = 0; + if (gl_search_mode == 0) { + search_last = hist_pos = hist_last; + search_update(0); + gl_search_mode = 1; + gl_buf[0] = 0; + gl_fixup(search_prompt, 0, 0); + } else if (search_pos > 0) { + while (!found) { + p = hist_prev(); + if (*p == 0) { /* not found, done looking */ + gl_buf[0] = 0; + gl_fixup(search_prompt, 0, 0); + found = 1; + } else if ((loc = strstr(p, search_string)) != 0) { + strcpy(gl_buf, p); + gl_fixup(search_prompt, 0, loc - p); + if (new_search) + search_last = hist_pos; + found = 1; + } + } + } else { + gl_putc('\007'); + } +} + +static void +search_forw(int new_search) +{ + int found = 0; + char *p, *loc; + + search_forw_flg = 1; + if (gl_search_mode == 0) { + search_last = hist_pos = hist_last; + search_update(0); + gl_search_mode = 1; + gl_buf[0] = 0; + gl_fixup(search_prompt, 0, 0); + } else if (search_pos > 0) { + while (!found) { + p = hist_next(); + if (*p == 0) { /* not found, done looking */ + gl_buf[0] = 0; + gl_fixup(search_prompt, 0, 0); + found = 1; + } else if ((loc = strstr(p, search_string)) != 0) { + strcpy(gl_buf, p); + gl_fixup(search_prompt, 0, loc - p); + if (new_search) + search_last = hist_pos; + found = 1; + } + } + } else { + gl_putc('\007'); + } +} +#if 0 +/*********************************************************************** + * * + * Strip blanks from both sides of a string. Space for the new * + * string is allocated and a pointer to it is returned. * + * * + ***********************************************************************/ +char *strip(char *s) +{ + char *r, *t1, *t2; + int l; + + l = strlen(s); + r = (char *)calloc(l+1, 1); + + if (l == 0) { + *r = '\0'; + return r; + } + + /* get rid of leading blanks */ + t1 = s; + while (*t1 == ' ') + t1++; + + t2 = s + l - 1; + while (*t2 == ' ' && t2 > s) + t2--; + + if (t1 > t2) { + *r = '\0'; + return r; + } + strncpy(r, t1, (size_t) (t2-t1+1)); + + return r; +} +#endif +/*****************************************************************************/ +/* Extra routine provided by Christian Lacunza */ +/*****************************************************************************/ + +/* move cursor back to beginning of _current_ word */ +/* unless it's already at the beginning, */ +/* in which case it moves back to the beginning */ +/* of the _previous_ word. */ +static void gl_back_1_word( void ) +{ + int i = gl_pos; + + /* if we're at the beginning of a word, */ + /* slip back into the preceeding whitespace */ + if( i>0 && is_whitespace(gl_buf[i-1]) ) { + i-=1; + } + + /* now move back over all consecutive whitespace */ + while( i>0 && is_whitespace(gl_buf[i]) ) { + i-=1; + } + + /* now keep moving back over all consecutive non-whitespace */ + /* until we find the beginning of this word. */ + /* ie. stop just before more whitespace shows up. */ + while( i>0 && !is_whitespace(gl_buf[i-1]) ) { + i-=1; + } + + /* move the cursor here */ + gl_fixup(gl_prompt, -1, i); +} + +/* kills from current position to end of word */ +static void gl_kill_1_word( void ) +{ + int i = gl_pos; + int j = gl_pos; + +/* delete this: */ +#if 0 + /* not sure what to do with "punctuation" */ + if( is_whitespace(gl_buf[j]) && gl_buf[j]!=' ' ) { + return; + } + /* first find a word */ + while( j +#include +#include +#include +#include + +#include "sigar.h" +#include "sigar_private.h" +#include "sigar_os.h" +#include "sigar_util.h" + +#ifndef WIN32 + +#include + +SIGAR_INLINE char *sigar_uitoa(char *buf, unsigned int n, int *len) +{ + char *start = buf + UITOA_BUFFER_SIZE - 1; + + *start = 0; + + do { + *--start = '0' + (n % 10); + ++*len; + n /= 10; + } while (n); + + return start; +} + +SIGAR_INLINE char *sigar_skip_line(char *buffer, int buflen) +{ + char *ptr = buflen ? + (char *)memchr(buffer, '\n', buflen) : /* bleh */ + strchr(buffer, '\n'); + return ++ptr; +} + +SIGAR_INLINE char *sigar_skip_token(char *p) +{ + while (sigar_isspace(*p)) p++; + while (*p && !sigar_isspace(*p)) p++; + return p; +} + +SIGAR_INLINE char *sigar_skip_multiple_token(char *p, int count) +{ + int i; + + for (i = 0; i < count; i++) { + p = sigar_skip_token(p); + } + + return p; +} + +int sigar_file2str(const char *fname, char *buffer, int buflen) +{ + int len; + int fd = open(fname, O_RDONLY); + + if (fd < 0) { + return ENOENT; + } + + len = read(fd, buffer, buflen); + buffer[len] = '\0'; + close(fd); + + return SIGAR_OK; +} + +/* avoiding sprintf */ + +char *sigar_proc_filename(char *buffer, int buflen, + sigar_pid_t bigpid, + const char *fname, int fname_len) +{ + int len = 0; + char *ptr = buffer; + unsigned int pid = (unsigned int)bigpid; /* XXX -- This isn't correct */ + char pid_buf[UITOA_BUFFER_SIZE]; + char *pid_str = sigar_uitoa(pid_buf, pid, &len); + + assert((unsigned int)buflen >= + (SSTRLEN(PROC_FS_ROOT) + UITOA_BUFFER_SIZE + fname_len + 1)); + + memcpy(ptr, PROC_FS_ROOT, SSTRLEN(PROC_FS_ROOT)); + ptr += SSTRLEN(PROC_FS_ROOT); + + memcpy(ptr, pid_str, len); + ptr += len; + + memcpy(ptr, fname, fname_len); + ptr += fname_len; + *ptr = '\0'; + + return buffer; +} + +int sigar_proc_file2str(char *buffer, int buflen, + sigar_pid_t pid, + const char *fname, + int fname_len) +{ + int retval; + + buffer = sigar_proc_filename(buffer, buflen, pid, + fname, fname_len); + + retval = sigar_file2str(buffer, buffer, buflen); + + if (retval != SIGAR_OK) { + switch (retval) { + case ENOENT: + retval = ESRCH; /* no such process */ + default: + break; + } + } + + return retval; +} + +int sigar_proc_list_procfs_get(sigar_t *sigar, + sigar_proc_list_t *proclist) +{ + DIR *dirp = opendir("/proc"); + struct dirent *ent; +#ifdef HAVE_READDIR_R + struct dirent dbuf; +#endif + + if (!dirp) { + return errno; + } + + sigar_proc_list_create(proclist); + +#ifdef HAVE_READDIR_R + while (readdir_r(dirp, &dbuf, &ent) == 0) { + if (ent == NULL) { + break; + } +#else + while ((ent = readdir(dirp))) { +#endif + if (!sigar_isdigit(*ent->d_name)) { + continue; + } + + /* XXX: more sanity checking */ + + SIGAR_PROC_LIST_GROW(proclist); + + proclist->data[proclist->number++] = + strtoul(ent->d_name, NULL, 10); + } + + closedir(dirp); + + return SIGAR_OK; +} + +int sigar_proc_fd_count(sigar_t *sigar, sigar_pid_t pid, + sigar_uint64_t *total) +{ + DIR *dirp; + struct dirent *ent; +#ifdef HAVE_READDIR_R + struct dirent dbuf; +#endif + char name[BUFSIZ]; + + (void)SIGAR_PROC_FILENAME(name, pid, "/fd"); + + *total = 0; + + if (!(dirp = opendir(name))) { + return errno; + } + +#ifdef HAVE_READDIR_R + while (readdir_r(dirp, &dbuf, &ent) == 0) { + if (ent == NULL) { + break; + } +#else + while ((ent = readdir(dirp))) { +#endif + if (!sigar_isdigit(*ent->d_name)) { + continue; + } + + (*total)++; + } + + closedir(dirp); + + return SIGAR_OK; +} + +#endif /* WIN32 */ + +/* os impls should use an optimized version of this */ +int sigar_proc_count(sigar_t *sigar, sigar_uint64_t *total) +{ + int status; + sigar_proc_list_t proclist; + + *total = 0; + + if ((status = sigar_proc_list_get(sigar, &proclist)) != SIGAR_OK) { + return status; + } + + *total = proclist.number; + + sigar_proc_list_destroy(sigar, &proclist); + + return SIGAR_OK; +} + +int sigar_mem_calc_ram(sigar_t *sigar, sigar_mem_t *mem) +{ + sigar_uint64_t lram = (mem->total / (1024 * 1024)); + int ram = (int)lram; /* must cast after division */ + int remainder = ram % 8; + + if (remainder > 0) { + ram += (8 - remainder); + } + + mem->ram = ram; + + return ram; +} + +double sigar_file_system_usage_calc_used(sigar_t *sigar, + sigar_file_system_usage_t *fsusage) +{ + /* + * win32 will not convert __uint64 to double. + * convert to KB then do unsigned long -> double. + */ + sigar_uint64_t b_used = (fsusage->total - fsusage->free) / 1024; + sigar_uint64_t b_avail = fsusage->avail / 1024; + unsigned long utotal = b_used + b_avail; + unsigned long used = b_used; + + if (utotal != 0) { + unsigned long u100 = used * 100; + double pct = u100 / utotal + + ((u100 % utotal != 0) ? 1 : 0); + return pct / 100; + } + + return 0; +} + +#ifdef WIN32 +#define vsnprintf _vsnprintf +#endif + +SIGAR_DECLARE(void) sigar_log_printf(sigar_t *sigar, int level, + const char *format, ...) +{ + va_list args; + char buffer[8192]; + + if (level > sigar->log_level) { + return; + } + + if (!sigar->log_impl) { + return; + } + + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + sigar->log_impl(sigar, sigar->log_data, level, buffer); +} + +SIGAR_DECLARE(void) sigar_log(sigar_t *sigar, int level, char *message) +{ + if (level > sigar->log_level) { + return; + } + + if (!sigar->log_impl) { + return; + } + + sigar->log_impl(sigar, sigar->log_data, level, message); +} + +SIGAR_DECLARE(void) sigar_log_impl_set(sigar_t *sigar, void *data, + sigar_log_impl_t impl) +{ + sigar->log_data = data; + sigar->log_impl = impl; +} + +SIGAR_DECLARE(int) sigar_log_level_get(sigar_t *sigar) +{ + return sigar->log_level; +} + +static const char *log_levels[] = { + "FATAL", + "ERROR", + "WARN", + "INFO", + "DEBUG", + "TRACE" +}; + +SIGAR_DECLARE(const char *) sigar_log_level_string_get(sigar_t *sigar) +{ + return log_levels[sigar->log_level]; +} + +SIGAR_DECLARE(void) sigar_log_level_set(sigar_t *sigar, int level) +{ + sigar->log_level = level; +} + +SIGAR_DECLARE(void) sigar_log_impl_file(sigar_t *sigar, void *data, + int level, char *message) +{ + FILE *fp = (FILE*)data; + fprintf(fp, "[%s] %s\n", log_levels[level], message); +} diff --git a/tools/PerfBrowser/App.ico b/tools/PerfBrowser/App.ico new file mode 100644 index 00000000..3a5525fd Binary files /dev/null and b/tools/PerfBrowser/App.ico differ diff --git a/tools/PerfBrowser/AssemblyInfo.cs b/tools/PerfBrowser/AssemblyInfo.cs new file mode 100644 index 00000000..9f89a328 --- /dev/null +++ b/tools/PerfBrowser/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/tools/PerfBrowser/MainForm.cs b/tools/PerfBrowser/MainForm.cs new file mode 100644 index 00000000..68a8ca7f --- /dev/null +++ b/tools/PerfBrowser/MainForm.cs @@ -0,0 +1,289 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; + +namespace PerfBrowser +{ + ///

+ /// Summary description for Form1. + /// + public class MainForm : System.Windows.Forms.Form + { + private System.Windows.Forms.ColumnHeader col1; + private System.Windows.Forms.ColumnHeader col2; + private System.Windows.Forms.ComboBox comboBox; + private System.Windows.Forms.ListView lvCounters; + private System.Windows.Forms.TreeView tvCategories; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Splitter splitter2; + private System.Windows.Forms.Panel panel2; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public MainForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lvCounters = new System.Windows.Forms.ListView(); + this.col1 = new System.Windows.Forms.ColumnHeader(); + this.col2 = new System.Windows.Forms.ColumnHeader(); + this.comboBox = new System.Windows.Forms.ComboBox(); + this.tvCategories = new System.Windows.Forms.TreeView(); + this.panel1 = new System.Windows.Forms.Panel(); + this.splitter2 = new System.Windows.Forms.Splitter(); + this.panel2 = new System.Windows.Forms.Panel(); + this.panel1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.SuspendLayout(); + // + // lvCounters + // + this.lvCounters.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.lvCounters.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.col1, + this.col2}); + this.lvCounters.Cursor = System.Windows.Forms.Cursors.Default; + this.lvCounters.Location = new System.Drawing.Point(0, 36); + this.lvCounters.MultiSelect = false; + this.lvCounters.Name = "lvCounters"; + this.lvCounters.Size = new System.Drawing.Size(468, 324); + this.lvCounters.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.lvCounters.TabIndex = 2; + this.lvCounters.View = System.Windows.Forms.View.Details; + // + // col1 + // + this.col1.Text = "Name"; + this.col1.Width = 314; + // + // col2 + // + this.col2.Text = "Value"; + this.col2.Width = 150; + // + // comboBox + // + this.comboBox.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.comboBox.Location = new System.Drawing.Point(8, 8); + this.comboBox.Name = "comboBox"; + this.comboBox.Size = new System.Drawing.Size(688, 21); + this.comboBox.TabIndex = 2; + this.comboBox.Text = "."; + this.comboBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.comboBox_KeyDown); + // + // tvCategories + // + this.tvCategories.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.tvCategories.ImageIndex = -1; + this.tvCategories.Location = new System.Drawing.Point(8, 36); + this.tvCategories.Name = "tvCategories"; + this.tvCategories.SelectedImageIndex = -1; + this.tvCategories.Size = new System.Drawing.Size(212, 324); + this.tvCategories.Sorted = true; + this.tvCategories.TabIndex = 3; + this.tvCategories.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvCategories_AfterSelect); + // + // panel1 + // + this.panel1.Controls.AddRange(new System.Windows.Forms.Control[] { + this.tvCategories}); + this.panel1.Dock = System.Windows.Forms.DockStyle.Left; + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(220, 369); + this.panel1.TabIndex = 0; + // + // splitter2 + // + this.splitter2.Location = new System.Drawing.Point(220, 0); + this.splitter2.Name = "splitter2"; + this.splitter2.Size = new System.Drawing.Size(8, 369); + this.splitter2.TabIndex = 1; + this.splitter2.TabStop = false; + // + // panel2 + // + this.panel2.Controls.AddRange(new System.Windows.Forms.Control[] { + this.lvCounters}); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(228, 0); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(476, 369); + this.panel2.TabIndex = 3; + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(704, 369); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.comboBox, + this.panel2, + this.splitter2, + this.panel1}); + this.Name = "MainForm"; + this.Text = "Performance Browser"; + this.Load += new System.EventHandler(this.MainForm_Load); + this.panel1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.Run(new MainForm()); + } + + private void RefreshContent() + { + // Clear everthing currently displayed in the app + this.tvCategories.Nodes.Clear(); + this.lvCounters.Items.Clear(); + this.Refresh(); + + // Suspend drawing until we're done + this.tvCategories.BeginUpdate(); + + try + { + PerformanceCounterCategory[] aPerfCat = PerformanceCounterCategory.GetCategories(this.comboBox.Text); + + foreach(PerformanceCounterCategory perfcat in aPerfCat) + { + TreeNode nodeCat = new TreeNode(perfcat.CategoryName); + this.tvCategories.Nodes.Add(nodeCat); + + try + { + String[] astrInstNames = perfcat.GetInstanceNames(); + + foreach(String strInstName in astrInstNames) + nodeCat.Nodes.Add(new TreeNode(strInstName)); + } + catch(Exception ex) + { + Console.WriteLine(ex); + } + } + + //this.tvCategories.SelectedNode.Index = 0; + } + catch(Exception) + { + this.lvCounters.Items.Add("Machine not found."); + } + + // Resume drawing + this.tvCategories.EndUpdate(); + } + + private void MainForm_Load(object sender, System.EventArgs e) + { + this.comboBox.Text = SystemInformation.ComputerName; + this.RefreshContent(); + } + + private void comboBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) + { + if(e.KeyCode == Keys.Enter) + this.RefreshContent(); + } + + private void tvCategories_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e) + { + // Clear the listview to prep it for new data + this.lvCounters.Items.Clear(); + + // If a category was selected, that has multiple instances skip everything + if(e.Node.Parent == null && e.Node.GetNodeCount(false) > 0) + return; + + // If there are no instances just get the category name, otherwise get category and instance name + String strCategory; + String strInstance = null; + + if(e.Node.Parent == null) + { + strCategory = e.Node.Text; + } + else + { + strCategory = e.Node.Parent.Text; + strInstance = e.Node.Text; + } + + // Suspend drawing until we're done + this.lvCounters.BeginUpdate(); + + // Get the selected category + PerformanceCounterCategory perfcat = new PerformanceCounterCategory(strCategory, this.comboBox.Text); + + try + { + InstanceDataCollectionCollection datacollcoll = perfcat.ReadCategory(); + + foreach(InstanceDataCollection datacoll in datacollcoll.Values) + { + foreach(InstanceData data in datacoll.Values) + { + if(strInstance == null || data.InstanceName == strInstance) + { + ListViewItem item = new ListViewItem(datacoll.CounterName); + item.SubItems.Add(data.RawValue.ToString()); + this.lvCounters.Items.Add(item); + break; + } + } + } + } + catch(Exception ex) + { + Console.WriteLine(ex); + } + + // Result drawing + this.lvCounters.EndUpdate(); + } + } +} diff --git a/tools/PerfBrowser/MainForm.resx b/tools/PerfBrowser/MainForm.resx new file mode 100644 index 00000000..d4899948 --- /dev/null +++ b/tools/PerfBrowser/MainForm.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + MainForm + + \ No newline at end of file diff --git a/tools/PerfBrowser/PerfBrowser.csproj b/tools/PerfBrowser/PerfBrowser.csproj new file mode 100644 index 00000000..60535259 --- /dev/null +++ b/tools/PerfBrowser/PerfBrowser.csproj @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/PerfBrowser/PerfBrowser.csproj.user b/tools/PerfBrowser/PerfBrowser.csproj.user new file mode 100644 index 00000000..c8340eaa --- /dev/null +++ b/tools/PerfBrowser/PerfBrowser.csproj.user @@ -0,0 +1,48 @@ + + + + + + + + + + + + diff --git a/tools/PerfBrowser/PerfBrowser.exe.manifest b/tools/PerfBrowser/PerfBrowser.exe.manifest new file mode 100644 index 00000000..fe84ad9e --- /dev/null +++ b/tools/PerfBrowser/PerfBrowser.exe.manifest @@ -0,0 +1,22 @@ + + + +PerfBrowser + + + + + + diff --git a/tools/PerfBrowser/PerfBrowser.sln b/tools/PerfBrowser/PerfBrowser.sln new file mode 100644 index 00000000..5acd8dd3 --- /dev/null +++ b/tools/PerfBrowser/PerfBrowser.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PerfBrowser", "PerfBrowser.csproj", "{00B4B56E-53CA-4E24-A937-4503D19E993C}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {00B4B56E-53CA-4E24-A937-4503D19E993C}.Debug.ActiveCfg = Debug|.NET + {00B4B56E-53CA-4E24-A937-4503D19E993C}.Debug.Build.0 = Debug|.NET + {00B4B56E-53CA-4E24-A937-4503D19E993C}.Release.ActiveCfg = Release|.NET + {00B4B56E-53CA-4E24-A937-4503D19E993C}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/tools/PerfBrowser/default.build b/tools/PerfBrowser/default.build new file mode 100644 index 00000000..f39075dd --- /dev/null +++ b/tools/PerfBrowser/default.build @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + +