diff --git a/CHANGES.md b/CHANGES.md index 0fef464881..e948204401 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ Features * [#1191](https://github.com/java-native-access/jna/pull/1191): Add `c.s.j.p.win32.Advapi32Util#getTokenPrimaryGroup` - [@dbwiddis](https://github.com/dbwiddis). * [#1194](https://github.com/java-native-access/jna/pull/1194): Add `GetConsoleScreenBufferInfo`, `ReadConsoleInput` and `WriteConsole` with associated structures to `c.s.j.p.win32.Wincon` - [@rednoah](https://github.com/rednoah). * [#1198](https://github.com/java-native-access/jna/pull/1198): Add `NetSessionEnum` to `c.s.j.p.win32.Netapi32` and `WTSEnumerateSessions`, `WTSQuerySessionInformation`, and `WTSFreeMemory` to `c.s.j.p.win32.Wtsapi32` - [@dbwiddis](https://github.com/dbwiddis). +* [#1200](https://github.com/java-native-access/jna/pull/1200): Add mappings for `libudev` to `c.s.j.p.linux.Udev` - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/linux/Udev.java b/contrib/platform/src/com/sun/jna/platform/linux/Udev.java new file mode 100644 index 0000000000..23db457cfa --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/linux/Udev.java @@ -0,0 +1,550 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * 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.linux; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.PointerType; + +/** + * libudev.h provides APIs to introspect and enumerate devices on the local + * system. Udev objects are opaque and must not be accessed by the caller via + * different means than functions provided by libudev. + */ +public interface Udev extends Library { + + Udev INSTANCE = Native.load("udev", Udev.class); + + /** + * All functions require a libudev context to operate. This context can be + * created via {@link #udev_new}. It is used to track library state and link + * objects together. No global state is used by libudev, everything is always + * linked to a udev context. Furthermore, multiple different udev contexts can + * be used in parallel by multiple threads. However, a single context must not + * be accessed by multiple threads in parallel. The caller is responsible for + * providing suitable locking if they intend to use it from multiple threads. + */ + class UdevContext extends PointerType { + /** + * Acquire a further reference to this object. + * + * @return this object, unmodified. + */ + UdevContext ref() { + return INSTANCE.udev_ref(this); + } + + /** + * Drop a reference to this object. Once the reference count hits 0, the context + * object is destroyed and freed. + */ + public void unref() { + INSTANCE.udev_unref(this); + } + + /** + * Create a udev enumerate object. Initially, the reference count of the + * enumerate object is 1. + * + * @return On success, returns the allocated enumerator. On failure, NULL is + * returned. + */ + public UdevEnumerate enumerateNew() { + return INSTANCE.udev_enumerate_new(this); + } + + /** + * Creates a udev device object based on information found in {@code /sys}, + * annotated with properties from the udev-internal device database. Initially, + * the reference count of the device is 1. + * + * @param syspath + * The path of the device in {@code /sys}. + * @return the allocated udev device. On failure, NULL is returned, and + * {@code errno} is set appropriately. + */ + public UdevDevice deviceNewFromSyspath(String syspath) { + return INSTANCE.udev_device_new_from_syspath(this, syspath); + } + } + + /** + * To enumerate local devices on the system, an enumeration object can be + * created via {@link UdevContext#enumerateNew()}. + */ + class UdevEnumerate extends PointerType { + /** + * Acquire a further reference to this object. + * + * @return this object, unmodified. + */ + public UdevEnumerate ref() { + return INSTANCE.udev_enumerate_ref(this); + } + + /** + * Drop a reference to this object. Once the reference count hits 0, the context + * object is destroyed and freed. + */ + public void unref() { + INSTANCE.udev_enumerate_unref(this); + } + + /** + * Modify filters of this object to match a subsystem. + * + * @param subsystem + * The subsystem to match + * @return an integer greater than, or equal to, 0 on success. + */ + public int addMatchSubsystem(String subsystem) { + return INSTANCE.udev_enumerate_add_match_subsystem(this, subsystem); + } + + /** + * Query this object. Scans {@code /sys} for all devices which match the given + * filters. No filters will return all currently available devices. + * + * @return an integer greater than, or equal to, 0 on success. + */ + public int scanDevices() { + return INSTANCE.udev_enumerate_scan_devices(this); + } + + /** + * Get the first list entry from this object. + * + * @return On success, returns the first entry in the list of found devices. If + * the list is empty, or on failure, NULL is returned. + */ + public UdevListEntry getListEntry() { + return INSTANCE.udev_enumerate_get_list_entry(this); + } + } + + /** + * Whenever libudev returns a list of objects, the {@code udev_list_entry} API + * should be used to iterate, access and modify those lists. + */ + class UdevListEntry extends PointerType { + /** + * Gets the next entry in the enumeration. + * + * @return On success, returns the next list entry. If no such entry can be + * found, or on failure, NULL is returned. + */ + public UdevListEntry getNext() { + return INSTANCE.udev_list_entry_get_next(this); + } + + /** + * Get the name of this entry, which is the path of the device in {@code /sys}. + * + * @return A string representing the syspath. On failure, NULL is returned. + */ + public String getName() { + return INSTANCE.udev_list_entry_get_name(this); + } + } + + /** + * To introspect a local device on a system, a udev device object can be created + * via {@link UdevContext#deviceNewFromSyspath(String)} and friends. The device + * object allows one to query current state, read and write attributes and + * lookup properties of the device in question. + */ + class UdevDevice extends PointerType { + /** + * Acquire a further reference to this object. + * + * @return this object, unmodified. + */ + public UdevDevice ref() { + return INSTANCE.udev_device_ref(this); + } + + /** + * Drop a reference to this object. Once the reference count hits 0, the context + * object is destroyed and freed. + */ + public void unref() { + INSTANCE.udev_device_unref(this); + } + + /** + * Gets the parent of this device + * + * @return the parent device. No additional reference to this device is + * acquired, but the child device owns a reference to the parent device. + * On failure, NULL is returned. + */ + public UdevDevice getParent() { + return INSTANCE.udev_device_get_parent(this); + } + + /** + * Gets the parent of this device matching a subsystem and devtype + * + * @param subsystem + * The subsystem to match + * @param devtype + * The device type to match + * @return the parent device. No additional reference to this device is + * acquired, but the child device owns a reference to the parent device. + * On failure, NULL is returned. + */ + public UdevDevice getParentWithSubsystemDevtype(String subsystem, String devtype) { + return INSTANCE.udev_device_get_parent_with_subsystem_devtype(this, subsystem, devtype); + } + + /** + * Gets the syspath of this device + * + * @return a string that describes the syspath. On failure, may return NULL. + */ + public String getSyspath() { + return INSTANCE.udev_device_get_syspath(this); + } + + /** + * Gets the sysname of this device + * + * @return a string that describes the sysname. On failure, may return NULL. + */ + public String getSysname() { + return INSTANCE.udev_device_get_syspath(this); + } + + /** + * Gets the devnode of this device + * + * @return a string that describes the devnode. On failure, may return NULL. + */ + public String getDevnode() { + return INSTANCE.udev_device_get_devnode(this); + } + + /** + * Gets the devtype of this device + * + * @return a string that describes the devtype. On failure, may return NULL. + */ + public String getDevtype() { + return INSTANCE.udev_device_get_devtype(this); + } + + /** + * Gets the subsystem of this device + * + * @return a string that describes the subsystem. On failure, may return NULL. + */ + public String getSubsystem() { + return INSTANCE.udev_device_get_subsystem(this); + } + + /** + * Retrieves a device attribute from this device + * + * @param sysattr + * The attribute to retrieve. + * @return a string of the requested value. On error, NULL is returned. + * Attributes that may contain NUL bytes should not be retrieved with + * udev_device_get_sysattr_value(); instead, read them directly from the + * files within the device's syspath. + */ + public String getSysattrValue(String sysattr) { + return INSTANCE.udev_device_get_sysattr_value(this, sysattr); + } + + /** + * Retrieves a device property from this device + * + * @param key + * The property to retrieve. + * @return a string of the requested value. On error, NULL is returned. + */ + public String getPropertyValue(String key) { + return INSTANCE.udev_device_get_property_value(this, key); + } + } + + /** + * Allocates a new udev context object and returns a pointer to it. This object + * is opaque and must not be accessed by the caller via different means than + * functions provided by libudev. Initially, the reference count of the context + * is 1. + * + * @return On success, returns a pointer to the allocated udev context. On + * failure, NULL is returned. + */ + UdevContext udev_new(); + + /** + * Acquire further references to a udev context object. + * + * @param udev + * A udev context object. + * @return the argument that was passed, unmodified. + */ + UdevContext udev_ref(UdevContext udev); + + /** + * Drop a reference to a udev context object. Once the reference count hits 0, + * the context object is destroyed and freed. + * + * @param udev + * A udev context object. + * @return always returns NULL. + */ + UdevContext udev_unref(UdevContext udev); + + /** + * Allocates a new udev device object and returns a pointer to it. This object + * is opaque and must not be accessed by the caller via different means than + * functions provided by libudev. Initially, the reference count of the device + * is 1. + *

+ * Creates the device object based on information found in {@code /sys}, + * annotated with properties from the udev-internal device database. A syspath + * is any subdirectory of {@code /sys}, with the restriction that a subdirectory + * of {@code /sys/devices} (or a symlink to one) represents a real device and as + * such must contain a uevent file. + * + * @param udev + * A udev context object. + * @param syspath + * The path of the device in {@code /sys}. + * @return a pointer to the allocated udev device. On failure, NULL is returned, + * and {@code errno} is set appropriately. + */ + UdevDevice udev_device_new_from_syspath(UdevContext udev, String syspath); + + /** + * Create a udev enumerate object. Initially, the reference count of the + * enumerate object is 1. + * + * @param udev + * A udev context object. + * @return On success, returns a pointer to the allocated udev monitor. On + * failure, NULL is returned. + */ + UdevEnumerate udev_enumerate_new(UdevContext udev); + + /** + * Acquire further references to a udev enumerate object. + * + * @param udev_enumerate + * A udev enumerate object. + * @return the argument that was passed, unmodified. + */ + UdevEnumerate udev_enumerate_ref(UdevEnumerate udev_enumerate); + + /** + * Drop a reference to a udev enumerate object. Once the reference count hits 0, + * the enumerate object is destroyed and freed. + * + * @param udev_enumerate + * A udev enumerate object. + * @return always returns NULL. + */ + UdevEnumerate udev_enumerate_unref(UdevEnumerate udev_enumerate); + + /** + * Modify filters of a udev enumerate object to match a subsystem. + * + * @param udev_enumerate + * The udev enumerate object to modify. + * @param subsystem + * The subsystem to match + * @return an integer greater than, or equal to, 0 on success. + */ + int udev_enumerate_add_match_subsystem(UdevEnumerate udev_enumerate, String subsystem); + + /** + * Query a udev enumerate object. Scans {@code /sys} for all devices which match + * the given filters. No matches will return all currently available devices. + * + * @param udev_enumerate + * The udev enumerate object, with optional filters. + * @return an integer greater than, or equal to, 0 on success. + */ + int udev_enumerate_scan_devices(UdevEnumerate udev_enumerate); + + /** + * Get the first list entry from a udev enumerate object. + * + * @param udev_enumerate + * The udev enumerate object. + * @return On success, returns a pointer to the first entry in the list of found + * devices. If the list is empty, or on failure, NULL is returned. + */ + UdevListEntry udev_enumerate_get_list_entry(UdevEnumerate udev_enumerate); + + /** + * Gets the next entry in the enumeration. + * + * @param list_entry + * the current list entry + * @return On success, returns a pointer to the next list entry. If no such + * entry can be found, or on failure, NULL is returned. + */ + UdevListEntry udev_list_entry_get_next(UdevListEntry list_entry); + + /** + * Get the name of the udev list entry + * + * @param list_entry + * A udev list entry + * @return a pointer to a constant string representing the requested value. The + * string is bound to the lifetime of the list entry itself. On failure, + * NULL is returned. + */ + String udev_list_entry_get_name(UdevListEntry list_entry); + + /** + * Acquire further references to a udev device object. + * + * @param udev_device + * A udev device object. + * @return the argument that was passed, unmodified. + */ + UdevDevice udev_device_ref(UdevDevice udev_device); + + /** + * Drop a reference to a udev device object. Once the reference count hits 0, + * the device object is destroyed and freed. + * + * @param udev_device + * A udev device object. + * @return always returns NULL. + */ + UdevDevice udev_device_unref(UdevDevice udev_device); + + /** + * Gets the parent of a udev device + * + * @param udev_device + * A udev device object. + * @return a pointer to the parent device. No additional reference to this + * device is acquired, but the child device owns a reference to such a + * parent device. On failure, NULL is returned. + */ + UdevDevice udev_device_get_parent(UdevDevice udev_device); + + /** + * Gets the parent of a udev device matching a subsystem and devtype + * + * @param udev_device + * A udev device object. + * @param subsystem + * The subsystem to match + * @param devtype + * The device type to match + * @return a pointer to the parent device. No additional reference to this + * device is acquired, but the child device owns a reference to such a + * parent device. On failure, NULL is returned. + */ + UdevDevice udev_device_get_parent_with_subsystem_devtype(UdevDevice udev_device, String subsystem, String devtype); + + /** + * Gets the syspath of a udev device + * + * @param udev_device + * A udev device object. + * @return a pointer to a constant string that describes the syspath. The + * lifetime of this string is bound to the device it was requested on. + * On failure, may return NULL. + */ + String udev_device_get_syspath(UdevDevice udev_device); + + /** + * Gets the sysname of a udev device + * + * @param udev_device + * A udev device object. + * @return a pointer to a constant string that describes the sysname. The + * lifetime of this string is bound to the device it was requested on. + * On failure, may return NULL. + */ + String udev_device_get_sysname(UdevDevice udev_device); + + /** + * Gets the devnode of a udev device + * + * @param udev_device + * A udev device object. + * @return a pointer to a constant string that describes the devnode. The + * lifetime of this string is bound to the device it was requested on. + * On failure, may return NULL. + */ + String udev_device_get_devnode(UdevDevice udev_device); + + /** + * Gets the devtype of a udev device + * + * @param udev_device + * A udev device object. + * @return a pointer to a constant string that describes the devtype. The + * lifetime of this string is bound to the device it was requested on. + * On failure, may return NULL. + */ + String udev_device_get_devtype(UdevDevice udev_device); + + /** + * Gets the subsystem of a udev device + * + * @param udev_device + * A udev device object. + * @return a pointer to a constant string that describes the subsystem. The + * lifetime of this string is bound to the device it was requested on. + * On failure, may return NULL. + */ + String udev_device_get_subsystem(UdevDevice udev_device); + + /** + * Retrieves a device attributesfrom a udev device. + * + * @param udev_device + * A udev device object. + * @param sysattr + * The attribute to retrieve. + * @return a pointer to a constant string of the requested value. On error, NULL + * is returned. Attributes that may contain NUL bytes should not be + * retrieved with udev_device_get_sysattr_value(); instead, read them + * directly from the files within the device's syspath. + */ + String udev_device_get_sysattr_value(UdevDevice udev_device, String sysattr); + + /** + * Retrieves a device property from a udev device. + * + * @param udev_device + * A udev device object. + * @param key + * The property to retrieve. + * @return a pointer to a constant string of the requested value. On error, NULL + * is returned. + */ + String udev_device_get_property_value(UdevDevice udev_device, String key); +} \ No newline at end of file diff --git a/contrib/platform/test/com/sun/jna/platform/linux/UdevTest.java b/contrib/platform/test/com/sun/jna/platform/linux/UdevTest.java new file mode 100644 index 0000000000..90dc035fb9 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/linux/UdevTest.java @@ -0,0 +1,114 @@ +/* Copyright (c) 2020 Daniel Widdis, All Rights Reserved + * + * 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.linux; + +import org.junit.Test; + +import com.sun.jna.platform.linux.Udev.UdevContext; +import com.sun.jna.platform.linux.Udev.UdevDevice; +import com.sun.jna.platform.linux.Udev.UdevEnumerate; +import com.sun.jna.platform.linux.Udev.UdevListEntry; + +import junit.framework.TestCase; + +/** + * Exercise the {@link Udev} class. + */ +public class UdevTest extends TestCase { + + @Test + public void testEnumerateDevices() { + // Start with the context object + UdevContext udev = Udev.INSTANCE.udev_new(); + try { + assertNotNull("Failed to create udev context", udev); + // Create an enumerator + UdevEnumerate enumerate = udev.enumerateNew(); + try { + assertNotNull("Failed to create udev enumerator", enumerate); + // It's a good assumption the machine we're on has a disk and partition + // Add a filter for block devices and scan them + enumerate.addMatchSubsystem("block"); + enumerate.scanDevices(); + // Enumerator now points to block devices. + // Iterate the list + for (UdevListEntry entry = enumerate.getListEntry(); entry != null; entry = entry.getNext()) { + String syspath = entry.getName(); + assertEquals("Udev entry name should start with /sys", "/sys", syspath.substring(0, 4)); + UdevDevice device = udev.deviceNewFromSyspath(syspath); + if (device != null) { + try { + // devnode is what we use as name, like /dev/sda + String devnode = device.getDevnode(); + // Ignore loopback and ram disks + if (devnode != null && !devnode.startsWith("/dev/loop") + && !devnode.startsWith("/dev/ram")) { + String devType = device.getDevtype(); + // For partition, grab values of parent disk + String parentSize = null; + String parentMajor = null; + if ("partition".equals(devType)) { + UdevDevice parent = device.getParentWithSubsystemDevtype("block", "disk"); + UdevDevice parent2 = device.getParent(); + // No additional reference is acquired from getParent + if (parent != null && parent2 != null) { + // Devnode should match same parent without restricting to block and disk + assertEquals("Partition parent should match with and without filter", + parent.getDevnode(), parent2.getDevnode()); + // Save the size and major + parentSize = parent.getSysattrValue("size"); + parentMajor = parent.getPropertyValue("MAJOR"); + } + } + String size = device.getSysattrValue("size"); + assertTrue("Size must be nonnegative", 0 <= Long.parseLong(size)); + if (parentSize != null) { + assertTrue("Partition can't be bigger than its disk", + Long.parseLong(size) <= Long.parseLong(parentSize)); + } + String major = device.getPropertyValue("MAJOR"); + assertTrue("Major value must be nonnegative", 0 <= Long.parseLong(major)); + if (parentMajor != null) { + assertEquals("Partition and its parent disk should have same major number", major, + parentMajor); + } + assertEquals("DevType mismatch", devType, device.getDevtype()); + assertEquals("Subsystem mismatch", "block", device.getSubsystem()); + assertEquals("Syspath mismatch", syspath, device.getSyspath()); + assertTrue("Syspath should end with name", syspath.endsWith(device.getSysname())); + } + } finally { + // Release the reference and iterate to the next device + device.unref(); + } + } + } + } finally { + enumerate.unref(); + } + } finally { + udev.unref(); + } + } +}