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 <fields[$field_class_ix]";
+
+ my @init_fields = ("#define $init_define(cls)",
+ " if (!$field_class) {",
+ " $field_class = ",
+ " malloc(sizeof(*$field_class));",
+ " $field_class->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 0) {
+ pids = ProcessFinder.find(sigar, args[0]);
+ }
+ else if (args[0].equals("$$")) {
+ pids = new long[] { sigar.getPid() };
+ }
+ else {
+ pids = new long[] {
+ Long.parseLong(args[0])
+ };
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Usage: cmd [pid|query]");
+ }
+
+ return pids;
+ }
+
+ public static long getPid(SigarProxy sigar, String[] args)
+ throws SigarException {
+
+ long[] pids = getPids(sigar, args);
+ if (pids.length != 1) {
+ throw new IllegalArgumentException("Query matches more than 1 process");
+ }
+
+ return pids[0];
+ }
+}
diff --git a/bindings/java/src/net/hyperic/sigar/cmd/ProcFileInfo.java b/bindings/java/src/net/hyperic/sigar/cmd/ProcFileInfo.java
new file mode 100644
index 00000000..d6eaba41
--- /dev/null
+++ b/bindings/java/src/net/hyperic/sigar/cmd/ProcFileInfo.java
@@ -0,0 +1,63 @@
+package net.hyperic.sigar.cmd;
+
+import net.hyperic.sigar.Sigar;
+import net.hyperic.sigar.SigarException;
+import net.hyperic.sigar.ProcFd;
+import net.hyperic.sigar.ProcExe;
+
+public class ProcFileInfo extends SigarCommandBase {
+
+ public ProcFileInfo(Shell shell) {
+ super(shell);
+ }
+
+ public ProcFileInfo() {
+ super();
+ }
+
+ protected boolean validateArgs(String[] args) {
+ return true;
+ }
+
+ public String getUsageShort() {
+ return "Display process file info";
+ }
+
+ public boolean isPidCompleter() {
+ return true;
+ }
+
+ public void output(String[] args) throws SigarException {
+ long[] pids = this.shell.findPids(args);
+
+ for (int i=0; i 0) {
+ pids = this.shell.findPids(args);
+ }
+ else {
+ pids = this.proxy.getProcList();
+ }
+
+ for (int i=0; i 0) {
+ pids = ProcessFinder.find(sigar, args[0]);
+ }
+ else if (args[0].equals("$$")) {
+ pids = new long[] { sigar.getPid() };
+ }
+ else {
+ pids = new long[] {
+ Long.parseLong(args[0])
+ };
+ }
+ break;
+ default:
+ pids = new long[args.length];
+ 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 0) {
+ return partial;
+ }
+ return null;
+ }
+
+ public String complete(String line) {
+ this.completions.clear();
+ int len = line.length();
+
+ for (Iterator it = getIterator();
+ it.hasNext(); )
+ {
+ String name = (String)it.next();
+ if ((len == 0) || name.startsWith(line)) {
+ this.completions.add(name);
+ }
+ }
+
+ int size = this.completions.size();
+ switch (size) {
+ case 0:
+ return line;
+
+ case 1:
+ return (String)this.completions.get(0);
+
+ default:
+ String partial = displayPossible(this.completions);
+ if (partial != null) {
+ return partial;
+ }
+ return line;
+ }
+ }
+}
diff --git a/bindings/java/src/net/hyperic/sigar/shell/FileCompleter.java b/bindings/java/src/net/hyperic/sigar/shell/FileCompleter.java
new file mode 100644
index 00000000..b0d7ea54
--- /dev/null
+++ b/bindings/java/src/net/hyperic/sigar/shell/FileCompleter.java
@@ -0,0 +1,140 @@
+package net.hyperic.sigar.shell;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+import java.util.List;
+import java.util.Iterator;
+
+import net.hyperic.sigar.SigarLoader;
+
+public class FileCompleter
+ extends CollectionCompleter
+ implements FilenameFilter {
+
+ private static final String HOME =
+ System.getProperty("user.home");
+
+ private String name;
+
+ public FileCompleter() {
+ super();
+ }
+
+ public FileCompleter(ShellBase shell) {
+ super(shell);
+ }
+
+ public static String expand(String name) {
+ if (name.startsWith("~")) {
+ return HOME + name.substring(1, name.length());
+ }
+ return name;
+ }
+
+ public boolean accept(File dir, String name) {
+ if (name.equals(".") || name.equals("..")) {
+ return false;
+ }
+ return name.startsWith(this.name);
+ }
+
+ public Iterator getIterator() {
+ return null; //unused
+ }
+
+ private String appendSep(String name) {
+ if (name.endsWith(File.separator)) {
+ return name;
+ }
+
+ return name + File.separator;
+ }
+
+ //e.g. we don't want "~/." treated as a directory
+ //but we do want "." to be.
+ private boolean isDotFile(File file) {
+ return
+ file.getName().equals(".") &&
+ (file.getParentFile() != null);
+ }
+
+ public String complete(String line) {
+ String fileName = line;
+ boolean isHome = false;
+
+ if (line.length() == 0) {
+ return appendSep(".");
+ }
+ else if (fileName.startsWith("~")) {
+ isHome = true;
+ fileName = expand(fileName);
+ }
+
+ File file = new File(fileName);
+ File dir;
+ if (file.exists() && !isDotFile(file)) {
+ if (file.isDirectory()) {
+ this.name = null;
+ dir = file;
+ if (!fileName.endsWith(File.separator)) {
+ return line + File.separator;
+ }
+ }
+ else {
+ return line;
+ }
+ }
+ else {
+ this.name = file.getName();
+ dir = file.getParentFile();
+ if (dir == null) {
+ if (SigarLoader.IS_WIN32 &&
+ (line.length() == 1) &&
+ Character.isLetter(line.charAt(0)))
+ {
+ //e.g. C:\
+ return line + ":\\";
+ }
+
+ return line;
+ }
+ if (!(dir.exists() && dir.isDirectory())) {
+ return line;
+ }
+ }
+
+ String[] list;
+ if (this.name == null) {
+ list = dir.list();
+ }
+ else {
+ list = dir.list(this);
+ }
+
+ if (list.length == 1) {
+ fileName = appendSep(dir.toString()) + list[0];
+
+ if (new File(fileName).isDirectory()) {
+ fileName = appendSep(fileName);
+ }
+ if (isHome) {
+ return "~" + fileName.substring(HOME.length(),
+ fileName.length());
+ }
+
+ return fileName;
+ }
+
+ String partial = displayPossible(list);
+ if (partial != null) {
+ return appendSep(dir.toString()) + partial;
+ }
+ return line;
+ }
+
+ public static void main(String[] args) throws Exception {
+ String line = new FileCompleter().complete(args[0]);
+ System.out.println("\nsigar> '" + 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);
+
+ traceln("Sys=" + cpu.getSys());
+ assertTrue(cpu.getSys() >= 0);
+
+ traceln("Total=" + cpu.getTotal());
+ assertTrue(cpu.getTotal() >= 0);
+ }
+
+ public void testCreate() throws Exception {
+ Sigar sigar = new Sigar();
+
+ Cpu cpu = sigar.getCpu();
+
+ traceln("");
+
+ traceln("getCpu:");
+ checkCpu(cpu);
+
+ Cpu[] cpuList = sigar.getCpuList();
+
+ 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);
+ // XXX vsize, resident, share, rss
+ }
+}
diff --git a/bindings/java/src/net/hyperic/sigar/test/TestProcStat.java b/bindings/java/src/net/hyperic/sigar/test/TestProcStat.java
new file mode 100644
index 00000000..676e35d1
--- /dev/null
+++ b/bindings/java/src/net/hyperic/sigar/test/TestProcStat.java
@@ -0,0 +1,28 @@
+package net.hyperic.sigar.test;
+
+import junit.framework.TestCase;
+
+import net.hyperic.sigar.Sigar;
+import net.hyperic.sigar.ProcStat;
+
+public class TestProcStat extends TestCase {
+
+ public TestProcStat(String name) {
+ super(name);
+ }
+
+ public void testCreate() throws Exception {
+ Sigar sigar = new Sigar();
+
+ ProcStat stat = sigar.getProcStat();
+
+ long[] pids = sigar.getProcList();
+
+ //oh-no a racing condition!
+ //possible for this test to fail under normal
+ //conditions if the process table changes in between.
+ //if this is a real problem, can just change to:
+ //assertTrue(stat.getTotal() > 1);
+ assertTrue(pids.length == stat.getTotal());
+ }
+}
diff --git a/bindings/java/src/net/hyperic/sigar/test/TestProcState.java b/bindings/java/src/net/hyperic/sigar/test/TestProcState.java
new file mode 100644
index 00000000..8b6e27d6
--- /dev/null
+++ b/bindings/java/src/net/hyperic/sigar/test/TestProcState.java
@@ -0,0 +1,23 @@
+package net.hyperic.sigar.test;
+
+import junit.framework.TestCase;
+
+import net.hyperic.sigar.Sigar;
+import net.hyperic.sigar.ProcState;
+
+public class TestProcState extends TestCase {
+
+ public TestProcState(String name) {
+ super(name);
+ }
+
+ public void testCreate() throws Exception {
+ Sigar sigar = new Sigar();
+
+ ProcState procState = sigar.getProcState(sigar.getPid());
+
+ assertTrue(procState.getState() == 'R');
+
+ assertTrue(procState.getName().indexOf("java") != -1);
+ }
+}
diff --git a/bindings/java/src/net/hyperic/sigar/test/TestProcTime.java b/bindings/java/src/net/hyperic/sigar/test/TestProcTime.java
new file mode 100644
index 00000000..937f2b47
--- /dev/null
+++ b/bindings/java/src/net/hyperic/sigar/test/TestProcTime.java
@@ -0,0 +1,25 @@
+package net.hyperic.sigar.test;
+
+import net.hyperic.sigar.Sigar;
+import net.hyperic.sigar.ProcTime;
+
+public class TestProcTime extends SigarTestCase {
+
+ public TestProcTime(String name) {
+ super(name);
+ }
+
+ public void testCreate() throws Exception {
+ Sigar sigar = new Sigar();
+
+ ProcTime procTime = sigar.getProcTime(sigar.getPid());
+
+ assertGtEqZeroTrace("StartTime", procTime.getStartTime());
+ //XXX
+ //assertTrue(procTime.getStartTime() < System.currentTimeMillis());
+
+ assertGtEqZeroTrace("Utime", procTime.getUtime());
+
+ assertGtEqZeroTrace("Stime", procTime.getStime());
+ }
+}
diff --git a/bindings/java/src/net/hyperic/sigar/test/TestSwap.java b/bindings/java/src/net/hyperic/sigar/test/TestSwap.java
new file mode 100644
index 00000000..bf755003
--- /dev/null
+++ b/bindings/java/src/net/hyperic/sigar/test/TestSwap.java
@@ -0,0 +1,25 @@
+package net.hyperic.sigar.test;
+
+import junit.framework.TestCase;
+
+import net.hyperic.sigar.Sigar;
+import net.hyperic.sigar.Swap;
+
+public class TestSwap extends TestCase {
+
+ public TestSwap(String name) {
+ super(name);
+ }
+
+ public void testCreate() throws Exception {
+ Sigar sigar = new Sigar();
+
+ Swap swap = sigar.getSwap();
+
+ assertTrue(swap.getTotal() >= 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
+ *
+ *- is not escaped protected by a matching % or is
+ * not an escape % character,
+ *
- is not at the end of the format string, and
+ *
- 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 n
th
+ * 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.
+ *
+ *Sequence |
+ * Name |
+ * Description |
+ *\\ | backlash | None.
+ * |
+ *\a | alert | Attempts to alert
+ * the user through audible or visible
+ * notification.
+ * |
+ *\b | backspace | Moves the
+ * printing position to one column before
+ * the current position, unless the
+ * current position is the start of a line.
+ * |
+ *\f | form-feed | Moves the
+ * printing position to the initial
+ * printing position of the next logical
+ * page.
+ * |
+ *\n | newline | Moves the
+ * printing position to the start of the
+ * next line.
+ * |
+ *\r | carriage-return | Moves
+ * the printing position to the start of
+ * the current line.
+ * |
+ *\t | tab | Moves the printing
+ * position to the next implementation-
+ * defined horizontal tab position.
+ * |
+ *\v | vertical-tab | Moves 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
+ *
+ *- is not escaped protected by a matching % or
+ * is not an escape % character,
+ *
- is not at the end of the format string, and
+ *
- 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\