diff --git a/CHANGES.md b/CHANGES.md
index b6afc0e9be..8c87007235 100755
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@ Release 4.2 (Next)
Features
--------
+* [#452](https://github.com/twall/jna/pull/452): Added Mac OS X System.B library at `com.sun.jna.platform.mac.SystemB` including support for `sysctl`, `sysctlbyname`, `sysctlnametomib`, `mach_host_self`, `host_page_size`, `host_statistics` and `host_statistics64` - [@dbwiddis](https://github.com/dbwiddis).
* [#446](https://github.com/twall/jna/pull/446): Added `com.sun.jna.platform.win32.Advapi32.GetNamedSecurityInfo`, `SetNamedSecurityInfo`, `GetSecurityDescriptorLength`, `IsValidSecurityDescriptor`, `IsValidAcl` - [@amarcionek](https://github.com/amarcionek).
* [#387](https://github.com/twall/jna/pull/397): Use of interfaces and annotations to provide easier implementation of COM interfaces (with `InvocationHandler`) - [@dhakehurst](https://github.com/dhakehurst).
* [#387](https://github.com/twall/jna/pull/397): Support for COM event callbacks - [@dhakehurst](https://github.com/dhakehurst).
diff --git a/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java b/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java
new file mode 100644
index 0000000000..14f180c0d1
--- /dev/null
+++ b/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2015 Daniel Widdis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see .
+ */
+
+package com.sun.jna.platform.mac;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+
+/**
+ * Author: Daniel Widdis Date: 6/5/15
+ */
+public interface SystemB extends Library {
+
+ public static SystemB INSTANCE = (SystemB) Native.loadLibrary("System",
+ SystemB.class);
+
+ // host_statistics()
+ static int HOST_LOAD_INFO = 1;// System loading stats
+ static int HOST_VM_INFO = 2; // Virtual memory stats
+ static int HOST_CPU_LOAD_INFO = 3;// CPU load stats
+
+ // host_statistics64()
+ static int HOST_VM_INFO64 = 4; // 64-bit virtual memory stats
+
+ // host_cpu_load_info()
+ static int CPU_STATE_MAX = 4;
+ static int CPU_STATE_USER = 0;
+ static int CPU_STATE_SYSTEM = 1;
+ static int CPU_STATE_IDLE = 2;
+ static int CPU_STATE_NICE = 3;
+
+ // Data size
+ static int UINT64_SIZE = Native.getNativeSize(long.class);
+ static int INT_SIZE = Native.getNativeSize(int.class);
+
+ public static class HostCpuLoadInfo extends Structure {
+ public int cpu_ticks[] = new int[CPU_STATE_MAX];
+
+ protected List getFieldOrder() {
+ return Arrays.asList(new String[] { "cpu_ticks" });
+ }
+ }
+
+ public static class HostLoadInfo extends Structure {
+ public int[] avenrun = new int[3]; // scaled by LOAD_SCALE
+ public int[] mach_factor = new int[3]; // scaled by LOAD_SCALE
+
+ protected List getFieldOrder() {
+ return Arrays.asList(new String[] { "avenrun", "mach_factor" });
+ }
+ }
+
+ public static class VMStatistics extends Structure {
+ public int free_count; // # of pages free
+ public int active_count; // # of pages active
+ public int inactive_count; // # of pages inactive
+ public int wire_count; // # of pages wired down
+ public int zero_fill_count; // # of zero fill pages
+ public int reactivations; // # of pages reactivated
+ public int pageins; // # of pageins
+ public int pageouts; // # of pageouts
+ public int faults; // # of faults
+ public int cow_faults; // # of copy-on-writes
+ public int lookups; // object cache lookups
+ public int hits; // object cache hits
+ public int purgeable_count; // # of pages purgeable
+ public int purges; // # of pages purged
+ // # of pages speculative (included in free_count)
+ public int speculative_count;
+
+ protected List getFieldOrder() {
+ return Arrays.asList(new String[] { "free_count", "active_count",
+ "inactive_count", "wire_count", "zero_fill_count",
+ "reactivations", "pageins", "pageouts", "faults",
+ "cow_faults", "lookups", "hits", "purgeable_count",
+ "purges", "speculative_count" });
+ }
+ }
+
+ public static class VMStatistics64 extends VMStatistics {
+ public long zero_fill_count; // # of zero fill pages
+ public long reactivations; // # of pages reactivated
+ public long pageins; // # of pageins
+ public long pageouts; // # of pageouts
+ public long faults; // # of faults
+ public long cow_faults; // # of copy-on-writes
+ public long lookups; // object cache lookups
+ public long hits; // object cache hits
+ public long purges; // # of pages purged
+ public long decompressions; // # of pages decompressed
+ public long compressions; // # of pages compressed
+ // # of pages swapped in (via compression segments)
+ public long swapins;
+ // # of pages swapped out (via compression segments)
+ public long swapouts;
+ // # of pages used by the compressed pager to hold all the
+ // compressed data
+ public int compressor_page_count;
+ public int throttled_count; // # of pages throttled
+ // # of pages that are file-backed (non-swap)
+ public int external_page_count;
+ public int internal_page_count; // # of pages that are anonymous
+ // # of pages (uncompressed) held within the compressor.
+ public long total_uncompressed_pages_in_compressor;
+
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList(new String[] { "free_count", "active_count",
+ "inactive_count", "wire_count", "zero_fill_count",
+ "reactivations", "pageins", "pageouts", "faults",
+ "cow_faults", "lookups", "hits", "purges",
+ "purgeable_count", "speculative_count", "decompressions",
+ "compressions", "swapins", "swapouts",
+ "compressor_page_count", "throttled_count",
+ "external_page_count", "internal_page_count",
+ "total_uncompressed_pages_in_compressor" });
+ }
+ }
+
+ /**
+ * The mach_host_self system call returns the calling thread's host name
+ * port. It has an effect equivalent to receiving a send right for the host
+ * port.
+ *
+ * @return the host's name port
+ */
+ int mach_host_self();
+
+ /**
+ * The host_page_size function returns the page size for the given host.
+ *
+ * @param machPort
+ * The name (or control) port for the host for which the page
+ * size is desired.
+ * @param pPageSize
+ * The host's page size (in bytes), set on success.
+ * @return 0 on success; sets errno on failure
+ */
+ int host_page_size(int machPort, LongByReference pPageSize);
+
+ /**
+ * The host_statistics function returns scheduling and virtual memory
+ * statistics concerning the host as specified by hostStat.
+ *
+ * @param machPort
+ * The control port for the host for which information is to be
+ * obtained.
+ * @param hostStat
+ * The type of statistics desired (HOST_LOAD_INFO, HOST_VM_INFO,
+ * or HOST_CPU_LOAD_INFO)
+ * @param stats
+ * Statistics about the specified host.
+ * @param count
+ * On input, the maximum size of the buffer; on output, the size
+ * returned (in natural-sized units).
+ * @return 0 on success; sets errno on failure
+ */
+ int host_statistics(int machPort, int hostStat, Structure stats,
+ IntByReference count);
+
+ /**
+ * The host_statistics64 function returns 64-bit virtual memory statistics
+ * concerning the host as specified by hostStat.
+ *
+ * @param machPort
+ * The control port for the host for which information is to be
+ * obtained.
+ * @param hostStat
+ * The type of statistics desired (HOST_VM_INFO64)
+ * @param stats
+ * Statistics about the specified host.
+ * @param count
+ * On input, the maximum size of the buffer; on output, the size
+ * returned (in natural-sized units).
+ * @return 0 on success; sets errno on failure
+ */
+ int host_statistics64(int machPort, int hostStat, Structure stats,
+ IntByReference count);
+
+ /**
+ * The sysctl() function retrieves system information and allows processes
+ * with appropriate privileges to set system information. The information
+ * available from sysctl() consists of integers, strings, and tables.
+ *
+ * The state is described using a "Management Information Base" (MIB) style
+ * name, listed in name, which is a namelen length array of integers.
+ *
+ * The information is copied into the buffer specified by oldp. The size of
+ * the buffer is given by the location specified by oldlenp before the call,
+ * and that location gives the amount of data copied after a successful call
+ * and after a call that returns with the error code ENOMEM. If the amount
+ * of data available is greater than the size of the buffer supplied, the
+ * call supplies as much data as fits in the buffer provided and returns
+ * with the error code ENOMEM. If the old value is not desired, oldp and
+ * oldlenp should be set to NULL.
+ *
+ * The size of the available data can be determined by calling sysctl() with
+ * the NULL argument for oldp. The size of the available data will be
+ * returned in the location pointed to by oldlenp. For some operations, the
+ * amount of space may change often. For these operations, the system
+ * attempts to round up so that the returned size is large enough for a call
+ * to return the data shortly thereafter.
+ *
+ * To set a new value, newp is set to point to a buffer of length newlen
+ * from which the requested value is to be taken. If a new value is not to
+ * be set, newp should be set to NULL and newlen set to 0.
+ *
+ * @param name
+ * MIB array of integers
+ * @param namelen
+ * length of the MIB array
+ * @param oldp
+ * Information retrieved
+ * @param oldlenp
+ * Size of information retrieved
+ * @param newp
+ * Information to be written
+ * @param newlen
+ * Size of information to be written
+ * @return 0 on success; sets errno on failure
+ */
+ int sysctl(int[] name, int namelen, Pointer oldp, IntByReference oldlenp,
+ Pointer newp, int newlen);
+
+ /**
+ * The sysctlbyname() function accepts an ASCII representation of the name
+ * and internally looks up the integer name vector. Apart from that, it
+ * behaves the same as the standard sysctl() function.
+ *
+ * @param name
+ * ASCII representation of the MIB name
+ * @param oldp
+ * Information retrieved
+ * @param oldlenp
+ * Size of information retrieved
+ * @param newp
+ * Information to be written
+ * @param newlen
+ * Size of information to be written
+ * @return 0 on success; sets errno on failure
+ */
+ int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp,
+ Pointer newp, int newlen);
+
+ /**
+ * The sysctlnametomib() function accepts an ASCII representation of the
+ * name, looks up the integer name vector, and returns the numeric
+ * representation in the mib array pointed to by mibp. The number of
+ * elements in the mib array is given by the location specified by sizep
+ * before the call, and that location gives the number of entries copied
+ * after a successful call. The resulting mib and size may be used in
+ * subsequent sysctl() calls to get the data associated with the requested
+ * ASCII name. This interface is intended for use by applications that want
+ * to repeatedly request the same variable (the sysctl() function runs in
+ * about a third the time as the same request made via the sysctlbyname()
+ * function).
+ *
+ * The number of elements in the mib array can be determined by calling
+ * sysctlnametomib() with the NULL argument for mibp.
+ *
+ * The sysctlnametomib() function is also useful for fetching mib prefixes.
+ * If size on input is greater than the number of elements written, the
+ * array still contains the additional elements which may be written
+ * programmatically.
+ *
+ * @param name
+ * ASCII representation of the name
+ * @param mibp
+ * Integer array containing the corresponding name vector.
+ * @param size
+ * On input, number of elements in the returned array; on output,
+ * the number of entries copied.
+ * @return 0 on success; sets errno on failure
+ */
+ int sysctlnametomib(String name, Pointer mibp, IntByReference size);
+}
diff --git a/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java b/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java
new file mode 100644
index 0000000000..3cb39cb210
--- /dev/null
+++ b/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 Daniel Widdis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see .
+ */
+package com.sun.jna.platform.mac;
+
+import junit.framework.TestCase;
+import oshi.software.os.mac.local.SystemB.HostCpuLoadInfo;
+import oshi.software.os.mac.local.SystemB.HostLoadInfo;
+import oshi.software.os.mac.local.SystemB.VMStatistics;
+import oshi.software.os.mac.local.SystemB.VMStatistics64;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Platform;
+import com.sun.jna.Pointer;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+
+/**
+ * Exercise the {@link SystemB} class.
+ *
+ * @author widdis@gmail.com
+ */
+// @SuppressWarnings("unused")
+public class SystemBTest extends TestCase {
+
+ public void testSysctl() {
+ final String mibName = "hw.logicalcpu";
+ final int nCpu = Runtime.getRuntime().availableProcessors();
+
+ IntByReference size = new IntByReference(SystemB.INT_SIZE);
+ Pointer p = new Memory(size.getValue());
+ int ret = SystemB.INSTANCE.sysctlbyname(mibName, p, size, null, 0);
+ assertEquals(ret, 0);
+ // These values should be equal unless affinity is set, limiting nCpu
+ assertTrue(p.getInt(0) >= nCpu);
+
+ size = new IntByReference();
+ ret = SystemB.INSTANCE.sysctlnametomib(mibName, null, size);
+ assertEquals(ret, 0);
+ // Size should be 2
+ assertEquals(size.getValue(), 2);
+
+ Pointer mibp = new Memory(size.getValue() * SystemB.INT_SIZE);
+ ret = SystemB.INSTANCE.sysctlnametomib(mibName, mibp, size);
+ assertEquals(ret, 0);
+ // Size should be 2
+ assertEquals(size.getValue(), 2);
+
+ int[] mib = mibp.getIntArray(0, size.getValue());
+ // mib should be { 6, 103(?) }
+ assertEquals(mib.length, 2);
+ assertEquals(mib[0], 6);
+
+ size = new IntByReference(SystemB.INT_SIZE);
+ p = new Memory(size.getValue());
+ ret = SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0);
+ assertTrue(p.getInt(0) >= nCpu);
+ };
+
+ public void testHostPageSize() {
+ int machPort = SystemB.INSTANCE.mach_host_self();
+ assertTrue(machPort > 0);
+
+ LongByReference pPageSize = new LongByReference();
+ int ret = SystemB.INSTANCE.host_page_size(machPort, pPageSize);
+ assertEquals(ret, 0);
+ // Probably 4096, definitely a power of 2
+ assertTrue(pPageSize.getValue() > 0);
+ assertEquals(pPageSize.getValue() & (pPageSize.getValue() - 1), 0);
+ }
+
+ public void testVMInfo() {
+ int machPort = SystemB.INSTANCE.mach_host_self();
+ assertTrue(machPort > 0);
+
+ VMStatistics vmStats = new VMStatistics();
+ int ret = SystemB.INSTANCE.host_statistics(machPort,
+ SystemB.HOST_VM_INFO, vmStats,
+ new IntByReference(vmStats.size() / SystemB.INT_SIZE));
+ assertEquals(ret, 0);
+ // Nonnegative
+ assertTrue(vmStats.free_count >= 0);
+
+ if (Platform.is64Bit()) {
+ VMStatistics64 vmStats64 = new VMStatistics64();
+ ret = SystemB.INSTANCE.host_statistics64(machPort,
+ SystemB.HOST_VM_INFO, vmStats64, new IntByReference(
+ vmStats64.size() / SystemB.INT_SIZE));
+ assertEquals(ret, 0);
+ // Nonnegative
+ assertTrue(vmStats64.free_count >= 0);
+ }
+ }
+
+ public void testCpuLoad() {
+ int machPort = SystemB.INSTANCE.mach_host_self();
+ assertTrue(machPort > 0);
+
+ HostCpuLoadInfo cpuLoadInfo = new HostCpuLoadInfo();
+ int ret = SystemB.INSTANCE.host_statistics(machPort,
+ SystemB.HOST_CPU_LOAD_INFO, cpuLoadInfo, new IntByReference(
+ cpuLoadInfo.size()));
+ assertEquals(ret, 0);
+ // Should be int[4]
+ assertEquals(cpuLoadInfo.cpu_ticks.length, SystemB.CPU_STATE_MAX);
+ }
+
+ public void testHostLoad() {
+ int machPort = SystemB.INSTANCE.mach_host_self();
+ assertTrue(machPort > 0);
+
+ HostLoadInfo hostLoadInfo = new HostLoadInfo();
+ int ret = SystemB.INSTANCE.host_statistics(machPort,
+ SystemB.HOST_CPU_LOAD_INFO, hostLoadInfo, new IntByReference(
+ hostLoadInfo.size()));
+ assertEquals(ret, 0);
+ // Should be two int[3]'s
+ assertEquals(hostLoadInfo.avenrun.length, 3);
+ assertEquals(hostLoadInfo.mach_factor.length, 3);
+ // Load factor can't be zero
+ assertTrue(hostLoadInfo.avenrun[0] > 0);
+ }
+
+ public static void main(java.lang.String[] argList) {
+ junit.textui.TestRunner.run(SystemBTest.class);
+ }
+
+}