diff --git a/CHANGES.md b/CHANGES.md index 5ab729b844..1dcae5f3eb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,11 +2,12 @@ NOTE: as of JNA 4.0, JNA is now dual-licensed under LGPL and AL 2.0 (see LICENSE NOTE: JNI native support is typically incompatible between minor versions, and almost always incompatible between major versions. -Next Release (5.4.1) +Next Release (5.5.0) ==================== Features -------- +* [#1131](https://github.com/java-native-access/jna/pull/1131): Add CoreFoundation, IOKit, and DiskArbitration mappings in `c.s.j.p.mac`. [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java b/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java new file mode 100644 index 0000000000..c456781eb5 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import com.sun.jna.ptr.ByReference; +import com.sun.jna.ptr.ByteByReference; +import com.sun.jna.ptr.DoubleByReference; +import com.sun.jna.ptr.FloatByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.ptr.ShortByReference; + +/** + * Core Foundation is a framework that provides fundamental software services + * useful to application services, application environments, and to applications + * themselves. Core Foundation also provides abstractions for common data types. + *

+ * Core Foundation functions have names that indicate when you own a returned + * object: Object-creation functions have “Create” embedded in the name, and + * Object-duplication functions that have “Copy” embedded in the name. If you + * own an object, it is your responsibility to relinquish ownership (using + * {@link #CFRelease}) when you have finished with it. + *

+ * If you receive an object from any Core Foundation function other than a + * creation or copy function—such as a Get function—you do not own it and cannot + * be certain of the object’s life span. If you want to ensure that such an + * object is not disposed of while you are using it, you must claim ownership + * (with the {@link #CFRetain} function). + */ +public interface CoreFoundation extends Library { + + CoreFoundation INSTANCE = Native.load("CoreFoundation", CoreFoundation.class); + + int kCFNotFound = -1; + + int kCFStringEncodingASCII = 0x0600; + int kCFStringEncodingUTF8 = 0x08000100; + + CFTypeID ARRAY_TYPE_ID = INSTANCE.CFArrayGetTypeID(); + CFTypeID BOOLEAN_TYPE_ID = INSTANCE.CFBooleanGetTypeID(); + CFTypeID DATA_TYPE_ID = INSTANCE.CFDataGetTypeID(); + CFTypeID DATE_TYPE_ID = INSTANCE.CFDateGetTypeID(); + CFTypeID DICTIONARY_TYPE_ID = INSTANCE.CFDictionaryGetTypeID(); + CFTypeID NUMBER_TYPE_ID = INSTANCE.CFNumberGetTypeID(); + CFTypeID STRING_TYPE_ID = INSTANCE.CFStringGetTypeID(); + + /** + * The {@code CFTypeRef} type is the base type defined in Core Foundation. It is + * used as the type and return value in several polymorphic functions. It is a + * generic object reference that acts as a placeholder for other true Core + * Foundation objects. + */ + class CFTypeRef extends PointerType { + public CFTypeRef() { + super(); + } + + public CFTypeRef(Pointer p) { + super(p); + } + + /** + * Convenience method for {@link CoreFoundation#CFGetTypeID} on this object. + * + * @return The {@link CFTypeID} + */ + public CFTypeID getTypeID() { + if (this.getPointer() == null) { + return new CFTypeID(0); + } + return INSTANCE.CFGetTypeID(this); + } + + /** + * Test whether this object has the specified ID + * + * @param typeID + * The {@link CFTypeID} for the class to test + * @return true if this object has the same ID as {@code typeID} + */ + public boolean isTypeID(CFTypeID typeID) { + return getTypeID().equals(typeID); + } + + /** + * Convenience method for {@link CoreFoundation#CFRetain} on this object. + */ + public void retain() { + INSTANCE.CFRetain(this); + } + + /** + * Convenience method for {@link CoreFoundation#CFRelease} on this object. + */ + public void release() { + INSTANCE.CFRelease(this); + } + } + + /** + * A reference type used in many Core Foundation parameters and function + * results. It refers to a {@code CFAllocator} object, which allocates, + * reallocates, and deallocates memory for Core Foundation objects. + */ + class CFAllocatorRef extends CFTypeRef { + } + + /** + * A reference to a {@code CFNumber} object. + */ + class CFNumberRef extends CFTypeRef { + public CFNumberRef() { + super(); + } + + public CFNumberRef(Pointer p) { + super(p); + if (!isTypeID(NUMBER_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFNumber. Type ID: " + getTypeID()); + } + } + + /** + * Convert this {@code CFNumber} to a {@code long}. + *

+ * This method assumes a 64-bit integer is stored and does not do type checking. + * Users should use {@link #CFNumberGetType} to determine the appropriate type + * conversion. If this object's type differs from the return type, and the + * conversion is lossy or the return value is out of range, then this method + * returns an approximate value. + * + * @return The corresponding {@code long} + */ + public long longValue() { + LongByReference lbr = new LongByReference(); + INSTANCE.CFNumberGetValue(this, CFNumberType.kCFNumberLongLongType.typeIndex(), lbr); + return lbr.getValue(); + } + + /** + * Convert this {@code CFNumber} to an {@code int}. + *

+ * This method assumes a 32-bit integer is stored and does not do type checking. + * Users should use {@link #CFNumberGetType} to determine the appropriate type + * conversion. If this object's type differs from the return type, and the + * conversion is lossy or the return value is out of range, then this method + * returns an approximate value. + * + * @return The corresponding {@code int} + */ + public int intValue() { + IntByReference ibr = new IntByReference(); + INSTANCE.CFNumberGetValue(this, CFNumberType.kCFNumberIntType.typeIndex(), ibr); + return ibr.getValue(); + } + + /** + * Convert this {@code CFNumber} to a {@code short}. + *

+ * This method assumes a 16-bit integer is stored and does not do type checking. + * Users should use {@link #CFNumberGetType} to determine the appropriate type + * conversion. If this object's type differs from the return type, and the + * conversion is lossy or the return value is out of range, then this method + * returns an approximate value. + * + * @return The corresponding {@code short} + */ + public short shortValue() { + ShortByReference sbr = new ShortByReference(); + INSTANCE.CFNumberGetValue(this, CFNumberType.kCFNumberShortType.typeIndex(), sbr); + return sbr.getValue(); + } + + /** + * Convert this {@code CFNumber} to a {@code byte}. + *

+ * This method assumes an 8-bit integer is stored and does not do type checking. + * Users should use {@link #CFNumberGetType} to determine the appropriate type + * conversion. If this object's type differs from the return type, and the + * conversion is lossy or the return value is out of range, then this method + * returns an approximate value. + * + * @return The corresponding {@code byte} + */ + public byte byteValue() { + ByteByReference bbr = new ByteByReference(); + INSTANCE.CFNumberGetValue(this, CFNumberType.kCFNumberCharType.typeIndex(), bbr); + return bbr.getValue(); + } + + /** + * Convert this {@code CFNumber} to a {@code double}. + *

+ * This method assumes a 64-bit floating point value is stored and does not do + * type checking. Users should use {@link #CFNumberGetType} to determine the + * appropriate type conversion. If this object's type differs from the return + * type, and the conversion is lossy or the return value is out of range, then + * this method returns an approximate value. + * + * @return The corresponding {@code double} + */ + public double doubleValue() { + DoubleByReference dbr = new DoubleByReference(); + INSTANCE.CFNumberGetValue(this, CFNumberType.kCFNumberDoubleType.typeIndex(), dbr); + return dbr.getValue(); + } + + /** + * Convert this {@code CFNumber} to a {@code float}. + *

+ * This method assumes a 32-bit floating point value is stored and does not do + * type checking. Users should use {@link #CFNumberGetType} to determine the + * appropriate type conversion. If this object's type differs from the return + * type, and the conversion is lossy or the return value is out of range, then + * this method returns an approximate value. + * + * @return The corresponding {@code float} + */ + public float floatValue() { + FloatByReference fbr = new FloatByReference(); + INSTANCE.CFNumberGetValue(this, CFNumberType.kCFNumberFloatType.typeIndex(), fbr); + return fbr.getValue(); + } + } + + /** + * Enum of values used for {@link CFNumberType} in {@link #CFNumberGetValue} and + * {@link #CFNumberGetType}. Use {@link CFNumberType#typeIndex} for the expected + * integer value corresponding to the C-style enum. + */ + enum CFNumberType { + unusedZero, kCFNumberSInt8Type, kCFNumberSInt16Type, kCFNumberSInt32Type, kCFNumberSInt64Type, + kCFNumberFloat32Type, kCFNumberFloat64Type, kCFNumberCharType, kCFNumberShortType, kCFNumberIntType, + kCFNumberLongType, kCFNumberLongLongType, kCFNumberFloatType, kCFNumberDoubleType, kCFNumberCFIndexType, + kCFNumberNSIntegerType, kCFNumberCGFloatType, kCFNumberMaxType; + + /** + * Index for the type of {@link CFNumberRef} stored. + * + * @return a {@link CFIndex} representing the enum ordinal. + */ + public CFIndex typeIndex() { + return new CFIndex(this.ordinal()); + } + } + + /** + * A reference to a {@code CFBoolean} object. + */ + class CFBooleanRef extends CFTypeRef { + public CFBooleanRef() { + super(); + } + + public CFBooleanRef(Pointer p) { + super(p); + if (!isTypeID(BOOLEAN_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFBoolean. Type ID: " + getTypeID()); + } + } + + /** + * Convert a reference to a Core Foundations Boolean into its {@code boolean} + * + * @return The corresponding {@code boolean} + */ + public boolean booleanValue() { + return 0 != INSTANCE.CFBooleanGetValue(this); + } + } + + /** + * A reference to an immutable {@code CFArray} object. + *

+ * CFArray is “toll-free bridged” with its Cocoa Foundation counterpart, + * {@code NSArray}. Therefore, in a method where you see an {@code NSArray *} + * parameter, you can pass in a {@link #CFArrayRef}. + */ + class CFArrayRef extends CFTypeRef { + public CFArrayRef() { + super(); + } + + public CFArrayRef(Pointer p) { + super(p); + if (!isTypeID(ARRAY_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFArray. Type ID: " + getTypeID()); + } + } + + /** + * Convenience method for {@link #CFArrayGetCount} on this object + * + * @return The number of values in this array. + */ + public int getCount() { + return INSTANCE.CFArrayGetCount(this).intValue(); + } + + /** + * Convenience method for {@link #CFArrayGetValueAtIndex} on this object + * + * @param idx + * The index of the value to retrieve. + * @return The value at the {@code idx} index. + */ + public Pointer getValueAtIndex(int idx) { + return INSTANCE.CFArrayGetValueAtIndex(this, new CFIndex(idx)); + } + } + + /** + * A reference to an immutable {@code CFData} object. + */ + class CFDataRef extends CFTypeRef { + public CFDataRef() { + super(); + } + + public CFDataRef(Pointer p) { + super(p); + if (!isTypeID(DATA_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFData. Type ID: " + getTypeID()); + } + } + + /** + * Convenience method for {@link #CFDataGetLength} on this object + * + * @return An index that specifies the number of bytes associated with this + * object. + */ + public int getLength() { + return INSTANCE.CFDataGetLength(this).intValue(); + } + + /** + * Convenience method for {@link #CFDataGetBytePtr} on this object + * + * @return A read-only pointer to the bytes associated with this object. + */ + public Pointer getBytePtr() { + return INSTANCE.CFDataGetBytePtr(this); + } + } + + /** + * A reference to an immutable {@code CFDictionary} object. + */ + class CFDictionaryRef extends CFTypeRef { + public CFDictionaryRef() { + super(); + } + + public CFDictionaryRef(Pointer p) { + super(p); + if (!isTypeID(DICTIONARY_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFDictionary. Type ID: " + getTypeID()); + } + } + + /** + * Convenience method for {@link CoreFoundation#CFDictionaryGetValue} on this + * object. + * + * @param key + * The key for which to find a match. + * @return The value associated with key, or {@code null} if no key-value pair + * matching key exists. + */ + public Pointer getValue(PointerType key) { + return INSTANCE.CFDictionaryGetValue(this, key); + } + + /** + * Convenience method for + * {@link CoreFoundation#CFDictionaryGetValueIfPresent} on this object. + * + * @param key + * The key for which to find a match. + * @param value + * A pointer to memory which, on return, is filled with the + * pointer-sized value if a matching key is found. + * @return {@code true} if a matching key was found, otherwise + * {@code false} + */ + public boolean getValueIfPresent(PointerType key, PointerByReference value) { + return INSTANCE.CFDictionaryGetValueIfPresent(this, key, value) > 0; + } + } + + /** + * A reference to a mutable {@code CFDictionary} object. + */ + class CFMutableDictionaryRef extends CFDictionaryRef { + public CFMutableDictionaryRef() { + super(); + } + + public CFMutableDictionaryRef(Pointer p) { + super(p); + } + + /** + * Convenience method for {@link CoreFoundation#CFDictionarySetValue} on this + * object. + * + * @param theDict + * The dictionary to modify. + * @param key + * The key of the value to set. + * @param value + * The value to add to or replace . + */ + public void setValue(PointerType key, PointerType value) { + INSTANCE.CFDictionarySetValue(this, key, value); + } + } + + /** + * A reference to a {@code CFString} object, which “encapsulates” a Unicode + * string along with its length. {@code CFString} is an opaque type that defines + * the characteristics and behavior of {@code CFString} objects. + */ + class CFStringRef extends CFTypeRef { + public CFStringRef() { + super(); + } + + public CFStringRef(Pointer p) { + super(p); + if (!isTypeID(STRING_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFString. Type ID: " + getTypeID()); + } + } + + /** + * Convenience function which calls {@link #CFStringCreateWithCharacters} to + * create a new {@link CFStringRef} from the given Java {@link java.lang.String} + * and returns its reference pointer. + *

+ * This reference must be released with {@link #CFRelease} to avoid leaking + * references. + * + * @param s + * A {@link java.lang.String}. + * @return An immutable string containing {@code s}, or {@code null} if there + * was a problem creating the object. + */ + public static CFStringRef createCFString(String s) { + final char[] chars = s.toCharArray(); + return INSTANCE.CFStringCreateWithCharacters(null, chars, new CFIndex(chars.length)); + } + + /** + * Convert a reference to a Core Foundations String into its + * {@link java.lang.String} + * + * @return The corresponding {@link java.lang.String}, or null if the conversion + * failed. + */ + public String stringValue() { + CFIndex length = INSTANCE.CFStringGetLength(this); + CFIndex maxSize = INSTANCE.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + if (maxSize.intValue() == kCFNotFound) { + return null; + } + Memory buf = new Memory(maxSize.longValue()); + if (0 != INSTANCE.CFStringGetCString(this, buf, maxSize, kCFStringEncodingUTF8)) { + return buf.getString(0, "UTF8"); + } + return null; + } + } + + /** + * A wrapper for the {@link NativeLong} type, used for {@link CFNumberRef} + * types, {@link CFStringRef} lengths, and {@link CFArrayRef} sizes and indices. + */ + class CFIndex extends NativeLong { + private static final long serialVersionUID = 1L; + + public CFIndex() { + super(); + } + + public CFIndex(long value) { + super(value); + } + } + + /** + * A type for unique, constant integer values that identify particular Core + * Foundation opaque types. + *

+ * Because the value for a type ID can change from release to release, your code + * should not rely on stored or hard-coded type IDs nor should it hard-code any + * observed properties of a type ID (such as, for example, it being a small + * integer). + */ + class CFTypeID extends NativeLong { + private static final long serialVersionUID = 1L; + + public CFTypeID() { + super(); + } + + public CFTypeID(long value) { + super(value); + } + + @Override + public String toString() { + if (this.equals(ARRAY_TYPE_ID)) { + return "CFArray"; + } else if (this.equals(BOOLEAN_TYPE_ID)) { + return "CFBoolean"; + } else if (this.equals(DATA_TYPE_ID)) { + return "CFData"; + } else if (this.equals(DATE_TYPE_ID)) { + return "CFDate"; + } else if (this.equals(DICTIONARY_TYPE_ID)) { + return "CFDictionary"; + } else if (this.equals(NUMBER_TYPE_ID)) { + return "CFNumber"; + } else if (this.equals(STRING_TYPE_ID)) { + return "CFString"; + } else { + return super.toString(); + } + } + } + + /** + * Creates a string from a buffer of Unicode characters. + *

+ * This reference must be released with {@link #CFRelease} to avoid leaking + * references. + * + * @param alloc + * The allocator to use to allocate memory for the new string. Pass + * {@code null} or {@code kCFAllocatorDefault} to use the current + * default allocator. + * @param chars + * The buffer of Unicode characters to copy into the new string. + * @param length + * The number of characters in the buffer pointed to by chars. Only + * this number of characters will be copied to internal storage. + * @return An immutable string containing {@code chars}, or {@code null} if + * there was a problem creating the object. + */ + CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, char[] chars, CFIndex length); + + /** + * Creates a {@code CFNumber} object using a specified value. + *

+ * This reference must be released with {@link #CFRelease} to avoid leaking + * references. + * + * @param alloc + * The allocator to use to allocate memory for the new object. Pass + * {@code null} or {@code kCFAllocatorDefault} to use the current + * default allocator. + * @param theType + * A constant that specifies the data type of the value to convert. + * The ordinal value of the enum. + *

+ * The {@code theType} parameter is not necessarily preserved when + * creating a new {@code CFNumber} object. The {@code CFNumber} + * object will be created using whatever internal storage type the + * creation function deems appropriate. Use the function + * {@link #CFNumberGetType} to find out what type the + * {@code CFNumber} object used to store your value. + * @param valuePtr + * A pointer to the value for the returned number object. + * @return A new number with the value specified by {@code valuePtr}. + */ + CFNumberRef CFNumberCreate(CFAllocatorRef alloc, CFIndex theType, ByReference valuePtr); + + /** + * Creates a new immutable array with the given values. + *

+ * This reference must be released with {@link #CFRelease} to avoid leaking + * references. + * + * @param alloc + * The allocator to use to allocate memory for the new array and its + * storage for values. Pass {@code null} or + * {@code kCFAllocatorDefault} to use the current default allocator. + * @param values + * A C array of the pointer-sized values to be in the new array. The + * values in the new array are ordered in the same order in which + * they appear in this C array. This value may be {@code null} if + * {@code numValues} is 0. This C array is not changed or freed by + * this function. If {@code values} is not a valid pointer to a C + * array of at least {@code numValues} elements, the behavior is + * undefined. + * @param numValues + * The number of values to copy from the {@code values} C array into + * the new array. This number will be the count of the new array—it + * must not be negative or greater than the number of elements in + * values. + * @param callBacks + * A pointer to a {@code CFArrayCallBacks} structure initialized with + * the callbacks for the array to use on each value in the + * collection. The retain callback is used within this function, for + * example, to retain all of the new values from the {@code values} C + * array. A copy of the contents of the callbacks structure is made, + * so that a pointer to a structure on the stack can be passed in or + * can be reused for multiple collection creations. + *

+ * This value may be {@code null}, which is treated as if a valid + * structure of version 0 with all fields {@code null} had been + * passed in. + * @return A new immutable array containing {@code numValues} from + * {@code values}, or {@code null} if there was a problem creating the + * object. + */ + CFArrayRef CFArrayCreate(CFAllocatorRef alloc, Pointer values, CFIndex numValues, Pointer callBacks); + + /** + * Creates an immutable {@code CFData} object using data copied from a specified + * byte buffer. + *

+ * This reference must be released with {@link #CFRelease} to avoid leaking + * references. + * + * @param alloc + * The allocator to use to allocate memory for the new object. Pass + * {@code null} or {@code kCFAllocatorDefault} to use the current + * default allocator. + * @param bytes + * A pointer to the byte buffer that contains the raw data to be + * copied into the Data. + * @param length + * The number of bytes in the buffer ({@code bytes}). + * @return A new {@code CFData} object, or {@code null} if there was a problem + * creating the object. + */ + CFDataRef CFDataCreate(CFAllocatorRef alloc, Pointer bytes, CFIndex length); + + /** + * Creates a new mutable dictionary. + *

+ * This reference must be released with {@link #CFRelease} to avoid leaking + * references. + * + * @param alloc + * The allocator to use to allocate memory for the new string. Pass + * {@code null} or {@code kCFAllocatorDefault} to use the current + * default allocator. + * @param capacity + * The maximum number of key-value pairs that can be contained by the + * new dictionary. The dictionary starts empty and can grow to this + * number of key-value pairs (and it can have less). + *

+ * Pass 0 to specify that the maximum capacity is not limited. The + * value must not be negative. + * @param keyCallBacks + * A pointer to a {@code CFDictionaryKeyCallBacks} structure + * initialized with the callbacks to use to retain, release, + * describe, and compare keys in the dictionary. A copy of the + * contents of the callbacks structure is made, so that a pointer to + * a structure on the stack can be passed in or can be reused for + * multiple collection creations. + *

+ * This value may be {@code null}, which is treated as a valid + * structure of version 0 with all fields {@code null}. + * @param valueCallBacks + * A pointer to a {@code CFDictionaryValueCallBacks} structure + * initialized with the callbacks to use to retain, release, + * describe, and compare values in the dictionary. A copy of the + * contents of the callbacks structure is made, so that a pointer to + * a structure on the stack can be passed in or can be reused for + * multiple collection creations. + *

+ * This value may be {@code null}, which is treated as a valid + * structure of version 0 with all fields {@code null}. + * @return A new dictionary, or {@code null} if there was a problem creating the + * object. + */ + CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef alloc, CFIndex capacity, Pointer keyCallBacks, + Pointer valueCallBacks); + + /** + * Returns a textual description of a Core Foundation object. + *

+ * The nature of the description differs by object. For example, a description + * of a CFArray object would include descriptions of each of the elements in the + * collection. + *

+ * You can use this function for debugging Core Foundation objects in your code. + * Note, however, that the description for a given object may be different in + * different releases of the operating system. Do not create dependencies in + * your code on the content or format of the information returned by this + * function. + * + * @param cf + * The {@code CFType} object (a generic reference of type + * {@code CFTypeRef}) from which to derive a description. + * @return A string that contains a description of {@code cf}. + */ + CFStringRef CFCopyDescription(CFTypeRef cf); + + /** + * Releases a Core Foundation object. + *

+ * If the retain count of {@code cf} becomes zero the memory allocated to the + * object is deallocated and the object is destroyed. If you create, copy, or + * explicitly retain (see the {@link #CFRetain} function) a Core Foundation + * object, you are responsible for releasing it when you no longer need it. + * + * @param cf + * A {@code CFType} object to release. This value must not be + * {@code null}. + */ + void CFRelease(CFTypeRef cf); + + /** + * Retains a Core Foundation object. You should retain a Core Foundation object + * when you receive it from elsewhere (that is, you did not create or copy it) + * and you want it to persist. + *

+ * If you retain a Core Foundation object you are responsible for releasing it + * with {@link #CFRelease}. + * + * @param cf + * The {@code CFType} object to retain. This value must not be + * {@code null}. + * @return The input value, {code cf}. + */ + CFTypeRef CFRetain(CFTypeRef cf); + + /** + * Returns the reference count of a Core Foundation object. + * + * @param cf + * The {@code CFType} object to examine. + * @return A number representing the reference count of {code cf}. + */ + CFIndex CFGetRetainCount(CFTypeRef cf); + + /** + * Returns the value associated with a given key. + * + * @param theDict + * The dictionary to examine. + * @param key + * The key for which to find a match in {@code theDict}. The key hash + * and equal callbacks provided when the dictionary was created are + * used to compare. If the hash callback was {@code null}, the key is + * treated as a pointer and converted to an integer. If the equal + * callback was {@code null}, pointer equality (in C, ==) is used. If + * {@code key}, or any of the keys in {@code theDict}, is not + * understood by the equal callback, the behavior is undefined. + * @return The value associated with key in {@code theDict}, or {@code null} if + * no key-value pair matching key exists. Since {@code null} is also a + * valid value in some dictionaries, use + * {@link #CFDictionaryGetValueIfPresent} to distinguish between a value + * that is not found, and a {@code null} value. + */ + Pointer CFDictionaryGetValue(CFDictionaryRef theDict, PointerType key); + + /** + * Returns a boolean value that indicates whether a given value for a given key + * is in a dictionary, and returns that value indirectly if it exists. + * + * @param theDict + * The dictionary to examine. + * @param key + * The key for which to find a match in {@code theDict}. The key hash + * and equal callbacks provided when the dictionary was created are + * used to compare. If the hash callback was {@code null}, the key is + * treated as a pointer and converted to an integer. If the equal + * callback was {@code null}, pointer equality (in C, ==) is used. If + * {@code key}, or any of the keys in {@code theDict}, is not + * understood by the equal callback, the behavior is undefined. + * @param value + * A pointer to memory which, on return, is filled with the + * pointer-sized value if a matching key is found. If no key match is + * found, the contents of the storage pointed to by this parameter + * are undefined. This value may be {@code null}, in which case the + * value from the dictionary is not returned (but the return value of + * this function still indicates whether or not the key-value pair + * was present). + * @return 1 if a matching key was found, otherwise 0. + */ + byte CFDictionaryGetValueIfPresent(CFDictionaryRef theDict, PointerType key, PointerByReference value); + + /** + * Sets the value corresponding to a given key. + * + * @param theDict + * The dictionary to modify. If this parameter is a fixed-capacity + * dictionary and it is full before this operation, and the key does + * not exist in the dictionary, the behavior is undefined. + * @param key + * The key of the value to set in {@code theDict}. If a key which + * matches {@code key} is already present in the dictionary, only the + * value for the key is changed ("add if absent, replace if + * present"). If no key matches {@code key}, the key-value pair is + * added to the dictionary. + *

+ * If a key-value pair is added, both key and value are retained by + * the dictionary, using the retain callback provided when + * {@code theDict} was created. {@code key} must be of the type + * expected by the key retain callback. + * @param value + * The value to add to or replace in {@code theDict}. {@code value} + * is retained using the value retain callback provided when + * {@code theDict} was created, and the previous value if any is + * released. {@code value} must be of the type expected by the retain + * and release callbacks. + */ + void CFDictionarySetValue(CFMutableDictionaryRef theDict, PointerType key, PointerType value); + + /** + * Copies the character contents of a string to a local C string buffer after + * converting the characters to a given encoding. + * + * @param theString + * The string whose contents you wish to access. + * @param bufferToFill + * The C string buffer into which to copy the string. On return, the + * buffer contains the converted characters. If there is an error in + * conversion, the buffer contains only partial results. + *

+ * The buffer must be large enough to contain the converted + * characters and a NUL terminator. + * @param bufferSize + * The length of {@code buffer} in bytes. + * @param encoding + * The string encoding to which the character contents of + * {@code theString} should be converted. The encoding must specify + * an 8-bit encoding. + * @return 1 upon success or 0 if the conversion fails or the provided buffer is + * too small. + */ + byte CFStringGetCString(CFStringRef theString, Pointer bufferToFill, CFIndex bufferSize, int encoding); + + /** + * Returns the value of a {@code CFBoolean} object. + * + * @param bool + * The boolean to examine. + * @return 1 if the value of {@code bool} is {@code true}, 0 otherwise. + */ + byte CFBooleanGetValue(CFBooleanRef bool); + + /** + * Returns the number of values currently in an array. + * + * @param theArray + * a {@link CFArrayRef} object. + * @return The number of values in {@code array}. + */ + CFIndex CFArrayGetCount(CFArrayRef theArray); + + /** + * Retrieves a value at a given index. + * + * @param theArray + * The array to examine. + * @param idx + * The index of the value to retrieve. If the index is outside the + * index space of {@code theArray} (0 to N-1 inclusive (where N is + * the count of {@code theArray})), the behavior is undefined. + * @return The value at the {@code idx} index in {@code theArray}). + */ + Pointer CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx); + + /** + * Returns the type used by a {@code CFNumber} object to store its value. + * + * @param number + * The {@code CFNumber} object to examine. + * @return A constant that indicates the data type of the value contained in + * number. See {@link CFNumberType} for a list of possible values. + */ + CFIndex CFNumberGetType(CFNumberRef number); + + /** + * Obtains the value of a {@code CFNumber} object cast to a specified type. + * + * @param number + * The {@code CFNumber} object to examine. + * @param theType + * A constant that specifies the data type to return. See + * {@link CFNumberType} for a list of possible values. + * @param valuePtr + * On return, contains the value of {@code number}. + * @return 1 if the operation was successful, otherwise 0. + */ + byte CFNumberGetValue(CFNumberRef number, CFIndex theType, ByReference valuePtr); + + /** + * Returns the number (in terms of UTF-16 code pairs) of Unicode characters in a + * string. + * + * @param theString + * The string to examine. + * @return The number (in terms of UTF-16 code pairs) of characters stored in + * {@code theString}. + */ + CFIndex CFStringGetLength(CFStringRef theString); + + /** + * Returns the maximum number of bytes a string of a specified length (in + * Unicode characters) will take up if encoded in a specified encoding. + * + * @param length + * The number of Unicode characters to evaluate. + * @param encoding + * The string encoding for the number of characters specified by + * length. + * @return The maximum number of bytes that could be needed to represent length + * number of Unicode characters with the string encoding encoding, or + * {@link #kCFNotFound} if the number exceeds {@link Long#MAX_VALUE}. + */ + CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, int encoding); + + /** + * Gets the default allocator object for the current thread. + * + * @return A reference to the default allocator for the current thread. If none + * has been explicitly set, returns the generic system allocator. + *

+ * The default allocator can never be released, so it is not necessary + * to {@link #CFRetain} this reference. + */ + CFAllocatorRef CFAllocatorGetDefault(); + + /** + * Returns the number of bytes contained by a {@code CFData} object. + * + * @param theData + * The {@code CFData} object to examine. + * @return An index that specifies the number of bytes in {@code theData}. + */ + CFIndex CFDataGetLength(CFDataRef theData); + + /** + * Returns a read-only pointer to the bytes of a {@code CFData} object. + * + * @param theData + * The {@code CFData} object to examine. + * @return A read-only pointer to the bytes associated with {@code theData}. + */ + Pointer CFDataGetBytePtr(CFDataRef theData); + + /** + * Returns the type of a {@code CFType} object. + * + * @param theObject + * The {@code CFData} object to examine. + * @return A value of type {@link CFTypeID} that identifies the opaque type of + * {@code cf}. + */ + CFTypeID CFGetTypeID(CFTypeRef theObject); + + /** + * @return The type identifier for the {@code CFArray} opaque type. + */ + CFTypeID CFArrayGetTypeID(); + + /** + * @return The type identifier for the {@code CFBoolean} opaque type. + */ + CFTypeID CFBooleanGetTypeID(); + + /** + * @return The type identifier for the {@code CFDate} opaque type. + */ + CFTypeID CFDateGetTypeID(); + + /** + * @return The type identifier for the {@code CFData} opaque type. + *

+ * {@code CFMutableData} objects have the same type identifier as + * {@code CFData} objects. + */ + CFTypeID CFDataGetTypeID(); + + /** + * @return The type identifier for the {@code CFDictionary} opaque type. + *

+ * {@code CFMutableDictionary} objects have the same type identifier as + * {@code CFDictionary} objects. + */ + CFTypeID CFDictionaryGetTypeID(); + + /** + * @return The type identifier for the {@code CFNumber} opaque type. + */ + CFTypeID CFNumberGetTypeID(); + + /** + * @return The type identifier for the {@code CFString} opaque type. + */ + CFTypeID CFStringGetTypeID(); +} diff --git a/contrib/platform/src/com/sun/jna/platform/mac/DiskArbitration.java b/contrib/platform/src/com/sun/jna/platform/mac/DiskArbitration.java new file mode 100644 index 0000000000..fdc7a34838 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/mac/DiskArbitration.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.platform.mac.CoreFoundation.CFAllocatorRef; +import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef; +import com.sun.jna.platform.mac.IOKit.IOObject; + +/** + * Disk Arbitration is a low-level framework based on Core Foundation. The Disk + * Arbitration framework provides the ability to get various pieces of + * information about a volume. + */ +public interface DiskArbitration extends Library { + + DiskArbitration INSTANCE = Native.load("DiskArbitration", DiskArbitration.class); + + /** + * Type of a reference to {@code DASession} instances. + */ + class DASessionRef extends CFTypeRef { + } + + /** + * Type of a reference to {@code DADisk} instances. + */ + class DADiskRef extends CFTypeRef { + } + + /** + * Creates a new session. The caller of this function receives a reference to + * the returned object. + *

+ * The caller also implicitly retains the object and is responsible for + * releasing it with {@link CoreFoundation#CFRelease}. + * + * @param alloc + * The allocator object to be used to allocate memory. + * @return A reference to a new {@code DASession}. + */ + DASessionRef DASessionCreate(CFAllocatorRef alloc); + + /** + * Creates a new disk object. The caller of this function receives a reference + * to the returned object. + *

+ * The caller also implicitly retains the object and is responsible for + * releasing it with {@link CoreFoundation#CFRelease}. + * + * @param alloc + * The allocator object to be used to allocate memory. + * @param session + * The {@code DASession} in which to contact Disk Arbitration. + * @param diskName + * the BSD device name. + * @return A reference to a new {@code DADisk}. + */ + DADiskRef DADiskCreateFromBSDName(CFAllocatorRef alloc, DASessionRef session, String diskName); + + /** + * Creates a new disk object. The caller of this function receives a reference + * to the returned object. + *

+ * The caller also implicitly retains the object and is responsible for + * releasing it with {@link CoreFoundation#CFRelease}. + * + * @param allocator + * The allocator object to be used to allocate memory. + * @param session + * The {@code DASession} in which to contact Disk Arbitration. + * @param media + * The I/O Kit media object. + * @return A reference to a new {@code DADisk}. + */ + DADiskRef DADiskCreateFromIOMedia(CFAllocatorRef allocator, DASessionRef session, IOObject media); + + /** + * Obtains the Disk Arbitration description of the specified disk. This function + * will contact Disk Arbitration to acquire the latest description of the + * specified disk, unless this function is called on a disk object passed within + * the context of a registered callback, in which case the description is + * current as of that callback event. + *

+ * The caller of this function receives a reference to the returned object. The + * caller also implicitly retains the object and is responsible for releasing it + * with {@link CoreFoundation#CFRelease}. + * + * @param disk + * The {@code DADisk} for which to obtain the Disk Arbitration + * description. + * @return The disk's Disk Arbitration description. + */ + CFDictionaryRef DADiskCopyDescription(DADiskRef disk); + + /** + * Obtains the BSD device name for the specified disk. + * + * @param disk + * The {@code DADisk} for which to obtain the BSD device name. + * @return The disk's BSD device name. + */ + String DADiskGetBSDName(DADiskRef disk); +} diff --git a/contrib/platform/src/com/sun/jna/platform/mac/IOKit.java b/contrib/platform/src/com/sun/jna/platform/mac/IOKit.java new file mode 100644 index 0000000000..8355349e56 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/mac/IOKit.java @@ -0,0 +1,849 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import com.sun.jna.platform.mac.CoreFoundation.CFAllocatorRef; +import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef; +import com.sun.jna.platform.mac.CoreFoundation.CFBooleanRef; +import com.sun.jna.platform.mac.CoreFoundation.CFDataRef; +import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef; +import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; +import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; +import com.sun.jna.ptr.PointerByReference; + +/** + * The I/O Kit framework implements non-kernel access to I/O Kit objects + * (drivers and nubs) through the device-interface mechanism. + */ +public interface IOKit extends Library { + + IOKit INSTANCE = Native.load("IOKit", IOKit.class); + + int kIORegistryIterateRecursively = 0x00000001; + int kIORegistryIterateParents = 0x00000002; + + /** + * Return value when attempting parent or child in registry and they do not + * exist + */ + int kIOReturnNoDevice = 0xe00002c0; + + double kIOPSTimeRemainingUnlimited = -2.0; + double kIOPSTimeRemainingUnknown = -1.0; + + /** + * IOKitLib implements non-kernel task access to common IOKit object types - + * IORegistryEntry, IOService, IOIterator etc. These functions are generic - + * families may provide API that is more specific. + *

+ * IOKitLib represents IOKit objects outside the kernel with the types + * io_object_t, io_registry_entry_t, io_service_t, and io_connect_t. Function + * names usually begin with the type of object they are compatible with - e.g., + * IOObjectRelease can be used with any io_object_t. Inside the kernel, the c++ + * class hierarchy allows the subclasses of each object type to receive the same + * requests from user level clients, for example in the kernel, IOService is a + * subclass of IORegistryEntry, which means any of the IORegistryEntryXXX + * functions in IOKitLib may be used with io_service_t's as well as + * io_registry_t's. There are functions available to introspect the class of the + * kernel object which any io_object_t et al. represents. IOKit objects returned + * by all functions should be released with {@link IOKit#IOObjectRelease}. + */ + class IOObject extends PointerType { + public IOObject() { + super(); + } + + public IOObject(Pointer p) { + super(p); + } + + /** + * Convenience method for {@link IOKit#IOObjectConformsTo} on this object. + * + * @param className + * The name of the class. + * @return If the object handle is valid, and represents an object in the kernel + * that dynamic casts to the class true is returned, otherwise false. + */ + public boolean conformsTo(String className) { + return INSTANCE.IOObjectConformsTo(this, className); + } + + /** + * Convenience method for {@link IOKit#IOObjectRelease} on this object. + * + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + public int release() { + return INSTANCE.IOObjectRelease(this); + } + } + + /** + * An IOKit iterator handle. + */ + class IOIterator extends IOObject { + public IOIterator() { + super(); + } + + public IOIterator(Pointer p) { + super(p); + } + + /** + * Convenience method for {@link IOKit#IOIteratorNext} on this object. + * + * @return If the iterator handle is valid, the next element in the iteration is + * returned, otherwise {@code null} is returned. The element should be + * released by the caller when it is finished. + */ + public IORegistryEntry next() { + return INSTANCE.IOIteratorNext(this); + } + } + + /** + * The base class for all objects in the registry. + */ + class IORegistryEntry extends IOObject { + public IORegistryEntry() { + super(); + } + + public IORegistryEntry(Pointer p) { + super(p); + } + + /** + * Convenience method for {@link #IORegistryEntryGetRegistryEntryID} to + * return an ID for this registry entry that is global to all tasks. + * + * @return the ID. + * @throws IOReturnException + * if the ID could not be retrieved. + */ + public long getRegistryEntryID() { + LongByReference id = new LongByReference(); + int kr = INSTANCE.IORegistryEntryGetRegistryEntryID(this, id); + if (kr != 0) { + throw new IOReturnException(kr); + } + return id.getValue(); + } + + /** + * Convenience method for {@link #IORegistryEntryGetName} to return a + * name assigned to this registry entry. + * + * @return The name + * @throws IOReturnException + * if the name could not be retrieved. + */ + public String getName() { + Memory name = new Memory(128); + int kr = INSTANCE.IORegistryEntryGetName(this, name); + if (kr != 0) { + throw new IOReturnException(kr); + } + return name.getString(0); + } + + /** + * Convenience method for {@link #IORegistryEntryGetChildIterator} to + * return an iterator over this registry entry’s child entries in a + * plane. + * + * @param plane + * The name of an existing registry plane. Plane names are + * defined in {@code IOKitKeys.h}, for example, + * {@code kIOServicePlane}. + * @return The iterator + * @throws IOReturnException + * if the iterator could not be retrieved. + */ + public IOIterator getChildIterator(String plane) { + PointerByReference iter = new PointerByReference(); + int kr = INSTANCE.IORegistryEntryGetChildIterator(this, plane, iter); + if (kr != 0) { + throw new IOReturnException(kr); + } + return new IOIterator(iter.getValue()); + } + + /** + * Convenience method for {@link #IORegistryEntryGetChildEntry} to + * return the first child of this registry entry in a plane. + * + * @param plane + * The name of an existing registry plane. + * @return The child registry entry, if a child exists, null otherwise + * @throws IOReturnException + * if the entry exists but could not be retrieved. + */ + public IORegistryEntry getChildEntry(String plane) { + PointerByReference child = new PointerByReference(); + int kr = INSTANCE.IORegistryEntryGetChildEntry(this, plane, child); + if (kr == kIOReturnNoDevice) { + return null; + } else if (kr != 0) { + throw new IOReturnException(kr); + } + return new IORegistryEntry(child.getValue()); + } + + /** + * Convenience method for {@link #IORegistryEntryGetParentEntry} to + * return the first parent of this registry entry in a plane. + * + * @param plane + * The name of an existing registry plane. + * @return The parent registry entry, if a parent exists, null otherwise + * @throws IOReturnException + * if the entry exists but could not be retrieved. + */ + public IORegistryEntry getParentEntry(String plane) { + PointerByReference parent = new PointerByReference(); + int kr = INSTANCE.IORegistryEntryGetParentEntry(this, plane, parent); + if (kr == kIOReturnNoDevice) { + return null; + } else if (kr != 0) { + throw new IOReturnException(kr); + } + return new IORegistryEntry(parent.getValue()); + } + + /** + * Convenience method for {@link #IORegistryEntryCreateCFProperty} to create a + * CF representation of this registry entry's property. + * + * @param key + * A {@code CFString} specifying the property name. + * @return A CF container is created and returned the caller on success. + *

+ * The caller should release with {@link CoreFoundation#CFRelease}. + */ + public CFTypeRef createCFProperty(CFStringRef key) { + return INSTANCE.IORegistryEntryCreateCFProperty(this, key, CoreFoundation.INSTANCE.CFAllocatorGetDefault(), + 0); + } + + /** + * Convenience method for {@link #IORegistryEntryCreateCFProperties} to + * create a CF dictionary representation of this registry entry's + * property table. + * + * @return The property table. + *

+ * The caller should release with + * {@link CoreFoundation#CFRelease}. + * @throws IOReturnException + * if the entry could not be retrieved. + */ + public CFMutableDictionaryRef createCFProperties() { + PointerByReference properties = new PointerByReference(); + int kr = INSTANCE.IORegistryEntryCreateCFProperties(this, properties, + CoreFoundation.INSTANCE.CFAllocatorGetDefault(), 0); + if (kr != 0) { + throw new IOReturnException(kr); + } + return new CFMutableDictionaryRef(properties.getValue()); + } + + /** + * Convenience method for {@link #IORegistryEntrySearchCFProperty} to create a + * CF representation of a registry entry's property searched from this object. + * + * @param plane + * The name of an existing registry plane. Plane names are defined in + * {@code IOKitKeys.h}, for example, {@code kIOServicePlane}. + * @param key + * A {@code CFString} specifying the property name. + * @param options + * {@link #kIORegistryIterateRecursively} may be set to recurse + * automatically into the registry hierarchy. Without this option, + * this method degenerates into the standard + * {@link #IORegistryEntryCreateCFProperty} call. + * {@link #kIORegistryIterateParents} may be set to iterate the + * parents of the entry, in place of the children. + * @return A CF container is created and returned the caller on success. The + * caller should release with CFRelease. + */ + CFTypeRef searchCFProperty(String plane, CFStringRef key, int options) { + return INSTANCE.IORegistryEntrySearchCFProperty(this, plane, key, + CoreFoundation.INSTANCE.CFAllocatorGetDefault(), options); + } + + /** + * Convenience method to get a {@link java.lang.String} value from this IO + * Registry Entry. + * + * @param key + * The string name of the key to retrieve + * @return The value of the registry entry if it exists; {@code null} otherwise + */ + public String getStringProperty(String key) { + String value = null; + CFStringRef keyAsCFString = CFStringRef.createCFString(key); + CFTypeRef valueAsCFType = this.createCFProperty(keyAsCFString); + keyAsCFString.release(); + if (valueAsCFType != null) { + CFStringRef valueAsCFString = new CFStringRef(valueAsCFType.getPointer()); + value = valueAsCFString.stringValue(); + valueAsCFType.release(); + } + return value; + } + + /** + * Convenience method to get a {@link java.lang.Long} value from this IO + * Registry Entry. + * + * @param key + * The string name of the key to retrieve + * @return The value of the registry entry if it exists; {@code null} otherwise + *

+ * This method assumes a 64-bit integer is stored and does not do type + * checking. If this object's type differs from the return type, and the + * conversion is lossy or the return value is out of range, then this + * method returns an approximate value. + */ + public Long getLongProperty(String key) { + Long value = null; + CFStringRef keyAsCFString = CFStringRef.createCFString(key); + CFTypeRef valueAsCFType = this.createCFProperty(keyAsCFString); + keyAsCFString.release(); + if (valueAsCFType != null) { + CFNumberRef valueAsCFNumber = new CFNumberRef(valueAsCFType.getPointer()); + value = valueAsCFNumber.longValue(); + valueAsCFType.release(); + } + return value; + } + + /** + * Convenience method to get an {@link java.lang.Integer} value from this IO + * Registry Entry. + * + * @param key + * The string name of the key to retrieve + * @return The value of the registry entry if it exists; {@code null} otherwise + *

+ * This method assumes a 32-bit integer is stored and does not do type + * checking. If this object's type differs from the return type, and the + * conversion is lossy or the return value is out of range, then this + * method returns an approximate value. + */ + public Integer getIntegerProperty(String key) { + Integer value = null; + CFStringRef keyAsCFString = CFStringRef.createCFString(key); + CFTypeRef valueAsCFType = this.createCFProperty(keyAsCFString); + keyAsCFString.release(); + if (valueAsCFType != null) { + CFNumberRef valueAsCFNumber = new CFNumberRef(valueAsCFType.getPointer()); + value = valueAsCFNumber.intValue(); + valueAsCFType.release(); + } + return value; + } + + /** + * Convenience method to get a {@link java.lang.Double} value from this IO + * Registry Entry. + * + * @param key + * The string name of the key to retrieve + * @return The value of the registry entry if it exists; {@code null} otherwise + *

+ * This method assumes a floating point value is stored and does not do + * type checking. If this object's type differs from the return type, + * and the conversion is lossy or the return value is out of range, then + * this method returns an approximate value. + */ + public Double getDoubleProperty(String key) { + Double value = null; + CFStringRef keyAsCFString = CFStringRef.createCFString(key); + CFTypeRef valueAsCFType = this.createCFProperty(keyAsCFString); + keyAsCFString.release(); + if (valueAsCFType != null) { + CFNumberRef valueAsCFNumber = new CFNumberRef(valueAsCFType.getPointer()); + value = valueAsCFNumber.doubleValue(); + valueAsCFType.release(); + } + return value; + } + + /** + * Convenience method to get a {@link java.lang.Boolean} value from this IO + * Registry Entry. + * + * @param key + * The string name of the key to retrieve + * @return The value of the registry entry if it exists; {@code null} otherwise + */ + public Boolean getBooleanProperty(String key) { + Boolean value = null; + CFStringRef keyAsCFString = CFStringRef.createCFString(key); + CFTypeRef valueAsCFType = this.createCFProperty(keyAsCFString); + keyAsCFString.release(); + if (valueAsCFType != null) { + CFBooleanRef valueAsCFBoolean = new CFBooleanRef(valueAsCFType.getPointer()); + value = valueAsCFBoolean.booleanValue(); + valueAsCFType.release(); + } + return value; + } + + /** + * Convenience method to get a {@link java.lang.byte} array value from this IO + * Registry Entry. + * + * @param key + * The string name of the key to retrieve + * @return The value of the registry entry if it exists; {@code null} otherwise + */ + public byte[] getByteArrayProperty(String key) { + byte[] value = null; + CFStringRef keyAsCFString = CFStringRef.createCFString(key); + CFTypeRef valueAsCFType = this.createCFProperty(keyAsCFString); + keyAsCFString.release(); + if (valueAsCFType != null) { + CFDataRef valueAsCFData = new CFDataRef(valueAsCFType.getPointer()); + int length = valueAsCFData.getLength(); + Pointer p = valueAsCFData.getBytePtr(); + value = p.getByteArray(0, length); + valueAsCFType.release(); + } + return value; + } + } + + /** + * The base class for most I/O Kit families, devices, and drivers. + */ + class IOService extends IORegistryEntry { + public IOService() { + super(); + } + + public IOService(Pointer p) { + super(p); + } + } + + /** + * For an application to communicate with a device, the first thing it must do + * is create a connection between itself and the in-kernel object representing + * the device. To do this, it creates a user client object. + */ + class IOConnect extends IOService { + public IOConnect() { + super(); + } + + public IOConnect(Pointer p) { + super(p); + } + } + + /** + * Returns the mach port used to initiate communication with IOKit. + * + * @param bootstrapPort + * Pass 0 for the default. + * @param port + * A pointer to the master port is returned. Multiple calls to + * IOMasterPort will not result in leaking ports (each call to + * IOMasterPort adds another send right to the port) but it is + * considered good programming practice to deallocate the port when + * you are finished with it using + * {@link SystemB#mach_port_deallocate}. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IOMasterPort(int bootstrapPort, IntByReference port); + + /** + * Create a matching dictionary that specifies an {@code IOService} class match. + * + * @param name + * The class name. Class matching is successful on {@code IOService}s + * of this class or any subclass. + * @return The matching dictionary created, is returned on success, or + * {@code null} on failure. + *

+ * The dictionary is commonly passed to + * {@link #IOServiceGetMatchingServices} which will consume a reference, + * otherwise it should be released with {@link CoreFoundation#CFRelease} + * by the caller. + */ + CFMutableDictionaryRef IOServiceMatching(String name); + + /** + * Create a matching dictionary that specifies an {@code IOService} name match. + * + * @param name + * The {@code IOService} name. + * @return The matching dictionary created, is returned on success, or + * {@code null} on failure. + *

+ * The dictionary is commonly passed to + * {@link #IOServiceGetMatchingServices} which will consume a reference, + * otherwise it should be released with {@link CoreFoundation#CFRelease} + * by the caller. + */ + CFMutableDictionaryRef IOServiceNameMatching(String name); + + /** + * Create a matching dictionary that specifies an {@code IOService} match based + * on BSD device name. + * + * @param masterPort + * The master port obtained from {@link #IOMasterPort}. + * @param options + * No options are currently defined. + * @param bsdName + * The BSD name. + * @return The matching dictionary created, is returned on success, or + * {@code null} on failure. + *

+ * The dictionary is commonly passed to + * {@link #IOServiceGetMatchingServices} which will consume a reference, + * otherwise it should be released with {@link CoreFoundation#CFRelease} + * by the caller. + */ + CFMutableDictionaryRef IOBSDNameMatching(int masterPort, int options, String bsdName); + + /** + * Look up a registered IOService object that matches a matching dictionary. + * + * @param masterPort + * The master port obtained from {@link #IOMasterPort}. + * @param matchingDictionary + * A CF dictionary containing matching information, of which one + * reference is always consumed by this function. IOKitLib can + * construct matching dictionaries for common criteria with helper + * functions such as {@link #IOServiceMatching}, + * {@link #IOServiceNameMatching}, and {@link #IOBSDNameMatching}. + * @return The first service matched is returned on success. + *

+ * The service must be released by the caller. + */ + IOService IOServiceGetMatchingService(int masterPort, CFDictionaryRef matchingDictionary); + + /** + * Look up registered IOService objects that match a matching dictionary. + * + * @param masterPort + * The master port obtained from {@link #IOMasterPort}. + * @param matchingDictionary + * A CF dictionary containing matching information, of which one + * reference is always consumed by this function. IOKitLib can + * construct matching dictionaries for common criteria with helper + * functions such as {@link #IOServiceMatching}, + * {@link #IOServiceNameMatching}, and {@link #IOBSDNameMatching}. + * @param iterator + * An iterator handle is returned on success, and should be released + * by the caller when the iteration is finished. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IOServiceGetMatchingServices(int masterPort, CFDictionaryRef matchingDictionary, PointerByReference iterator); + + /** + * Returns the next object in an iteration. + * + * @param iterator + * An IOKit iterator handle. + * @return If the iterator handle is valid, the next element in the iteration is + * returned, otherwise zero is returned. The element should be released + * by the caller when it is finished. + */ + IORegistryEntry IOIteratorNext(IOIterator iterator); + + /** + * Create a CF representation of a registry entry's property. + * + * @param entry + * The registry entry handle whose property to copy. + * @param key + * A {@code CFString} specifying the property name. + * @param allocator + * The CF allocator to use when creating the CF container. + * @param options + * No options are currently defined. + * @return A CF container is created and returned the caller on success. + *

+ * The caller should release with {@link CoreFoundation#CFRelease}. + */ + CFTypeRef IORegistryEntryCreateCFProperty(IORegistryEntry entry, CFStringRef key, CFAllocatorRef allocator, + int options); + + /** + * Create a CF dictionary representation of a registry entry's property table. + * + * @param entry + * The registry entry handle whose property table to copy. + * @param properties + * A CFDictionary is created and returned the caller on success. The + * caller should release with CFRelease. + * @param allocator + * The CF allocator to use when creating the CF containers. + * @param options + * No options are currently defined. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IORegistryEntryCreateCFProperties(IORegistryEntry entry, PointerByReference properties, + CFAllocatorRef allocator, int options); + + /** + * Create a CF representation of a registry entry's property. + * + * @param entry + * The registry entry at which to start the search. + * @param plane + * The name of an existing registry plane. Plane names are defined in + * {@code IOKitKeys.h}, for example, {@code kIOServicePlane}. + * @param key + * A {@code CFString} specifying the property name. + * @param allocator + * The CF allocator to use when creating the CF container. + * @param options + * {@link #kIORegistryIterateRecursively} may be set to recurse + * automatically into the registry hierarchy. Without this option, + * this method degenerates into the standard + * {@link #IORegistryEntryCreateCFProperty} call. + * {@link #kIORegistryIterateParents} may be set to iterate the + * parents of the entry, in place of the children. + * @return A CF container is created and returned the caller on success. The + * caller should release with CFRelease. + */ + CFTypeRef IORegistryEntrySearchCFProperty(IORegistryEntry entry, String plane, CFStringRef key, + CFAllocatorRef allocator, int options); + + /** + * Returns an ID for the registry entry that is global to all tasks. + * + * @param entry + * The registry entry handle whose ID to look up. + * @param id + * The resulting ID. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IORegistryEntryGetRegistryEntryID(IORegistryEntry entry, LongByReference id); + + /** + * Returns a name assigned to a registry entry. + * + * @param entry + * The registry entry handle whose name to look up. + * @param name + * The caller's buffer to receive the name. This must be a 128-byte + * buffer. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IORegistryEntryGetName(IORegistryEntry entry, Pointer name); + + /** + * Returns an iterator over a registry entry’s child entries in a plane. + * + * @param entry + * The registry entry whose children to iterate over. + * @param plane + * The name of an existing registry plane. Plane names are defined in + * {@code IOKitKeys.h}, for example, {@code kIOServicePlane}. + * @param iter + * The created iterator over the children of the entry, on success. + * The iterator must be released when the iteration is finished. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IORegistryEntryGetChildIterator(IORegistryEntry entry, String plane, PointerByReference iter); + + /** + * Returns the first child of a registry entry in a plane. + * + * @param entry + * The registry entry whose child to look up. + * @param plane + * The name of an existing registry plane. Plane names are defined in + * {@code IOKitKeys.h}, for example, {@code kIOServicePlane}. + * @param child + * The first child of the registry entry, on success. The child must + * be released by the caller. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IORegistryEntryGetChildEntry(IORegistryEntry entry, String plane, PointerByReference child); + + /** + * Returns the first parent of a registry entry in a plane. + * + * @param entry + * The registry entry whose parent to look up. + * @param plane + * The name of an existing registry plane. Plane names are defined in + * {@code IOKitKeys.h}, for example, {@code kIOServicePlane}. + * @param parent + * The first parent of the registry entry, on success. The parent + * must be released by the caller. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IORegistryEntryGetParentEntry(IORegistryEntry entry, String plane, PointerByReference parent); + + /** + * Return a handle to the registry root. + * + * @param masterPort + * The master port obtained from {@link #IOMasterPort}. + * @return A handle to the IORegistryEntry root instance, to be released with + * {@link #IOObjectRelease} by the caller, or 0 on failure. + */ + IORegistryEntry IORegistryGetRootEntry(int masterPort); + + /** + * Performs an OSDynamicCast operation on an IOKit object. + * + * @param object + * An IOKit object. + * @param className + * The name of the class. + * @return If the object handle is valid, and represents an object in the kernel + * that dynamic casts to the class true is returned, otherwise false. + */ + boolean IOObjectConformsTo(IOObject object, String className); + + /** + * Releases an object handle previously returned by {@code IOKitLib}. + * + * @param object + * The IOKit object to release. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IOObjectRelease(IOObject object); + + /** + * A request to create a connection to an IOService. + * + * @param service + * The IOService object to open a connection to, usually obtained via + * the {@link #IOServiceGetMatchingServices} API. + * @param owningTask + * The mach task requesting the connection. + * @param type + * A constant specifying the type of connection to be created, + * interpreted only by the IOService's family. + * @param connect + * An {@code io_connect_t} handle is returned on success, to be used + * with the IOConnectXXX APIs. It should be destroyed with + * {@link IOServiceClose}. + * @return A return code generated by {@code IOService::newUserClient}. + */ + int IOServiceOpen(IOService service, int owningTask, int type, PointerByReference connect); + + /** + * Returns the busyState of an IOService. + * + * @param service + * The IOService whose busyState to return. + * @param busyState + * The busyState count is returned. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IOServiceGetBusyState(IOService service, IntByReference busyState); + + /** + * Close a connection to an IOService and destroy the connect handle. + * + * @param connect + * The connect handle created by IOServiceOpen. It will be destroyed + * by this function, and should not be released with IOObjectRelease. + * @return 0 if successful, otherwise a {@code kern_return_t} error code. + */ + int IOServiceClose(IOConnect connect); + + /** + * Returns a blob of Power Source information in an opaque CFTypeRef. + * + * @return {@code null} if errors were encountered, a {@link CFTypeRef} + * otherwise. + *

+ * Caller must {@link CoreFoundation#CFRelease} the return value when + * done accessing it. + */ + CFTypeRef IOPSCopyPowerSourcesInfo(); + + /** + * Returns a CFArray of Power Source handles, each of type CFTypeRef. + * + * @param blob + * Takes the {@link CFTypeRef} returned by + * {@link #IOPSCopyPowerSourcesInfo} + * @return {@code null} if errors were encountered, otherwise a CFArray of + * {@link CFTypeRef}s. + *

+ * Caller must {@link CoreFoundation#CFRelease} the returned + * {@link CFArrayRef}. + */ + CFArrayRef IOPSCopyPowerSourcesList(CFTypeRef blob); + + /** + * Returns a CFDictionary with readable information about the specific power + * source. + * + * @param blob + * the {@link CFTypeRef} returned by + * {@link #IOPSCopyPowerSourcesInfo} + * @param ps + * One of the {@link CFTypeRef}s in the CFArray returned by + * {@link #IOPSCopyPowerSourcesList}. + * @return {@code null} if an error was encountered, otherwise a CFDictionary. + *

+ * Caller should NOT release the returned CFDictionary - it will be + * released as part of the {@link CFTypeRef} returned by + * {@link IOPSCopyPowerSourcesInfo}. + */ + CFDictionaryRef IOPSGetPowerSourceDescription(CFTypeRef blob, CFTypeRef ps); + + /** + * Returns the estimated seconds remaining until all power sources (battery + * and/or UPS's) are empty. + * + * @return Returns {@link #kIOPSTimeRemainingUnknown} if the OS cannot determine + * the time remaining. + *

+ * Returns {@link #kIOPSTimeRemainingUnlimited} if the system has an + * unlimited power source. + *

+ * Otherwise returns a positive number indicating the time remaining in + * seconds until all power sources are depleted. + */ + double IOPSGetTimeRemainingEstimate(); +} diff --git a/contrib/platform/src/com/sun/jna/platform/mac/IOKitUtil.java b/contrib/platform/src/com/sun/jna/platform/mac/IOKitUtil.java new file mode 100644 index 0000000000..dbe567b0ab --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/mac/IOKitUtil.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef; +import com.sun.jna.platform.mac.IOKit.IOIterator; +import com.sun.jna.platform.mac.IOKit.IORegistryEntry; +import com.sun.jna.platform.mac.IOKit.IOService; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +/** + * Provides utilities for IOKit. + */ +public class IOKitUtil { + private static final IOKit IO = IOKit.INSTANCE; + private static final SystemB SYS = SystemB.INSTANCE; + + private IOKitUtil() { + } + + /** + * Gets a pointer to the Mach Master Port. + * + * @return The master port. + *

+ * Multiple calls to {@link #getMasterPort} will not result in leaking + * ports (each call to {@link IOKit#IOMasterPort} adds another send + * right to the port) but it is considered good programming practice to + * deallocate the port when you are finished with it, using + * {@link SystemB#mach_port_deallocate}. + */ + public static int getMasterPort() { + IntByReference port = new IntByReference(); + IO.IOMasterPort(0, port); + return port.getValue(); + } + + /** + * Gets the IO Registry root. + * + * @return a handle to the IORoot. Callers should release when finished, using + * {@link IOKit#IOObjectRelease}. + */ + public static IORegistryEntry getRoot() { + int masterPort = getMasterPort(); + IORegistryEntry root = IO.IORegistryGetRootEntry(masterPort); + SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort); + return root; + } + + /** + * Opens a the first IOService matching a service name. + * + * @param serviceName + * The service name to match + * @return a handle to an IOService if successful, {@code null} if failed. + * Callers should release when finished, using + * {@link IOKit#IOObjectRelease}. + */ + public static IOService getMatchingService(String serviceName) { + CFMutableDictionaryRef dict = IO.IOServiceMatching(serviceName); + if (dict != null) { + return getMatchingService(dict); + } + return null; + } + + /** + * Opens a the first IOService matching a dictionary. + * + * @param matchingDictionary + * The dictionary to match. This method will consume a reference to + * the dictionary. + * @return a handle to an IOService if successful, {@code null} if failed. + * Callers should release when finished, using + * {@link IOKit#IOObjectRelease}. + */ + public static IOService getMatchingService(CFDictionaryRef matchingDictionary) { + int masterPort = getMasterPort(); + IOService service = IO.IOServiceGetMatchingService(masterPort, matchingDictionary); + SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort); + return service; + } + + /** + * Convenience method to get IOService objects matching a service name. + * + * @param serviceName + * The service name to match + * @return a handle to an IOIterator if successful, {@code null} if failed. + * Callers should release when finished, using + * {@link IOKit#IOObjectRelease}. + */ + public static IOIterator getMatchingServices(String serviceName) { + CFMutableDictionaryRef dict = IO.IOServiceMatching(serviceName); + if (dict != null) { + return getMatchingServices(dict); + } + return null; + } + + /** + * Convenience method to get IOService objects matching a dictionary. + * + * @param matchingDictionary + * The dictionary to match. This method will consume a reference to + * the dictionary. + * @return a handle to an IOIterator if successful, {@code null} if failed. + * Callers should release when finished, using + * {@link IOKit#IOObjectRelease}. + */ + public static IOIterator getMatchingServices(CFDictionaryRef matchingDictionary) { + int masterPort = getMasterPort(); + PointerByReference serviceIterator = new PointerByReference(); + int result = IO.IOServiceGetMatchingServices(masterPort, matchingDictionary, serviceIterator); + SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort); + if (result == 0 && serviceIterator.getValue() != null) { + return new IOIterator(serviceIterator.getValue()); + } + return null; + } + + /** + * Convenience method to get the IO dictionary matching a bsd name. + * + * @param bsdName + * The bsd name of the registry entry + * @return The dictionary ref if successful, {@code null} if failed. Callers + * should release when finished, using {@link IOKit#IOObjectRelease}. + */ + public static CFMutableDictionaryRef getBSDNameMatchingDict(String bsdName) { + int masterPort = getMasterPort(); + CFMutableDictionaryRef result = IO.IOBSDNameMatching(masterPort, 0, bsdName); + SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort); + return result; + } +} diff --git a/contrib/platform/src/com/sun/jna/platform/mac/IOReturnException.java b/contrib/platform/src/com/sun/jna/platform/mac/IOReturnException.java new file mode 100644 index 0000000000..0285f70cbb --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/mac/IOReturnException.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +/** + * Exception encapsulating {@code IOReturn} I/O Kit Error Return Values, defined + * as {@code kern_return_t} values in {@code IOKit/IOReturn.h} + *

+ * The return value supplies information in three separate bit fields: the high + * 6 bits specify the system in which the error occurred, the next 12 bits + * specify the subsystem, and the final 14 bits specify the error code itself. + */ +public class IOReturnException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private int ioReturn; + + /** + * New exception from {@code kern_return_t} + * + * @param kr + * The return value + */ + public IOReturnException(int kr) { + this(kr, formatMessage(kr)); + } + + /** + * New exception from {@code kern_return_t} with specified message + * + * @param kr + * The return value + * @param msg + * The exception message + */ + protected IOReturnException(int kr, String msg) { + super(msg); + this.ioReturn = kr; + } + + /** + * @return the IOReturn code + */ + public int getIOReturnCode() { + return ioReturn; + } + + /** + * The high 6 bits of the return value encode the system. + * + * @param kr + * The return value + * @return the system value + */ + public static int getSystem(int kr) { + return (kr >> 26) & 0x3f; + } + + /** + * The middle 12 bits of the return value encode the subsystem. + * + * @param kr + * The return value + * @return the subsystem value + */ + public static int getSubSystem(int kr) { + return (kr >> 14) & 0xfff; + } + + /** + * The low 14 bits of the return value encode the return code. + * + * @param kr + * The return value + * @return the return code + */ + public static int getCode(int kr) { + return kr & 0x3fff; + } + + private static String formatMessage(int kr) { + return "IOReturn error code: " + kr + " (system=" + getSystem(kr) + ", subSystem=" + getSubSystem(kr) + + ", code=" + getCode(kr) + ")"; + } +} diff --git a/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java b/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java index 9fed4bf1e3..2fefff44f3 100644 --- a/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java +++ b/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java @@ -25,9 +25,6 @@ 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.NativeLong; @@ -37,9 +34,6 @@ import com.sun.jna.ptr.LongByReference; import com.sun.jna.ptr.PointerByReference; -/** - * Author: Daniel Widdis Date: 6/5/15 - */ public interface SystemB extends Library { SystemB INSTANCE = Native.load("System", SystemB.class); @@ -92,22 +86,20 @@ public interface SystemB extends Library { // resource.h int RUSAGE_INFO_V2 = 2; - @Structure.FieldOrder({"cpu_ticks"}) + @Structure.FieldOrder({ "cpu_ticks" }) public static class HostCpuLoadInfo extends Structure { public int cpu_ticks[] = new int[CPU_STATE_MAX]; } - @Structure.FieldOrder({"avenrun", "mach_factor"}) + @Structure.FieldOrder({ "avenrun", "mach_factor" }) 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 } - @Structure.FieldOrder({"free_count", "active_count", - "inactive_count", "wire_count", "zero_fill_count", - "reactivations", "pageins", "pageouts", "faults", - "cow_faults", "lookups", "hits", "purgeable_count", - "purges", "speculative_count"}) + @Structure.FieldOrder({ "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 VMStatistics extends Structure { public int free_count; // # of pages free public int active_count; // # of pages active @@ -127,19 +119,11 @@ public static class VMStatistics extends Structure { public int speculative_count; } - @Structure.FieldOrder({"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"}) + @Structure.FieldOrder({ "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" }) public static class VMStatistics64 extends Structure { public int free_count; // # of pages free public int active_count; // # of pages active @@ -173,16 +157,15 @@ public static class VMStatistics64 extends Structure { public long total_uncompressed_pages_in_compressor; } - @Structure.FieldOrder({"pbsd", "ptinfo"}) + @Structure.FieldOrder({ "pbsd", "ptinfo" }) class ProcTaskAllInfo extends Structure { public ProcBsdInfo pbsd; public ProcTaskInfo ptinfo; } - @Structure.FieldOrder({"pbi_flags", "pbi_status", "pbi_xstatus", "pbi_pid", "pbi_ppid", - "pbi_uid", "pbi_gid", "pbi_ruid", "pbi_rgid", "pbi_svuid", "pbi_svgid", "rfu_1", "pbi_comm", - "pbi_name", "pbi_nfiles", "pbi_pgid", "pbi_pjobc", "e_tdev", "e_tpgid", "pbi_nice", - "pbi_start_tvsec", "pbi_start_tvusec"}) + @Structure.FieldOrder({ "pbi_flags", "pbi_status", "pbi_xstatus", "pbi_pid", "pbi_ppid", "pbi_uid", "pbi_gid", + "pbi_ruid", "pbi_rgid", "pbi_svuid", "pbi_svgid", "rfu_1", "pbi_comm", "pbi_name", "pbi_nfiles", "pbi_pgid", + "pbi_pjobc", "e_tdev", "e_tpgid", "pbi_nice", "pbi_start_tvsec", "pbi_start_tvusec" }) class ProcBsdInfo extends Structure { public int pbi_flags; public int pbi_status; @@ -208,10 +191,10 @@ class ProcBsdInfo extends Structure { public long pbi_start_tvusec; } - @Structure.FieldOrder({"pti_virtual_size", "pti_resident_size", "pti_total_user", - "pti_total_system", "pti_threads_user", "pti_threads_system", "pti_policy", "pti_faults", - "pti_pageins", "pti_cow_faults", "pti_messages_sent", "pti_messages_received", "pti_syscalls_mach", - "pti_syscalls_unix", "pti_csw", "pti_threadnum", "pti_numrunning", "pti_priority"}) + @Structure.FieldOrder({ "pti_virtual_size", "pti_resident_size", "pti_total_user", "pti_total_system", + "pti_threads_user", "pti_threads_system", "pti_policy", "pti_faults", "pti_pageins", "pti_cow_faults", + "pti_messages_sent", "pti_messages_received", "pti_syscalls_mach", "pti_syscalls_unix", "pti_csw", + "pti_threadnum", "pti_numrunning", "pti_priority" }) class ProcTaskInfo extends Structure { public long pti_virtual_size; /* virtual memory size (bytes) */ public long pti_resident_size; /* resident memory size (bytes) */ @@ -233,12 +216,11 @@ class ProcTaskInfo extends Structure { public int pti_priority; /* task priority */ } - @Structure.FieldOrder({"v_swtch", "v_trap", "v_syscall", "v_intr", "v_soft", "v_faults", - "v_lookups", "v_hits", "v_vm_faults", "v_cow_faults", "v_swpin", "v_swpout", "v_pswpin", - "v_pswpout", "v_pageins", "v_pageouts", "v_pgpgin", "v_pgpgout", "v_intrans", "v_reactivated", - "v_rev", "v_scan", "v_dfree", "v_pfree", "v_zfod", "v_nzfod", "v_page_size", "v_kernel_pages", - "v_free_target", "v_free_min", "v_free_count", "v_wire_count", "v_active_count", - "v_inactive_target", "v_inactive_count"}) + @Structure.FieldOrder({ "v_swtch", "v_trap", "v_syscall", "v_intr", "v_soft", "v_faults", "v_lookups", "v_hits", + "v_vm_faults", "v_cow_faults", "v_swpin", "v_swpout", "v_pswpin", "v_pswpout", "v_pageins", "v_pageouts", + "v_pgpgin", "v_pgpgout", "v_intrans", "v_reactivated", "v_rev", "v_scan", "v_dfree", "v_pfree", "v_zfod", + "v_nzfod", "v_page_size", "v_kernel_pages", "v_free_target", "v_free_min", "v_free_count", "v_wire_count", + "v_active_count", "v_inactive_target", "v_inactive_count" }) class VMMeter extends Structure { /* * General system activity. @@ -288,11 +270,11 @@ class VMMeter extends Structure { public int v_inactive_count; /* number of pages inactive */ } - @Structure.FieldOrder({"ri_uuid", "ri_user_time", "ri_system_time", "ri_pkg_idle_wkups", - "ri_interrupt_wkups", "ri_pageins", "ri_wired_size", "ri_resident_size", "ri_phys_footprint", - "ri_proc_start_abstime", "ri_proc_exit_abstime", "ri_child_user_time", "ri_child_system_time", - "ri_child_pkg_idle_wkups", "ri_child_interrupt_wkups", "ri_child_pageins", - "ri_child_elapsed_abstime", "ri_diskio_bytesread", "ri_diskio_byteswritten" }) + @Structure.FieldOrder({ "ri_uuid", "ri_user_time", "ri_system_time", "ri_pkg_idle_wkups", "ri_interrupt_wkups", + "ri_pageins", "ri_wired_size", "ri_resident_size", "ri_phys_footprint", "ri_proc_start_abstime", + "ri_proc_exit_abstime", "ri_child_user_time", "ri_child_system_time", "ri_child_pkg_idle_wkups", + "ri_child_interrupt_wkups", "ri_child_pageins", "ri_child_elapsed_abstime", "ri_diskio_bytesread", + "ri_diskio_byteswritten" }) class RUsageInfoV2 extends Structure { public byte[] ri_uuid = new byte[16]; public long ri_user_time; @@ -315,14 +297,14 @@ class RUsageInfoV2 extends Structure { public long ri_diskio_byteswritten; } - @Structure.FieldOrder({"vip_vi", "vip_path"}) + @Structure.FieldOrder({ "vip_vi", "vip_path" }) class VnodeInfoPath extends Structure { public byte[] vip_vi = new byte[152]; // vnode_info but we don't // need its data public byte[] vip_path = new byte[MAXPATHLEN]; } - @Structure.FieldOrder({"pvi_cdir", "pvi_rdir"}) + @Structure.FieldOrder({ "pvi_cdir", "pvi_rdir" }) class VnodePathInfo extends Structure { public VnodeInfoPath pvi_cdir; public VnodeInfoPath pvi_rdir; @@ -330,12 +312,12 @@ class VnodePathInfo extends Structure { /** * The statfs() routine returns information about a mounted file system. The - * path argument is the path name of any file or directory within the - * mounted file system. The buf argument is a pointer to a statfs structure. + * path argument is the path name of any file or directory within the mounted + * file system. The buf argument is a pointer to a statfs structure. */ - @Structure.FieldOrder({"f_bsize", "f_iosize", "f_blocks", "f_bfree", "f_bavail", "f_files", - "f_ffree", "f_fsid", "f_owner", "f_type", "f_flags", "f_fssubtype", "f_fstypename", "f_mntonname", - "f_mntfromname", "f_reserved" }) + @Structure.FieldOrder({ "f_bsize", "f_iosize", "f_blocks", "f_bfree", "f_bavail", "f_files", "f_ffree", "f_fsid", + "f_owner", "f_type", "f_flags", "f_fssubtype", "f_fstypename", "f_mntonname", "f_mntfromname", + "f_reserved" }) class Statfs extends Structure { public int f_bsize; /* fundamental file system block size */ public int f_iosize; /* optimal transfer block size */ @@ -361,7 +343,7 @@ class Statfs extends Structure { /** * Return type for sysctl vm.swapusage */ - @Structure.FieldOrder({"xsu_total", "xsu_avail", "xsu_used", "xsu_pagesize", "xsu_encrypted"}) + @Structure.FieldOrder({ "xsu_total", "xsu_avail", "xsu_used", "xsu_pagesize", "xsu_encrypted" }) class XswUsage extends Structure { public long xsu_total; public long xsu_avail; @@ -373,12 +355,11 @@ class XswUsage extends Structure { /** * Data type as part of IFmsgHdr */ - @Structure.FieldOrder({"ifi_type", "ifi_typelen", "ifi_physical", "ifi_addrlen", "ifi_hdrlen", - "ifi_recvquota", "ifi_xmitquota", "ifi_unused1", "ifi_mtu", "ifi_metric", "ifi_baudrate", - "ifi_ipackets", "ifi_ierrors", "ifi_opackets", "ifi_oerrors", "ifi_collisions", "ifi_ibytes", - "ifi_obytes", "ifi_imcasts", "ifi_omcasts", "ifi_iqdrops", "ifi_noproto", "ifi_recvtiming", - "ifi_xmittiming", "ifi_lastchange", "ifi_unused2", "ifi_hwassist", "ifi_reserved1", - "ifi_reserved2"}) + @Structure.FieldOrder({ "ifi_type", "ifi_typelen", "ifi_physical", "ifi_addrlen", "ifi_hdrlen", "ifi_recvquota", + "ifi_xmitquota", "ifi_unused1", "ifi_mtu", "ifi_metric", "ifi_baudrate", "ifi_ipackets", "ifi_ierrors", + "ifi_opackets", "ifi_oerrors", "ifi_collisions", "ifi_ibytes", "ifi_obytes", "ifi_imcasts", "ifi_omcasts", + "ifi_iqdrops", "ifi_noproto", "ifi_recvtiming", "ifi_xmittiming", "ifi_lastchange", "ifi_unused2", + "ifi_hwassist", "ifi_reserved1", "ifi_reserved2" }) class IFdata extends Structure { public byte ifi_type; // ethernet, tokenring, etc public byte ifi_typelen; // Length of frame type id @@ -414,8 +395,8 @@ class IFdata extends Structure { /** * Return type for sysctl CTL_NET,PF_ROUTE */ - @Structure.FieldOrder({"ifm_msglen", "ifm_version", "ifm_type", "ifm_addrs", "ifm_flags", - "ifm_index", "ifm_data" }) + @Structure.FieldOrder({ "ifm_msglen", "ifm_version", "ifm_type", "ifm_addrs", "ifm_flags", "ifm_index", + "ifm_data" }) class IFmsgHdr extends Structure { public short ifm_msglen; // to skip over non-understood messages public byte ifm_version; // future binary compatability @@ -437,11 +418,10 @@ public IFmsgHdr(Pointer p) { /** * Data type as part of IFmsgHdr */ - @Structure.FieldOrder({ "ifi_type", "ifi_typelen", "ifi_physical", "ifi_addrlen", "ifi_hdrlen", - "ifi_recvquota", "ifi_xmitquota", "ifi_unused1", "ifi_mtu", "ifi_metric", "ifi_baudrate", - "ifi_ipackets", "ifi_ierrors", "ifi_opackets", "ifi_oerrors", "ifi_collisions", "ifi_ibytes", - "ifi_obytes", "ifi_imcasts", "ifi_omcasts", "ifi_iqdrops", "ifi_noproto", "ifi_recvtiming", - "ifi_xmittiming", "ifi_lastchange"}) + @Structure.FieldOrder({ "ifi_type", "ifi_typelen", "ifi_physical", "ifi_addrlen", "ifi_hdrlen", "ifi_recvquota", + "ifi_xmitquota", "ifi_unused1", "ifi_mtu", "ifi_metric", "ifi_baudrate", "ifi_ipackets", "ifi_ierrors", + "ifi_opackets", "ifi_oerrors", "ifi_collisions", "ifi_ibytes", "ifi_obytes", "ifi_imcasts", "ifi_omcasts", + "ifi_iqdrops", "ifi_noproto", "ifi_recvtiming", "ifi_xmittiming", "ifi_lastchange" }) class IFdata64 extends Structure { public byte ifi_type; // ethernet, tokenring, etc public byte ifi_typelen; // Length of frame type id @@ -473,8 +453,8 @@ class IFdata64 extends Structure { /** * Return type for sysctl CTL_NET,PF_ROUTE */ - @Structure.FieldOrder({ "ifm_msglen", "ifm_version", "ifm_type", "ifm_addrs", "ifm_flags", - "ifm_index", "ifm_snd_len", "ifm_snd_maxlen", "ifm_snd_drops", "ifm_timer", "ifm_data"}) + @Structure.FieldOrder({ "ifm_msglen", "ifm_version", "ifm_type", "ifm_addrs", "ifm_flags", "ifm_index", + "ifm_snd_len", "ifm_snd_maxlen", "ifm_snd_drops", "ifm_timer", "ifm_data" }) class IFmsgHdr2 extends Structure { public short ifm_msglen; // to skip over non-understood messages public byte ifm_version; // future binary compatability @@ -496,8 +476,8 @@ public IFmsgHdr2(Pointer p) { /** * Return type for getpwuid */ - @Structure.FieldOrder({"pw_name", "pw_passwd", "pw_uid", "pw_gid", "pw_change", "pw_class", - "pw_gecos", "pw_dir", "pw_shell", "pw_expire", "pw_fields" }) + @Structure.FieldOrder({ "pw_name", "pw_passwd", "pw_uid", "pw_gid", "pw_change", "pw_class", "pw_gecos", "pw_dir", + "pw_shell", "pw_expire", "pw_fields" }) class Passwd extends Structure { public String pw_name; // user name public String pw_passwd; // encrypted password @@ -515,7 +495,7 @@ class Passwd extends Structure { /** * Return type for getgrgid */ - @Structure.FieldOrder({"gr_name", "gr_passwd", "gr_gid", "gr_mem"}) + @Structure.FieldOrder({ "gr_name", "gr_passwd", "gr_gid", "gr_mem" }) class Group extends Structure { public String gr_name; /* group name */ public String gr_passwd; /* group password */ @@ -526,7 +506,7 @@ class Group extends Structure { /** * Time value */ - @Structure.FieldOrder({"tv_sec", "tv_usec"}) + @Structure.FieldOrder({ "tv_sec", "tv_usec" }) class Timeval extends Structure { public NativeLong tv_sec; // seconds public int tv_usec; // microseconds @@ -535,73 +515,82 @@ class Timeval extends Structure { /** * Time Zone */ - @Structure.FieldOrder({ "tz_minuteswest", "tz_dsttime"}) + @Structure.FieldOrder({ "tz_minuteswest", "tz_dsttime" }) class Timezone extends Structure { public int tz_minuteswest; /* of Greenwich */ public int tz_dsttime; /* type of dst correction to apply */ } /** - * The system's notion of the current Greenwich time and the current time - * zone is obtained with the gettimeofday() call, and set with the - * settimeofday() call. The time is expressed in seconds and microseconds - * since midnight (0 hour), January 1, 1970. The resolution of the system - * clock is hardware dependent, and the time may be updated continuously or - * in ``ticks.'' If tp is NULL and tzp is non-NULL, gettimeofday() will - * populate the timezone struct in tzp. If tp is non-NULL and tzp is NULL, - * then only the timeval struct in tp is populated. If both tp and tzp are - * NULL, nothing is returned. + * The system's notion of the current Greenwich time and the current time zone + * is obtained with the gettimeofday() call, and set with the settimeofday() + * call. The time is expressed in seconds and microseconds since midnight (0 + * hour), January 1, 1970. The resolution of the system clock is hardware + * dependent, and the time may be updated continuously or in ``ticks.'' If tp is + * NULL and tzp is non-NULL, gettimeofday() will populate the timezone struct in + * tzp. If tp is non-NULL and tzp is NULL, then only the timeval struct in tp is + * populated. If both tp and tzp are NULL, nothing is returned. * * @param tp * Timeval structure * @param tzp * Timezone structure - * @return A 0 return value indicates that the call succeeded. A -1 return - * value indicates an error occurred, and in this case an error code - * is stored into the global variable errno. + * @return A 0 return value indicates that the call succeeded. A -1 return value + * indicates an error occurred, and in this case an error code is stored + * into the global variable errno. */ int gettimeofday(Timeval tp, Timezone tzp); /** - * 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. + * 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 mach_task_self system call returns the calling thread's task_self - * port. It has an effect equivalent to receiving a send right for the task's - * kernel port. + * The mach_task_self system call returns the calling thread's task_self port. + * It has an effect equivalent to receiving a send right for the task's kernel + * port. * * @return the task's kernel port */ int mach_task_self(); + /** + * Decrement the target port right's user reference count. + * + * @param port + * The port holding the right. + * @param name + * The port's name for the right. + * @return 0 if successful, a {@code kern_return_t} code otherwise. + */ + int mach_port_deallocate(int port, int name); + /** * 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 hostPort + * 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); + int host_page_size(int hostPort, LongByReference pPageSize); /** - * The host_statistics function returns scheduling and virtual memory - * statistics concerning the host as specified by hostStat. + * The host_statistics function returns scheduling and virtual memory statistics + * concerning the host as specified by hostStat. * - * @param machPort + * @param hostPort * 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) + * The type of statistics desired ({@link #HOST_LOAD_INFO}, + * {@link #HOST_VM_INFO}, or {@link #HOST_CPU_LOAD_INFO}) * @param stats * Statistics about the specified host. * @param count @@ -609,17 +598,17 @@ class Timezone extends Structure { * returned (in natural-sized units). * @return 0 on success; sets errno on failure */ - int host_statistics(int machPort, int hostStat, Structure stats, IntByReference count); + int host_statistics(int hostPort, 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 + * @param hostPort * The control port for the host for which information is to be * obtained. * @param hostStat - * The type of statistics desired (HOST_VM_INFO64) + * The type of statistics desired ({@link #HOST_VM_INFO64}) * @param stats * Statistics about the specified host. * @param count @@ -627,35 +616,35 @@ class Timezone extends Structure { * returned (in natural-sized units). * @return 0 on success; sets errno on failure */ - int host_statistics64(int machPort, int hostStat, Structure stats, IntByReference count); + int host_statistics64(int hostPort, 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 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 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. + * 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. + * 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 @@ -671,13 +660,12 @@ class Timezone extends Structure { * 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); + 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. + * 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 @@ -691,37 +679,34 @@ int sysctl(int[] name, int namelen, Pointer oldp, IntByReference oldlenp, * 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); + 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 + * This 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. + *

+ * 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. + * 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); @@ -729,7 +714,7 @@ int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp, /** * The host_processor_info function returns information about processors. * - * @param machPort + * @param hostPort * The control port for the host for which information is to be * obtained. * @param flavor @@ -742,28 +727,30 @@ int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp, * Pointer to number of elements in the returned structure * @return 0 on success; sets errno on failure */ - int host_processor_info(int machPort, int flavor, IntByReference procCount, - PointerByReference procInfo, IntByReference procInfoCount); + int host_processor_info(int hostPort, int flavor, IntByReference procCount, PointerByReference procInfo, + IntByReference procInfoCount); /** - * The getloadavg() function returns the number of processes in the system - * run queue averaged over various periods of time. Up to nelem samples are - * retrieved and assigned to successive elements of loadavg[]. The system - * imposes a maximum of 3 samples, representing averages over the last 1, 5, - * and 15 minutes, respectively. + * The getloadavg() function returns the number of processes in the system run + * queue averaged over various periods of time. Up to nelem samples are + * retrieved and assigned to successive elements of loadavg[]. The system + * imposes a maximum of 3 samples, representing averages over the last 1, 5, and + * 15 minutes, respectively. + * * @param loadavg * An array of doubles which will be filled with the results * @param nelem * Number of samples to return - * @return If the load average was unobtainable, -1 is returned; otherwise, - * the number of samples actually retrieved is returned. - * @see getloadavg(3) + * @return If the load average was unobtainable, -1 is returned; otherwise, the + * number of samples actually retrieved is returned. + * @see getloadavg(3) */ int getloadavg(double[] loadavg, int nelem); /** - * This function searches the password database for the given user uid, - * always returning the first one encountered. + * This function searches the password database for the given user uid, always + * returning the first one encountered. * * @param uid * The user ID @@ -772,9 +759,9 @@ int host_processor_info(int machPort, int flavor, IntByReference procCount, Passwd getpwuid(int uid); /** - * This function searches the group database for the given group name - * pointed to by the group id given by gid, returning the first one - * encountered. Identical group gids may result in undefined behavior. + * This function searches the group database for the given group name pointed to + * by the group id given by gid, returning the first one encountered. Identical + * group gids may result in undefined behavior. * * @param gid * The group ID @@ -792,18 +779,18 @@ int host_processor_info(int machPort, int flavor, IntByReference procCount, * @param buffer * a C array of int-sized values to be filled with process * identifiers that hold an open file reference matching the - * specified path or volume. Pass NULL to obtain the minimum - * buffer size needed to hold the currently active processes. + * specified path or volume. Pass NULL to obtain the minimum buffer + * size needed to hold the currently active processes. * @param buffersize * the size (in bytes) of the provided buffer. - * @return the number of bytes of data returned in the provided buffer; -1 - * if an error was encountered; + * @return the number of bytes of data returned in the provided buffer; -1 if an + * error was encountered; */ int proc_listpids(int type, int typeinfo, int[] buffer, int buffersize); /** - * Return in buffer a proc_*info structure corresponding to the flavor for - * the specified process + * Return in buffer a proc_*info structure corresponding to the flavor for the + * specified process * * @param pid * the process identifier @@ -815,8 +802,8 @@ int host_processor_info(int machPort, int flavor, IntByReference procCount, * holds results * @param buffersize * size of results - * @return the number of bytes of data returned in the provided buffer; -1 - * if an error was encountered; + * @return the number of bytes of data returned in the provided buffer; -1 if an + * error was encountered; */ int proc_pidinfo(int pid, int flavor, long arg, Structure buffer, int buffersize); @@ -829,8 +816,7 @@ int host_processor_info(int machPort, int flavor, IntByReference procCount, * holds results * @param buffersize * size of results - * @return the length of the name returned in buffer if successful; 0 - * otherwise + * @return the length of the name returned in buffer if successful; 0 otherwise */ int proc_pidpath(int pid, Pointer buffer, int buffersize); @@ -850,31 +836,30 @@ int host_processor_info(int machPort, int flavor, IntByReference procCount, int proc_pid_rusage(int pid, int flavor, RUsageInfoV2 buffer); /** - * The getfsstat() function returns information about all mounted file - * systems. The buf argument is a pointer to an array of statfs structures. + * The getfsstat() function returns information about all mounted file systems. + * The buf argument is a pointer to an array of statfs structures. * * Fields that are undefined for a particular file system are set to -1. The * buffer is filled with an array of statfs structures, one for each mounted * file system up to the size specified by bufsize. * * @param buf - * Array of statfs structures that will be filled with results. - * If buf is given as NULL, getfsstat() returns just the number - * of mounted file systems. + * Array of statfs structures that will be filled with results. If + * buf is given as NULL, getfsstat() returns just the number of + * mounted file systems. * @param bufsize * Size of the buffer to fill * @param flags - * If flags is set to MNT_NOWAIT, getfsstat() will directly - * return the information retained in the kernel to avoid delays - * caused by waiting for updated information from a file system - * that is perhaps temporarily unable to respond. Some of the - * information returned may be out of date, however; if flags is - * set to MNT_WAIT or MNT_DWAIT instead, getfsstat() will request - * updated information from each mounted filesystem before - * returning. + * If flags is set to MNT_NOWAIT, getfsstat() will directly return + * the information retained in the kernel to avoid delays caused by + * waiting for updated information from a file system that is perhaps + * temporarily unable to respond. Some of the information returned + * may be out of date, however; if flags is set to MNT_WAIT or + * MNT_DWAIT instead, getfsstat() will request updated information + * from each mounted filesystem before returning. * @return Upon successful completion, the number of statfs structures is - * returned. Otherwise, -1 is returned and the global variable errno - * is set to indicate the error. + * returned. Otherwise, -1 is returned and the global variable errno is + * set to indicate the error. */ int getfsstat64(Statfs[] buf, int bufsize, int flags); diff --git a/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java b/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java new file mode 100644 index 0000000000..a7d621789d --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import org.junit.Test; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.mac.CoreFoundation.CFAllocatorRef; +import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef; +import com.sun.jna.platform.mac.CoreFoundation.CFDataRef; +import com.sun.jna.platform.mac.CoreFoundation.CFIndex; +import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef; +import com.sun.jna.platform.mac.CoreFoundation.CFNumberType; +import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; +import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef; +import com.sun.jna.ptr.DoubleByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; +import com.sun.jna.ptr.PointerByReference; + +public class CoreFoundationTest { + + private static final CoreFoundation CF = CoreFoundation.INSTANCE; + + @Test + public void testCFStringRef() throws UnsupportedEncodingException { + String awesome = "ǝɯosǝʍɐ sı ∀Nſ"; // Unicode + CFStringRef cfAwesome = CFStringRef.createCFString(awesome); + assertEquals(awesome.length(), CF.CFStringGetLength(cfAwesome).intValue()); + assertEquals(awesome, cfAwesome.stringValue()); + assertEquals(CoreFoundation.STRING_TYPE_ID, cfAwesome.getTypeID()); + + byte[] awesomeArr = awesome.getBytes("UTF-8"); + Memory mem = new Memory(awesomeArr.length + 1); + mem.clear(); + assertNotEquals(0, + CF.CFStringGetCString(cfAwesome, mem, new CFIndex(mem.size()), CoreFoundation.kCFStringEncodingUTF8)); + byte[] awesomeBytes = mem.getByteArray(0, (int) mem.size() - 1); + assertArrayEquals(awesomeArr, awesomeBytes); + // Essentially a toString, can't rely on format but should contain the string + CFStringRef desc = CF.CFCopyDescription(cfAwesome); + assertTrue(desc.stringValue().contains(awesome)); + + desc.release(); + cfAwesome.release(); + } + + @Test + public void testCFNumberRef() { + LongByReference max = new LongByReference(Long.MAX_VALUE); + CFNumberRef cfMax = CF.CFNumberCreate(null, CFNumberType.kCFNumberLongLongType.typeIndex(), max); + assertEquals(Long.MAX_VALUE, cfMax.longValue()); + assertEquals(CoreFoundation.NUMBER_TYPE_ID, cfMax.getTypeID()); + cfMax.release(); + + IntByReference zero = new IntByReference(0); + IntByReference one = new IntByReference(1); + CFNumberRef cfZero = CF.CFNumberCreate(null, CFNumberType.kCFNumberIntType.typeIndex(), zero); + CFNumberRef cfOne = CF.CFNumberCreate(null, CFNumberType.kCFNumberIntType.typeIndex(), one); + + assertEquals(0, cfZero.intValue()); + assertEquals(1, cfOne.intValue()); + cfZero.release(); + cfOne.release(); + } + + @Test + public void testCFRetainCount() { + DoubleByReference pi = new DoubleByReference(Math.PI); + DoubleByReference e = new DoubleByReference(Math.E); + CFNumberRef cfE = CF.CFNumberCreate(null, CFNumberType.kCFNumberDoubleType.typeIndex(), e); + CFNumberRef cfPi = CF.CFNumberCreate(null, CFNumberType.kCFNumberDoubleType.typeIndex(), pi); + assertEquals(1, CF.CFGetRetainCount(cfE).intValue()); + assertEquals(1, CF.CFGetRetainCount(cfPi).intValue()); + cfE.retain(); + cfPi.retain(); + cfPi.retain(); + assertEquals(2, CF.CFGetRetainCount(cfE).intValue()); + assertEquals(3, CF.CFGetRetainCount(cfPi).intValue()); + + List irrationalReferences = Arrays.asList(cfE, cfPi); + for (CFTypeRef value : irrationalReferences) { + value.release(); + } + + assertEquals(1, CF.CFGetRetainCount(cfE).intValue()); + assertEquals(2, CF.CFGetRetainCount(cfPi).intValue()); + cfPi.release(); + assertEquals(1, CF.CFGetRetainCount(cfPi).intValue()); + cfE.release(); + cfPi.release(); + } + + @Test + public void testCFArray() { + CFNumberRef[] refArray = new CFNumberRef[3]; + int size = Native.getNativeSize(CFNumberRef.class); + Memory contiguousArray = new Memory(size * refArray.length); + for (int i = 0; i < refArray.length; i++) { + refArray[i] = CF.CFNumberCreate(null, CoreFoundation.CFNumberType.kCFNumberIntType.typeIndex(), + new IntByReference(i)); + contiguousArray.setPointer(i * size, refArray[i].getPointer()); + } + CFArrayRef cfPtrArray = CF.CFArrayCreate(null, contiguousArray, new CFIndex(refArray.length), null); + assertEquals(CoreFoundation.ARRAY_TYPE_ID, cfPtrArray.getTypeID()); + + assertEquals(refArray.length, cfPtrArray.getCount()); + for (int i = 0; i < refArray.length; i++) { + Pointer result = cfPtrArray.getValueAtIndex(i); + try { + new CFStringRef(result); + fail("Should have thrown a ClassCastExcpetion."); + } catch (ClassCastException expected) { + assertEquals("Unable to cast to CFString. Type ID: CFNumber", expected.getMessage()); + } + CFNumberRef numRef = new CFNumberRef(result); + assertEquals(i, numRef.intValue()); + } + + for (int i = 0; i < refArray.length; i++) { + refArray[i].release(); + } + cfPtrArray.release(); + } + + @Test + public void testCFData() { + int size = 128; + // Create some random bytes + byte[] randomBytes = new byte[size]; + new Random().nextBytes(randomBytes); + // Fill native memory with them + Memory nativeBytes = new Memory(size); + nativeBytes.write(0, randomBytes, 0, randomBytes.length); + // Create a CF reference to the data + CFDataRef cfData = CF.CFDataCreate(null, nativeBytes, new CFIndex(size)); + assertEquals(CoreFoundation.DATA_TYPE_ID, cfData.getTypeID()); + + int dataSize = cfData.getLength(); + assertEquals(size, dataSize); + // Read it back out and convert to an array + Pointer bytes = cfData.getBytePtr(); + byte[] dataBytes = bytes.getByteArray(0, dataSize); + assertArrayEquals(randomBytes, dataBytes); + cfData.release(); + } + + @Test + public void testCFDictionary() { + CFAllocatorRef alloc = CF.CFAllocatorGetDefault(); + CFMutableDictionaryRef dict = CF.CFDictionaryCreateMutable(alloc, new CFIndex(2), null, null); + assertEquals(CoreFoundation.DICTIONARY_TYPE_ID, dict.getTypeID()); + + CFStringRef oneStr = CFStringRef.createCFString("one"); + + // Key does not exist, returns null + assertFalse(dict.getValueIfPresent(oneStr, null)); + Pointer cfNull = dict.getValue(oneStr); + assertNull(cfNull); + + // Store and retrieve null value + dict.setValue(oneStr, null); + assertTrue(dict.getValueIfPresent(oneStr, null)); + Pointer cfNullValue = dict.getValue(oneStr); + assertNull(cfNullValue); + + // Store (replace the null) and retrieve integer value + IntByReference one = new IntByReference(1); + CFNumberRef cfOne = CF.CFNumberCreate(null, CFNumberType.kCFNumberIntType.typeIndex(), one); + dict.setValue(oneStr, cfOne); + + assertTrue(dict.getValueIfPresent(oneStr, null)); + Pointer result = dict.getValue(oneStr); + CFNumberRef numRef = new CFNumberRef(result); + assertEquals(1, numRef.intValue()); + + PointerByReference resultPtr = new PointerByReference(); + assertTrue(dict.getValueIfPresent(oneStr, resultPtr)); + numRef = new CFNumberRef(resultPtr.getValue()); + assertEquals(1, numRef.intValue()); + + // Test non-CF type as key + IntByReference onePtr = new IntByReference(1); + dict.setValue(onePtr, oneStr); + result = dict.getValue(onePtr); + CFStringRef strRef = new CFStringRef(result); + assertEquals("one", strRef.stringValue()); + + oneStr.release(); + cfOne.release(); + dict.release(); + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/mac/DiskArbitrationTest.java b/contrib/platform/test/com/sun/jna/platform/mac/DiskArbitrationTest.java new file mode 100644 index 0000000000..60c106ed79 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/mac/DiskArbitrationTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.mac.CoreFoundation.CFBooleanRef; +import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef; +import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; +import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef; +import com.sun.jna.platform.mac.DiskArbitration.DADiskRef; +import com.sun.jna.platform.mac.DiskArbitration.DASessionRef; +import com.sun.jna.platform.mac.IOKit.IOIterator; +import com.sun.jna.platform.mac.IOKit.IORegistryEntry; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +public class DiskArbitrationTest { + + private static final DiskArbitration DA = DiskArbitration.INSTANCE; + private static final CoreFoundation CF = CoreFoundation.INSTANCE; + private static final IOKit IO = IOKit.INSTANCE; + private static final SystemB SYS = SystemB.INSTANCE; + + @Test + public void testDiskCreate() { + IntByReference masterPortPtr = new IntByReference(); + assertEquals(0, IO.IOMasterPort(0, masterPortPtr)); + int masterPort = masterPortPtr.getValue(); + + // Create some keys we'll need + CFStringRef daMediaBSDName = CFStringRef.createCFString("DAMediaBSDName"); + CFStringRef daMediaWhole = CFStringRef.createCFString("DAMediaWhole"); + CFStringRef daMediaLeaf = CFStringRef.createCFString("DAMediaLeaf"); + CFStringRef daMediaSize = CFStringRef.createCFString("DAMediaSize"); + CFStringRef daMediaBlockSize = CFStringRef.createCFString("DAMediaBlockSize"); + CFStringRef wholeKey = CFStringRef.createCFString("Whole"); + + // Open a DiskArbitration session + DASessionRef session = DA.DASessionCreate(CF.CFAllocatorGetDefault()); + assertNotNull(session); + + // Get IOMedia objects representing whole drives and save the BSD Name + List bsdNames = new ArrayList(); + PointerByReference iterPtr = new PointerByReference(); + + CFMutableDictionaryRef dict = IOKit.INSTANCE.IOServiceMatching("IOMedia"); + // Consumes a reference to dict + assertEquals(0, IO.IOServiceGetMatchingServices(masterPort, dict, iterPtr)); + IOIterator iter = new IOIterator(iterPtr.getValue()); + IORegistryEntry media = iter.next(); + while (media != null) { + CFTypeRef cfWhole = media.createCFProperty(wholeKey); + assertNotNull(cfWhole); + CFBooleanRef cfWholeBool = new CFBooleanRef(cfWhole.getPointer()); + assertEquals(CoreFoundation.BOOLEAN_TYPE_ID, cfWholeBool.getTypeID()); + if (cfWholeBool.booleanValue()) { + // check that util boolean matches + assertTrue(media.getBooleanProperty("Whole")); + // check long, int, double values for major + Long majorLong = media.getLongProperty("BSD Major"); + Integer majorInt = media.getIntegerProperty("BSD Major"); + Double majorDouble = media.getDoubleProperty("BSD Major"); + assertNotNull(majorLong); + assertNotNull(majorInt); + assertNotNull(majorDouble); + assertEquals(majorLong.intValue(), majorInt.intValue()); + assertEquals(majorDouble.doubleValue(), majorInt.doubleValue(), 1e-15); + + DADiskRef disk = DA.DADiskCreateFromIOMedia(CF.CFAllocatorGetDefault(), session, media); + bsdNames.add(DA.DADiskGetBSDName(disk)); + disk.release(); + } else { + assertFalse(media.getBooleanProperty("Whole")); + } + cfWhole.release(); + assertEquals(0, media.release()); + media = iter.next(); + } + assertEquals(0, iter.release()); + + // Now iterate the bsdNames + for (String bsdName : bsdNames) { + // Get a reference to the disk - only matching /dev/disk* + String path = "/dev/" + bsdName; + File f = new File(path); + assertTrue(f.exists()); + // Get the DiskArbitration dictionary for this disk, which has size (capacity) + DADiskRef disk = DA.DADiskCreateFromBSDName(CF.CFAllocatorGetDefault(), session, path); + assertNotNull(disk); + CFDictionaryRef diskInfo = DA.DADiskCopyDescription(disk); + assertNotNull(diskInfo); + + // Since we looked up "whole" BSD disks these should match + Pointer result = diskInfo.getValue(daMediaBSDName); + CFStringRef bsdNamePtr = new CFStringRef(result); + assertEquals(bsdName, bsdNamePtr.stringValue()); + result = diskInfo.getValue(daMediaWhole); + CFBooleanRef bsdWholePtr = new CFBooleanRef(result); + assertTrue(bsdWholePtr.booleanValue()); + + // Size is a multiple of block size + result = diskInfo.getValue(daMediaSize); + CFNumberRef sizePtr = new CFNumberRef(result); + long size = sizePtr.longValue(); + result = diskInfo.getValue(daMediaBlockSize); + CFNumberRef blockSizePtr = new CFNumberRef(result); + long blockSize = blockSizePtr.longValue(); + assertEquals(0, size % blockSize); + + diskInfo.release(); + disk.release(); + } + wholeKey.release(); + daMediaBSDName.release(); + daMediaWhole.release(); + daMediaLeaf.release(); + daMediaSize.release(); + daMediaBlockSize.release(); + + session.release(); + assertEquals(0, SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort)); + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/mac/IOKitTest.java b/contrib/platform/test/com/sun/jna/platform/mac/IOKitTest.java new file mode 100644 index 0000000000..f7400f088c --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/mac/IOKitTest.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef; +import com.sun.jna.platform.mac.CoreFoundation.CFBooleanRef; +import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef; +import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef; +import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; +import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef; +import com.sun.jna.platform.mac.IOKit.IOConnect; +import com.sun.jna.platform.mac.IOKit.IOIterator; +import com.sun.jna.platform.mac.IOKit.IORegistryEntry; +import com.sun.jna.platform.mac.IOKit.IOService; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +public class IOKitTest { + + private static final IOKit IO = IOKit.INSTANCE; + private static final SystemB SYS = SystemB.INSTANCE; + + private static final String IO_SERVICE = "IOService"; + + @Test + public void testMatching() { + int masterPort = IOKitUtil.getMasterPort(); + + String match = "matching BSD Name"; + CFMutableDictionaryRef dict = IO.IOBSDNameMatching(masterPort, 0, match); + CFStringRef bsdNameKey = CFStringRef.createCFString("BSD Name"); + Pointer result = dict.getValue(bsdNameKey); + CFStringRef cfBsdName = new CFStringRef(result); + assertEquals(match, cfBsdName.stringValue()); + bsdNameKey.release(); + dict.release(); + + match = "matching IOClass Name"; + dict = IO.IOServiceNameMatching(match); + CFStringRef classNameKey = CFStringRef.createCFString("IONameMatch"); + result = dict.getValue(classNameKey); + CFStringRef cfClassName = new CFStringRef(result); + assertEquals(match, cfClassName.stringValue()); + classNameKey.release(); + dict.release(); + + match = "IOPlatformExpertDevice"; + dict = IO.IOServiceMatching(match); + CFStringRef classKey = CFStringRef.createCFString("IOProviderClass"); + result = dict.getValue(classKey); + CFStringRef cfClass = new CFStringRef(result); + assertEquals(match, cfClass.stringValue()); + classKey.release(); + + // Get matching service (consumes dict reference) + IORegistryEntry platformExpert = IO.IOServiceGetMatchingService(masterPort, dict); + // Get a single key + CFStringRef serialKey = CFStringRef.createCFString("IOPlatformSerialNumber"); + CFTypeRef cfSerialAsType = platformExpert.createCFProperty(serialKey); + assertNotNull(cfSerialAsType); + CFStringRef cfSerial = new CFStringRef(cfSerialAsType.getPointer()); + String serialNumber = cfSerial.stringValue(); + + // Test util method for the same thing + String serialNumberViaUtil = platformExpert.getStringProperty("IOPlatformSerialNumber"); + assertEquals(serialNumber, serialNumberViaUtil); + + assertEquals(12, serialNumber.length()); + // Get all the keys + dict = platformExpert.createCFProperties(); + assertNotEquals(0, dict.getValueIfPresent(serialKey, null)); + result = dict.getValue(serialKey); + cfSerial = new CFStringRef(result); + assertEquals(serialNumber, cfSerial.stringValue()); + dict.release(); + assertEquals(0, platformExpert.release()); + + // Get a single key from a nested entry + IORegistryEntry root = IOKitUtil.getRoot(); + assertNotNull(root); + // Root should have no parent + assertNull(root.getParentEntry(IO_SERVICE)); + // Follow down the chain for a child, shouldn't reach a depth of 50 + int treeDepth = 0; + IORegistryEntry child = root.getChildEntry(IO_SERVICE); + while (child != null && ++treeDepth < 50) { + child = child.getChildEntry(IO_SERVICE); + } + assertNotEquals(50, treeDepth); + + cfSerialAsType = root.searchCFProperty(IO_SERVICE, serialKey, 0); + // without recursive search should be null + assertNull(cfSerialAsType); + cfSerialAsType = root.searchCFProperty(IO_SERVICE, serialKey, IOKit.kIORegistryIterateRecursively); + // with recursive search should return a match + cfSerial = new CFStringRef(cfSerialAsType.getPointer()); + assertEquals(serialNumber, cfSerial.stringValue()); + serialKey.release(); + cfSerialAsType.release(); + + assertEquals(0, root.release()); + assertEquals(0, SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort)); + } + + @Test + public void testIteratorParentChild() { + int masterPort = IOKitUtil.getMasterPort(); + + Set uniqueEntryIdSet = new HashSet<>(); + // Iterate over USB Controllers. All devices are children of one of + // these controllers in the "IOService" plane + IOIterator iter = IOKitUtil.getMatchingServices("IOUSBController"); + assertNotNull(iter); + IORegistryEntry controllerDevice = iter.next(); + while (controllerDevice != null) { + long id = controllerDevice.getRegistryEntryID(); + // EntryIDs 0 thru 19 are reserved, all are unique + assertTrue(id > 19); + assertFalse(uniqueEntryIdSet.contains(id)); + uniqueEntryIdSet.add(id); + + // Get device name + String controllerName = controllerDevice.getName(); + // Root controllers always begin with "AppleUSB" + assertEquals("AppleUSB", controllerName.substring(0, 8)); + + // Get the first child, to test vs. iterator + boolean testFirstChild = true; + IORegistryEntry firstChild = controllerDevice.getChildEntry(IO_SERVICE); + // If this returns non-null, we have at least one child entry to + // test. If not, the iterator will never check whether to test + + // Now iterate the children of this device in the "IOService" plane. + IOIterator childIter = controllerDevice.getChildIterator(IO_SERVICE); + IORegistryEntry childDevice = childIter.next(); + while (childDevice != null) { + assertTrue(childDevice.conformsTo("IOUSBDevice")); + long childId = childDevice.getRegistryEntryID(); + assertTrue(childId > 19); + assertFalse(uniqueEntryIdSet.contains(childId)); + uniqueEntryIdSet.add(childId); + + // If first child, test and release the retained first child pointer + if (testFirstChild) { + assertEquals(childDevice, firstChild); + assertEquals(0, firstChild.release()); + testFirstChild = false; + } + + // Get this device's parent in IOService plane, matches controller + IORegistryEntry parent = childDevice.getParentEntry(IO_SERVICE); + assertEquals(controllerDevice, parent); + assertEquals(0, parent.release()); + + // Release this device and iterate to the next one + assertEquals(0, childDevice.release()); + childDevice = childIter.next(); + } + assertEquals(0, childIter.release()); + + // Release this controller and iterate to the next one + assertEquals(0, controllerDevice.release()); + controllerDevice = iter.next(); + } + assertEquals(0, iter.release()); + assertEquals(0, SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort)); + } + + @Test + public void testIOConnect() { + int masterPort = IOKitUtil.getMasterPort(); + + IOService smcService = IOKitUtil.getMatchingService("AppleSMC"); + assertNotNull(smcService); + + PointerByReference connPtr = new PointerByReference(); + int taskSelf = SYS.mach_task_self(); + assertEquals(0, IO.IOServiceOpen(smcService, taskSelf, 0, connPtr)); + IOConnect conn = new IOConnect(connPtr.getValue()); + + IntByReference busy = new IntByReference(Integer.MIN_VALUE); + IO.IOServiceGetBusyState(smcService, busy); + assertTrue(busy.getValue() >= 0); + + IO.IOServiceClose(conn); + assertEquals(0, smcService.release()); + assertEquals(0, SYS.mach_port_deallocate(SYS.mach_task_self(), masterPort)); + } + + @Test + public void testPowerSources() { + CFTypeRef powerSourcesInfo = IO.IOPSCopyPowerSourcesInfo(); + assertNotNull(powerSourcesInfo); + CFArrayRef powerSourcesList = IO.IOPSCopyPowerSourcesList(powerSourcesInfo); + assertNotNull(powerSourcesList); + double timeRemaining = IO.IOPSGetTimeRemainingEstimate(); + assertTrue(timeRemaining > 0 || timeRemaining == IOKit.kIOPSTimeRemainingUnknown + || timeRemaining == IOKit.kIOPSTimeRemainingUnlimited); + + CFStringRef isPresentKey = CFStringRef.createCFString("Is Present"); + CFStringRef currentCapacityKey = CFStringRef.createCFString("Current Capacity"); + CFStringRef maxCapacityKey = CFStringRef.createCFString("Max Capacity"); + int powerSourcesCount = powerSourcesList.getCount(); + for (int ps = 0; ps < powerSourcesCount; ps++) { + // Get the dictionary for that Power Source + Pointer pwrSrcPtr = powerSourcesList.getValueAtIndex(ps); + CFTypeRef powerSource = new CFTypeRef(pwrSrcPtr); + CFDictionaryRef dictionary = IOKit.INSTANCE.IOPSGetPowerSourceDescription(powerSourcesInfo, powerSource); + + // Get values from dictionary (See IOPSKeys.h) + // Skip if not present + PointerByReference result = new PointerByReference(); + if (dictionary.getValueIfPresent(isPresentKey, result)) { + CFBooleanRef isPresentRef = new CFBooleanRef(result.getValue()); + if (isPresentRef.booleanValue()) { + int currentCapacity = 0; + if (dictionary.getValueIfPresent(currentCapacityKey, result)) { + CFNumberRef cap = new CFNumberRef(result.getValue()); + currentCapacity = cap.intValue(); + } + int maxCapacity = 100; + if (dictionary.getValueIfPresent(maxCapacityKey, result)) { + CFNumberRef cap = new CFNumberRef(result.getValue()); + maxCapacity = cap.intValue(); + } + assertTrue(currentCapacity <= maxCapacity); + } + } + } + isPresentKey.release(); + currentCapacityKey.release(); + maxCapacityKey.release(); + powerSourcesList.release(); + powerSourcesInfo.release(); + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/mac/IOReturnExceptionTest.java b/contrib/platform/test/com/sun/jna/platform/mac/IOReturnExceptionTest.java new file mode 100644 index 0000000000..e2ce4fa01e --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/mac/IOReturnExceptionTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Daniel Widdis + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.mac; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class IOReturnExceptionTest { + + @Test + public void testException() { + try { + throw new IOReturnException(IOKit.kIOReturnNoDevice); + } catch (IOReturnException e) { + int code = e.getIOReturnCode(); + assertEquals(0xe00002c0, code); + assertEquals(0x38, IOReturnException.getSystem(code)); // io_kit + assertEquals(0x0, IOReturnException.getSubSystem(code)); // sub_iokit_common + assertEquals(0x2c0, IOReturnException.getCode(code)); // kIOReturnNoDevice + assertEquals("IOReturn error code: -536870208 (system=56, subSystem=0, code=704)", e.getMessage()); + } + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java b/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java index 5df6c70e55..f24040314f 100644 --- a/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java +++ b/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java @@ -23,6 +23,8 @@ */ package com.sun.jna.platform.mac; +import static org.junit.Assert.assertNotEquals; + import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Platform; @@ -52,10 +54,7 @@ /** * Exercise the {@link SystemB} class. - * - * @author widdis@gmail.com */ -// @SuppressWarnings("unused") public class SystemBTest extends TestCase { public void testSysctl() { @@ -93,11 +92,11 @@ public void testSysctl() { } public void testHostPageSize() { - int machPort = SystemB.INSTANCE.mach_host_self(); - assertTrue(machPort > 0); + int hostPort = SystemB.INSTANCE.mach_host_self(); + assertNotEquals(0, hostPort); LongByReference pPageSize = new LongByReference(); - int ret = SystemB.INSTANCE.host_page_size(machPort, pPageSize); + int ret = SystemB.INSTANCE.host_page_size(hostPort, pPageSize); assertEquals(ret, 0); // Probably 4096, definitely a power of 2 assertTrue(pPageSize.getValue() > 0); @@ -105,12 +104,11 @@ public void testHostPageSize() { } public void testVMInfo() { - int machPort = SystemB.INSTANCE.mach_host_self(); - assertTrue(machPort > 0); + int hostPort = SystemB.INSTANCE.mach_host_self(); + assertNotEquals(0, hostPort); VMStatistics vmStats = new VMStatistics(); - int ret = SystemB.INSTANCE.host_statistics(machPort, - SystemB.HOST_VM_INFO, vmStats, + int ret = SystemB.INSTANCE.host_statistics(hostPort, SystemB.HOST_VM_INFO, vmStats, new IntByReference(vmStats.size() / SystemB.INT_SIZE)); assertEquals(ret, 0); // Nonnegative @@ -118,9 +116,8 @@ public void testVMInfo() { 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)); + ret = SystemB.INSTANCE.host_statistics64(hostPort, SystemB.HOST_VM_INFO, vmStats64, + new IntByReference(vmStats64.size() / SystemB.INT_SIZE)); assertEquals(ret, 0); // Nonnegative assertTrue(vmStats64.free_count >= 0); @@ -128,26 +125,24 @@ SystemB.HOST_VM_INFO, vmStats64, new IntByReference( } public void testCpuLoad() { - int machPort = SystemB.INSTANCE.mach_host_self(); - assertTrue(machPort > 0); + int hostPort = SystemB.INSTANCE.mach_host_self(); + assertNotEquals(0, hostPort); HostCpuLoadInfo cpuLoadInfo = new HostCpuLoadInfo(); - int ret = SystemB.INSTANCE.host_statistics(machPort, - SystemB.HOST_CPU_LOAD_INFO, cpuLoadInfo, new IntByReference( - cpuLoadInfo.size())); + int ret = SystemB.INSTANCE.host_statistics(hostPort, 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); + int hostPort = SystemB.INSTANCE.mach_host_self(); + assertNotEquals(0, hostPort); HostLoadInfo hostLoadInfo = new HostLoadInfo(); - int ret = SystemB.INSTANCE.host_statistics(machPort, - SystemB.HOST_CPU_LOAD_INFO, hostLoadInfo, new IntByReference( - hostLoadInfo.size())); + int ret = SystemB.INSTANCE.host_statistics(hostPort, SystemB.HOST_CPU_LOAD_INFO, hostLoadInfo, + new IntByReference(hostLoadInfo.size())); assertEquals(ret, 0); // Should be two int[3]'s assertEquals(hostLoadInfo.avenrun.length, 3); @@ -157,27 +152,19 @@ SystemB.HOST_CPU_LOAD_INFO, hostLoadInfo, new IntByReference( } public void testHostProcessorInfo() { - int machPort = SystemB.INSTANCE.mach_host_self(); - assertTrue(machPort > 0); + int hostPort = SystemB.INSTANCE.mach_host_self(); + assertNotEquals(0, hostPort); IntByReference procCount = new IntByReference(); PointerByReference procCpuLoadInfo = new PointerByReference(); IntByReference procInfoCount = new IntByReference(); - int ret = SystemB.INSTANCE.host_processor_info(machPort, - SystemB.PROCESSOR_CPU_LOAD_INFO, procCount, procCpuLoadInfo, - procInfoCount); + int ret = SystemB.INSTANCE.host_processor_info(hostPort, SystemB.PROCESSOR_CPU_LOAD_INFO, procCount, + procCpuLoadInfo, procInfoCount); assertEquals(ret, 0); assertTrue(procCount.getValue() > 0); - assertEquals(procCpuLoadInfo.getValue().getIntArray(0, - procInfoCount.getValue()).length, procInfoCount.getValue()); - } - - public void testMachPorts() { - int machPort = SystemB.INSTANCE.mach_host_self(); - assertTrue(machPort > 0); - machPort = SystemB.INSTANCE.mach_task_self(); - assertTrue(machPort > 0); + assertEquals(procCpuLoadInfo.getValue().getIntArray(0, procInfoCount.getValue()).length, + procInfoCount.getValue()); } public void testGetLoadAvg() { @@ -208,9 +195,10 @@ public void testTimeofDay() { } public void testVMMeter() { - int machPort = SystemB.INSTANCE.mach_host_self(); + int hostPort = SystemB.INSTANCE.mach_host_self(); + assertNotEquals(0, hostPort); VMMeter vmstats = new VMMeter(); - assertEquals(0, SystemB.INSTANCE.host_statistics(machPort, SystemB.HOST_VM_INFO, vmstats, + assertEquals(0, SystemB.INSTANCE.host_statistics(hostPort, SystemB.HOST_VM_INFO, vmstats, new IntByReference(vmstats.size()))); assertTrue(vmstats.v_lookups >= 0); } @@ -330,7 +318,6 @@ public void testIFs() { IFmsgHdr2 if2m = new IFmsgHdr2(p); if2m.read(); - assertTrue(if2m.ifm_index >= 0); assertTrue(if2m.ifm_data.ifi_ibytes >= 0); assertTrue(if2m.ifm_data.ifi_lastchange.tv_usec >= 0);