Skip to content

Port remaining Linux JNA classes to FFM#3121

Merged
dbwiddis merged 5 commits intooshi:masterfrom
dbwiddis:udev-ffm
Apr 6, 2026
Merged

Port remaining Linux JNA classes to FFM#3121
dbwiddis merged 5 commits intooshi:masterfrom
dbwiddis:udev-ffm

Conversation

@dbwiddis
Copy link
Copy Markdown
Member

@dbwiddis dbwiddis commented Apr 6, 2026

Completes the FFM port of all remaining Linux-specific classes, following the established pattern: abstract base (no JNA) in oshi-core*JNA subclass in oshi-core*FFM subclass in oshi-core-java25.

Changes

New FFM bindings (oshi-core-java25)

  • LinuxLibcFunctions — FFM bindings for getpid, gettid,
    syscall(SYS_GETTID), getloadavg, sysinfo, statvfs,
    gethostname, getaddrinfo/freeaddrinfo/gai_strerror, getrlimit.
    Includes struct layouts: SYSINFO_LAYOUT, STATVFS_LAYOUT,
    RLIMIT_LAYOUT, ADDRINFO_LAYOUT.

Refactored classes

Class Change
LinuxCentralProcessor Abstract base; getSystemLoadAverage reads /proc/loadavg as fallback
LinuxCentralProcessorJNA Uses getloadavg() via JNA; falls back to super on failure
LinuxCentralProcessorFFM Uses LinuxLibcFunctions.getloadavg; falls back to super on any Throwable
LinuxUsbDevice Renamed to LinuxUsbDeviceJNA; new abstract LinuxUsbDevice base holds shared template method, constants, and tree-building logic
LinuxUsbDeviceFFM Now extends LinuxUsbDevice; implements enumerateUsbDevices via UdevFunctions
LinuxFileSystem Abstract base extracted; LinuxFileSystemJNA and LinuxFileSystemFFM added
LinuxNetworkParams Abstract base extracted; LinuxNetworkParamsJNA and LinuxNetworkParamsFFM added
LinuxOSProcess Abstract base extracted; LinuxOSProcessJNA and LinuxOSProcessFFM added
LinuxOperatingSystem Abstract base; LinuxOperatingSystemJNA cleaned up (removed redundant override)
LinuxOperatingSystemFFM All abstract methods implemented via FFM/LinuxLibcFunctions

Minor fixes

  • Added ProcPath.LOADAVG constant.
  • Removed redundant getProcessCount override in LinuxOperatingSystemJNA
    (was identical to base class).
  • Updated LinuxHardwareAbstractionLayerJNA to reference LinuxUsbDeviceJNA.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Foreign Function & Memory (FFM) API support for Linux system operations as an alternative to JNA, enabling direct native function calls for process management, filesystem queries, and network parameters.
  • Refactoring

    • Restructured Linux OS and process classes to support pluggable backend implementations, allowing both FFM and JNA-based implementations to coexist.
  • Bug Fixes

    • Improved system load average handling to properly parse and handle missing values.

- Refactor LinuxCentralProcessor: move getSystemLoadAverage to base
  using /proc/loadavg fallback; JNA subclass uses getloadavg() with
  fallback to super; FFM subclass uses LinuxLibcFunctions with fallback
- Refactor LinuxUsbDevice: rename to LinuxUsbDeviceJNA, create abstract
  LinuxUsbDevice base with shared template method; LinuxUsbDeviceFFM
  extends base using UdevFunctions
- Refactor LinuxFileSystem, LinuxNetworkParams, LinuxOSProcess,
  LinuxOperatingSystem: extract abstract bases in oshi-core, add JNA
  subclasses, add FFM subclasses in oshi-core-java25
- Add LinuxLibcFunctions FFM bindings: getpid, gettid, getloadavg,
  sysinfo, statvfs, gethostname, getaddrinfo/freeaddrinfo, getrlimit
- Add ProcPath.LOADAVG constant
- Remove redundant getProcessCount override in LinuxOperatingSystemJNA
- Update LinuxHardwareAbstractionLayerJNA to use LinuxUsbDeviceJNA
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR migrates Linux platform code from JNA to FFM (Foreign Function & Memory API) by introducing comprehensive FFM bindings, converting concrete classes to abstract base classes, and implementing both FFM and JNA variants. A new LinuxLibcFunctions class provides FFM downcalls for libc functions; abstract base classes (LinuxOperatingSystem, LinuxOSProcess, LinuxNetworkParams, LinuxFileSystem) enable dual implementation paths; new FFM-backed classes provide native interop; and new JNA-backed classes preserve legacy functionality.

Changes

Cohort / File(s) Summary
FFM Bindings
oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
New 438-line FFM class providing downcall bindings for Linux libc APIs (getpid, gettid, syscall, getloadavg, sysinfo, statvfs, gethostname, getaddrinfo, freeaddrinfo, gai_strerror, getrlimit), struct layouts (sysinfo, statvfs, rlimit, addrinfo), platform-specific syscall number computation, and accessor methods.
FFM Linux Implementations
oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java, oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java, oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java, oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java, oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
New FFM-backed implementation classes providing native access via FFM: getSystemLoadAverage(), queryStatvfs(), hostname/domain resolution, getrlimit(), and OS-level process/filesystem/network queries with Arena-based memory management and exception fallbacks.
Abstract Base Classes & Refactoring
oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParams.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxOSFileStore.java
Converted classes to abstract, removed native implementations, introduced abstract hooks (createOSProcess, queryStatvfs, queryRlimitSoft/Hard) for subclass override, made constructors protected, and refactored static methods to instance methods.
JNA Implementations
oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java, oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
New concrete JNA-backed implementations preserving legacy functionality; expose capability flags (HAS_UDEV, HAS_GETTID, HAS_SYSCALL_GETTID) and provide native method bindings.
Udev Reference Updates
oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
Updated static imports to reference LinuxOperatingSystemJNA.HAS_UDEV instead of LinuxOperatingSystem.HAS_UDEV for capability detection.
LoadAvg Implementation Changes
oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
Changed LinuxCentralProcessor.getSystemLoadAverage() to read /proc/loadavg instead of libc getloadavg(); added LinuxCentralProcessorJNA override using JNA-based getloadavg().
Utilities & Documentation
oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java, CHANGELOG.md
Added LOADAVG constant to ProcPath; consolidated changelog entries for FFM migration PRs #3117, #3120, #3121.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant OSFactory as OS Factory
    participant LinuxOSAbs as LinuxOperatingSystem<br/>(abstract)
    participant LinuxOSJNA as LinuxOperatingSystemJNA
    participant LinuxOSFFM as LinuxOperatingSystemFFM
    participant JNAImpl as JNA Implementation<br/>(LibC, etc.)
    participant FFMImpl as FFM Implementation<br/>(LinuxLibcFunctions)

    App->>OSFactory: Create OS instance
    alt Factory selects JNA variant
        OSFactory->>LinuxOSJNA: new LinuxOperatingSystemJNA()
        Note over LinuxOSJNA: Initializes HAS_UDEV, HAS_GETTID, HAS_SYSCALL_GETTID
        App->>LinuxOSJNA: getProcessId()
        LinuxOSJNA->>JNAImpl: LinuxLibc.INSTANCE.getpid()
        JNAImpl-->>LinuxOSJNA: pid
        LinuxOSJNA-->>App: processId
    else Factory selects FFM variant
        OSFactory->>LinuxOSFFM: new LinuxOperatingSystemFFM()
        App->>LinuxOSFFM: getProcessId()
        LinuxOSFFM->>FFMImpl: LinuxLibcFunctions.getpid()
        FFMImpl->>FFMImpl: invokeExact(getpidHandle)
        FFMImpl-->>LinuxOSFFM: pid
        LinuxOSFFM-->>App: processId
    end
Loading
sequenceDiagram
    participant App as Application
    participant FileSysAbs as LinuxFileSystem<br/>(abstract)
    participant FileSysJNA as LinuxFileSystemJNA
    participant FileSysFFM as LinuxFileSystemFFM
    participant OSFileStore as OSFileStore
    participant JNALib as LibC (JNA)
    participant FFMLib as LinuxLibcFunctions (FFM)

    App->>FileSysAbs: getFileStores(localOnly)
    alt Implementation: JNA
        FileSysAbs->>FileSysJNA: queryStatvfs(path)
        FileSysJNA->>JNALib: statvfs(path, buf)
        JNALib-->>FileSysJNA: struct statvfs
        FileSysJNA->>FileSysJNA: Extract f_files, f_ffree, blocks*frsize
        FileSysJNA-->>FileSysAbs: long[] { inodes, free, space, usable, free }
    else Implementation: FFM
        FileSysAbs->>FileSysFFM: queryStatvfs(path)
        FileSysFFM->>FFMLib: getloadavg(pathSeg, buf)
        FFMLib->>FFMLib: Arena-allocated native memory
        FFMLib->>FFMLib: invokeExact(statvfs handle)
        FFMLib-->>FileSysFFM: int (return code)
        FileSysFFM->>FileSysFFM: Extract via VarHandle accessors
        FileSysFFM-->>FileSysAbs: long[] { inodes, free, space, usable, free }
    end
    FileSysAbs->>OSFileStore: Create with stats
    OSFileStore-->>App: FileStore object
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hops of Joy for FFM's Flight

JNA paths now fork in two—
Abstract bases born anew,
FFM bindings swift and lean,
Memory-safe, a cleaner scene,
Both roads lead to Linux's heart,
FFM and JNA play their part! 🎭

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Port remaining Linux JNA classes to FFM' directly and clearly describes the primary change—migrating Linux JNA classes to use the Foreign Function & Memory API, which is the main objective evident across the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.70%. Comparing base (180c940) to head (015ddc2).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3121      +/-   ##
==========================================
+ Coverage   66.81%   67.70%   +0.88%     
==========================================
  Files          37       37              
  Lines        2025     2025              
  Branches      336      336              
==========================================
+ Hits         1353     1371      +18     
+ Misses        557      539      -18     
  Partials      115      115              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Use LINKER.defaultLookup() instead of libraryLookup("c") to resolve
libc symbols. libraryLookup maps "c" to "libc.so" via
System.mapLibraryName, which fails on Linux where the actual soname
is "libc.so.6". libc is always linked into the JVM process so the
default lookup covers all its symbols without opening the library
by name.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java (1)

95-111: Consider simplifying the redundant hasGettid() check.

At line 99, LinuxLibcFunctions.hasGettid() is checked again inside the HAS_SYSCALL_GETTID block, but this was already evaluated during static initialization at line 50. If HAS_SYSCALL_GETTID is true and hasGettid() was true at init time, it will still be true here. The current code is safe but slightly redundant.

That said, rechecking at runtime provides defense against any hypothetical scenario where the function pointer could become invalid, so this is acceptable as a defensive measure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java`
around lines 95 - 111, The getThreadId method redundantly calls
LinuxLibcFunctions.hasGettid() inside the HAS_SYSCALL_GETTID guard; remove that
runtime check and directly invoke the appropriate getter (use
LinuxLibcFunctions.gettid() when HAS_SYSCALL_GETTID is true) and fall back to
the Files.readSymbolicLink parsing as before; update the try block in
getThreadId to call LinuxLibcFunctions.gettid() (and keep the existing catch and
fallback) so you no longer re-evaluate LinuxLibcFunctions.hasGettid() at
runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java`:
- Around line 45-53: The SYS_GETTID mapping currently sets a fallback of 224 for
unknown arches which is incorrect for several architectures; update the logic in
LinuxLibcFunctions where SYS_GETTID is assigned (the block using
System.getProperty("os.arch") and the SYS_GETTID constant) to explicitly handle
riscv64, s390x, ppc64le, and loongarch64 with the correct syscall numbers
(riscv64 -> 178, s390x -> 236, ppc64le -> 207, loongarch64 -> 178), keep
existing mappings for aarch64/arm64 and amd64/x86_64, and use a safe default or
throw an informative exception/log if an unknown/unsupported arch is encountered
so gettid() probes aren't silently incorrect.
- Around line 262-264: The method sysinfoProcs currently returns a signed short
but should expose the unsigned 16-bit sysinfo.procs; change the signature of
sysinfoProcs(MemorySegment info) to return int and convert the raw 16-bit value
from SYSINFO_PROCS to an unsigned int (e.g., mask with 0xFFFF or use
Short.toUnsignedInt) before returning; update any callers to handle an int
instead of short.
- Around line 174-175: The gethostname native binding currently declares its
second parameter as JAVA_INT, which is incorrect on LP64 Linux where size_t is
64-bit; update the FunctionDescriptor.of(...) call used in LINKER.downcallHandle
for gethostname to use JAVA_LONG for the second parameter instead of JAVA_INT,
and change the corresponding wrapper method signature/parameter type for
gethostname from int to long so the call and ABI match; apply the same change
pattern to the similar binding located at the other occurrence referenced (lines
344-345) so both LINKER.downcallHandle(FunctionDescriptor.of(...)) and their
Java wrapper parameter types use JAVA_LONG.

In `@oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java`:
- Around line 62-64: The code calls Udev.INSTANCE.udev_new() and immediately
uses the returned Udev.UdevContext (variable udev) to call enumerateNew(), but
udev_new() can return null; add a null check after Udev.INSTANCE.udev_new() and
handle the failure path (log or return early) before calling
udev.enumerateNew(). In other words, check if udev == null after the udev_new()
call in LinuxUsbDeviceJNA.java and avoid calling udev.enumerateNew() (or clean
up) when null to prevent a NullPointerException.

In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java`:
- Around line 46-57: The getDomainName() block leaks native memory because the
Addrinfo returned by LIBC.getaddrinfo(...) is freed only via Addrinfo.close()
which calls Util.freeMemory() (no-op for native C allocations); update the
try-with-resources handling around CloseablePointerByReference and Addrinfo so
that after using new Addrinfo(ptr.getValue()) you explicitly call
LIBC.freeaddrinfo(ptr.getValue()) (or LIBC.freeaddrinfo(info.getPointer()) if
available) before closing/letting Addrinfo be garbage-collected, mirroring the
pattern used in MacNetworkParams/FreeBsdNetworkParams; ensure you still return
the canonical name (info.ai_canonname) and handle nulls as before.

In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java`:
- Line 53: The change made LinuxOperatingSystem into an abstract type with a
protected constructor and a new abstract factory, which is a public API break;
either revert the class to a concrete public class with a public constructor
(restore LinuxOperatingSystem to its previous non-abstract declaration and
public ctor) or add a deprecated compatibility shim by introducing a public
subclass (e.g., DeprecatedLinuxOperatingSystem) that provides a public
constructor and implements the new abstract factory methods, marked `@Deprecated`
and delegating to the new API; update all affected locations (the
LinuxOperatingSystem class, its constructor, and the newly introduced abstract
factory methods referenced around the changed blocks) so external subclasses can
still instantiate without changing their code.

In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java`:
- Around line 23-33: The getrlimit() return value is currently ignored in
queryRlimitSoft() and queryRlimitHard(), risking use of uninitialized
Resource.Rlimit fields on error; update both methods (queryRlimitSoft and
queryRlimitHard) to capture the int result from
LinuxLibc.INSTANCE.getrlimit(LinuxLibc.RLIMIT_NOFILE, rlimit), check for success
(0) and on failure call and return the fallback getProcessOpenFileLimit();
ensure you only read rlimit.rlim_cur or rlim_max when getrlimit succeeded and
otherwise return the fallback value, mirroring the error-handling pattern used
in LinuxOSProcessFFM.

---

Nitpick comments:
In
`@oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java`:
- Around line 95-111: The getThreadId method redundantly calls
LinuxLibcFunctions.hasGettid() inside the HAS_SYSCALL_GETTID guard; remove that
runtime check and directly invoke the appropriate getter (use
LinuxLibcFunctions.gettid() when HAS_SYSCALL_GETTID is true) and fall back to
the Files.readSymbolicLink parsing as before; update the try block in
getThreadId to call LinuxLibcFunctions.gettid() (and keep the existing catch and
fallback) so you no longer re-evaluate LinuxLibcFunctions.hasGettid() at
runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dc75890a-8108-4c23-b2c5-867037f245f1

📥 Commits

Reviewing files that changed from the base of the PR and between 180c940 and c2aee82.

📒 Files selected for processing (25)
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParams.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Test JDK 11, windows-latest
  • GitHub Check: Test JDK 11, macos-latest
  • GitHub Check: Test JDK 25, windows-latest
  • GitHub Check: Test JDK 25, macos-latest
  • GitHub Check: Analyze (java)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.
📚 Learning: 2026-04-05T15:37:16.042Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java:59-65
Timestamp: 2026-04-05T15:37:16.042Z
Learning: In OSHI's Linux USB device implementations (LinuxUsbDeviceFFM, and the JNA equivalent), when `getUsbDevices(boolean tree)` is called with `tree=false`, the returned flat list intentionally **includes** controller nodes — it does not exclude them. The existing Javadoc that says "returns a flat list excluding controllers" is incorrect. Do not flag the implementation for including controllers in flat mode; instead, flag only the Javadoc if it claims controllers are excluded.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
📚 Learning: 2026-04-05T05:40:43.357Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-04-05T05:39:18.791Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
📚 Learning: 2026-04-05T16:15:03.230Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java:95-97
Timestamp: 2026-04-05T16:15:03.230Z
Learning: In `LinuxLogicalVolumeGroupJNA.getLogicalVolumeGroups()` (oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java), `physicalVolumesMap.get(e.getKey())` in the final stream mapping cannot return null: both `logicalVolumesMap` and `physicalVolumesMap` are populated via `computeIfAbsent` inside the same conditional block (`!Util.isBlank(vgName) && !Util.isBlank(lvName)`), so every VG key in `logicalVolumesMap` is guaranteed to have a corresponding entry in `physicalVolumesMap`. Do not flag this `.get()` call as a potential null-return issue. The FFM version uses `getOrDefault` as extra defensiveness, not because null is possible here.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
📚 Learning: 2026-03-29T20:01:06.452Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java11/src/main/java/module-info.java:10-17
Timestamp: 2026-03-29T20:01:06.452Z
Learning: In the oshi project, `oshi-core-java25/src/main/java/module-info.java` intentionally does NOT export `oshi.util.platform.unix.freebsd`, `oshi.util.platform.unix.openbsd`, or `oshi.util.platform.unix.solaris`, because the FFM (Foreign Function & Memory) implementation only supports Windows, macOS, and Linux. This differs from `oshi-core-java11/src/main/java/module-info.java` which exports all Unix platform packages. The asymmetry is by design.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-04-05T05:40:43.357Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI’s Linux FFM implementations (e.g., LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the corresponding JNA udev code, do not require/flag a missing null check around `udev_enumerate_new()`/`enumerateNew()`. Per libudev semantics, `udev_enumerate_new()` returns NULL only if udev itself is broken (e.g., OOM). Since `udev_new()` is already guarded and is the successful prerequisite for enumeration, treating `udev_enumerate_new()` as non-null is consistent with the established pattern; adding a redundant null guard would be inconsistent.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-04-05T05:39:18.791Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: When reviewing OSHI Linux FFM/JNA code that uses libudev, do not flag a missing null check on `udev_enumerate_new()` if `udev_new()` has already been guarded. Per libudev semantics, `udev_enumerate_new()` returns NULL only when the udev context itself is broken (e.g., OOM), so a successful `udev_new()` implies `udev_enumerate_new()` should succeed as well. Therefore, lack of a redundant null guard on `udev_enumerate_new()` is acceptable in this pattern.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-04-03T02:07:47.621Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3110
File: oshi-core-java25/src/main/java/oshi/hardware/platform/mac/MacHWDiskStoreFFM.java:119-121
Timestamp: 2026-04-03T02:07:47.621Z
Learning: In OSHI’s Java FFM/API implementation classes (e.g., *FFM.java) that implement/override `updateAttributes()`, preserve the intentional failure-handling pattern: catch `Throwable` and return `false` without logging. `updateAttributes()` is a public API whose contract is to signal failure via the boolean return value, and callers rely on that return value (so emitting logs from this path is not desired and should not be introduced during review unless the method contract changes).

Applied to files:

  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-04-05T15:37:16.042Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java:59-65
Timestamp: 2026-04-05T15:37:16.042Z
Learning: For OSHI Linux USB device implementations, the behavior of `getUsbDevices(boolean tree)` with `tree=false` is intentional: the returned flat list includes controller nodes (it is not meant to exclude them). In code review, do not flag the implementation for including controllers in flat mode; instead, only flag/update documentation/JavaDoc that incorrectly claims controllers are excluded (e.g., Javadoc text like "returns a flat list excluding controllers" should be corrected).

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-03-29T20:03:00.830Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java25/src/main/java/oshi/util/platform/windows/PdhUtilFFM.java:87-89
Timestamp: 2026-03-29T20:03:00.830Z
Learning: In the OSHI FFM codebase (oshi-core-java25), all PDH/Kernel32/COM wrapper methods in classes like PdhFFM, Kernel32FFM, etc. declare `throws Throwable` solely as a compiler artifact of `MethodHandle.invokeExact`. They are native calls and do not throw Java checked exceptions in practice. The enclosing `getOpenFileDescriptors()` method in PdhUtilFFM already has an outer `catch (Throwable t)` that covers the entire method body including any `finally` blocks, so no exception-masking concern exists for cleanup calls like `PdhCloseQuery` in `finally`.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
🔇 Additional comments (40)
oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java (1)

30-30: LGTM: LOADAVG belongs in ProcPath.

Centralizing /proc/loadavg here keeps the fallback path consistent with the rest of the configurable procfs constants.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java (1)

501-509: LGTM: procfs fallback is clean.

This keeps the base class native-agnostic and preserves the -1d sentinel behavior for missing or unparsable load-average slots.

oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java (1)

185-194: LGTM: the shared PID walk stays centralized.

Using the factory here avoids duplicating the procfs traversal between the JNA and FFM process implementations.

oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java (1)

38-55: LGTM: native load-average fallback is robust.

The FFM path validates nelem, handles partial native results, and cleanly falls back to the procfs implementation on failure.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java (1)

39-40: LGTM: USB delegation follows the rename.

This keeps the HAL aligned with the new abstract-base + JNA-subclass split.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java (1)

7-7: LGTM: the udev capability flag points at the new home.

Importing HAS_UDEV from LinuxOperatingSystemJNA matches where JNA capability detection now lives.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java (1)

7-7: LGTM: correct HAS_UDEV source after the refactor.

The JNA disk path is now gated by the JNA-specific Linux OS capability probe.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java (1)

7-7: JNA-scoped HAS_UDEV import looks correct.

Line 7 cleanly aligns this class with the JNA capability flag source after the class split.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java (1)

7-7: Import migration to JNA HAS_UDEV is consistent.

Line 7 correctly follows the new JNA-specific Linux OS wiring.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java (2)

7-7: HAS_UDEV import update is good.

Line 7 correctly binds this JNA implementation to the JNA OS feature flag.


143-152: Remove suggestion to add try-catch around getloadavg().

The getloadavg() call relies on the return value (retval < 0) for error handling, which is the documented and consistent pattern across all OSHI platforms (Mac, Linux, Solaris, FreeBSD, OpenBSD). Native linkage errors are caught at library initialization time via Native.load(), not at individual method invocations. No other JNA method calls in OSHI are wrapped in try-catch (e.g., getpid(), sysctl(), getrlimit()), and this implementation correctly follows that established pattern.

			> Likely an incorrect or invalid review comment.
oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java (1)

25-39: queryStatvfs contract mapping looks correct.

The returned tuple order and null-on-failure behavior match the base class expectations.

oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java (1)

24-39: JNA statvfs implementation is solid.

The value ordering and failure handling align with the abstract LinuxFileSystem contract.

oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParams.java (1)

15-19: Abstract base conversion is clean.

The updated class role and Javadoc match the new subclass-based native implementations.

oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java (1)

32-45: ⚠️ Potential issue | 🔴 Critical

Check the return value from LinuxLibcFunctions.getrlimit() before reading the rlimit struct.

On lines 32 and 44, getrlimit() returns an int (0 on success, -1 on error) but the return value is ignored. The method does not throw an exception on native failure, so the fallback catch block is never triggered. If getrlimit fails, the rlimit struct remains uninitialized and garbage values are returned.

Add an error check:

int rc = LinuxLibcFunctions.getrlimit(LinuxLibcFunctions.RLIMIT_NOFILE, rlim);
if (rc != 0) {
    throw new IOException("getrlimit failed: " + rc);
}
⛔ Skipped due to learnings
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java25/src/main/java/oshi/util/platform/windows/PdhUtilFFM.java:87-89
Timestamp: 2026-03-29T20:03:00.830Z
Learning: In the OSHI FFM codebase (oshi-core-java25), all PDH/Kernel32/COM wrapper methods in classes like PdhFFM, Kernel32FFM, etc. declare `throws Throwable` solely as a compiler artifact of `MethodHandle.invokeExact`. They are native calls and do not throw Java checked exceptions in practice. The enclosing `getOpenFileDescriptors()` method in PdhUtilFFM already has an outer `catch (Throwable t)` that covers the entire method body including any `finally` blocks, so no exception-masking concern exists for cleanup calls like `PdhCloseQuery` in `finally`.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.
oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java (1)

61-68: LGTM!

The getHostName() implementation correctly allocates a buffer sized to HOST_NAME_MAX + 1 (accounting for null terminator), checks the return value, and falls back to super.getHostName() on failure. This matches the expected pattern from the AbstractNetworkParams fallback in the relevant code snippet.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java (2)

44-46: LGTM on the factory pattern for static entry point.

Creating a temporary instance to invoke the inherited queryUsbDevices(tree) is a clean way to leverage the template method pattern while maintaining API compatibility with the previous static getUsbDevices(boolean tree) signature.


96-101: LGTM on parent device handling.

Correctly identifies root controllers (no USB parent) vs. hub-connected devices. The getParentWithSubsystemDevtype returns a borrowed reference that should not be unref'd, which is handled correctly here.

oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java (3)

49-49: LGTM on abstract class conversion.

The conversion to an abstract class with queryRlimitSoft() and queryRlimitHard() abstract methods cleanly separates the native-specific getrlimit implementations into JNA and FFM subclasses while preserving shared logic in the base class.


286-296: LGTM on the rlimit delegation pattern.

The conditional check getProcessID() == this.os.getProcessId() correctly determines whether native getrlimit can be used (current process only) or must fall back to parsing /proc/pid/limits for other processes. This maintains backward compatibility while enabling native implementations in subclasses.


487-502: LGTM on visibility change.

Changing getProcessOpenFileLimit from private to protected allows subclasses to use this method as a fallback when their native getrlimit implementation fails, which is the intended pattern per the AI summary.

oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java (3)

63-70: LGTM on resource cleanup.

The freeaddrinfo(resPtr) call in the finally block ensures the native memory allocated by getaddrinfo is properly released, even if addrinfoCanoname throws an exception. This is the correct pattern for FFM native memory management.


28-38: LGTM on getHostName() implementation.

Proper use of confined arena for buffer allocation, correct buffer size with null terminator, and graceful fallback to super.getHostName() on any failure. The Throwable catch is appropriate for FFM calls that can throw various exceptions.


49-62: Code correctly sets ai_flags at offset 0.

The ADDRINFO_LAYOUT definition confirms ai_flags is the first field in the struct, so offset 0 is correct. No changes needed.

oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java (2)

47-59: LGTM on gettid capability detection.

The static initialization properly detects syscall availability with a fallback chain: first checks if gettid() function exists directly, then tries syscallGettid() as a fallback. The > 0 check is correct since thread IDs are always positive integers.


113-125: LGTM on getThreadCount() implementation.

Proper use of confined arena for the sysinfo struct allocation, correct error checking on the return value, and appropriate error logging with a 0 fallback. The Throwable catch handles FFM-specific exceptions.

oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java (3)

45-83: LGTM on static capability detection.

The static initialization properly detects library availability with appropriate error handling:

  • HAS_UDEV: Respects config flag and catches UnsatisfiedLinkError
  • HAS_GETTID/HAS_SYSCALL_GETTID: Progressive fallback detection
  • NoClassDefFoundError catch at line 77 handles missing JNA classes gracefully

The logging levels (warn for udev, debug for gettid fallbacks, error for JNA class issues) are appropriately chosen.


128-140: LGTM on getThreadCount() with proper resource management.

The CloseableSysinfo from oshi.jna.Struct (per the relevant code snippet) properly implements AutoCloseable with Util.freeMemory(getPointer()), ensuring no native memory leak. The error handling with Native.getLastError() provides useful diagnostic information.


114-126: LGTM on getThreadId() fallback chain.

Mirrors the FFM implementation with the same logic: prefers gettid() when available, falls back to syscall(SYS_GETTID), and ultimately reads from /proc/thread-self if syscalls are unavailable. The ProcPath.THREAD_SELF constant correctly resolves to /proc/thread-self.

oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java (3)

60-64: LGTM on udev initialization with proper null check.

Unlike the JNA counterpart, this implementation correctly checks for MemorySegment.NULL.equals(udev) before proceeding. Per the retrieved learnings, the subsequent udev_enumerate_new() at line 66 does not need a null guard since a successful udev_new() implies it will succeed.


107-117: LGTM on parent device handling.

Correctly identifies root controllers vs. hub-connected devices. The parent reference from getParentWithSubsystemDevtype is a borrowed reference in libudev semantics, so it correctly doesn't get unref'd. The parentPath null check at line 114 is a good defensive measure.


118-130: LGTM on resource cleanup.

Proper nested try/finally blocks ensure all udev resources are released in reverse order of allocation: device → enumerate → udev context. The Throwable catch at line 128 handles FFM exceptions gracefully.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java (3)

21-34: LGTM on abstract class structure.

The refactoring cleanly extracts the USB enumeration logic into an abstract base class with:

  • Protected constants shared between JNA and FFM implementations
  • Protected constructor restricting instantiation to subclasses
  • Well-documented abstract methods for the template method pattern

78-106: LGTM on template method implementation.

The queryUsbDevices method properly orchestrates the USB discovery flow:

  1. Calls subclass enumerateUsbDevices() to populate maps
  2. Builds device tree via getDeviceAndChildren()
  3. Handles both tree and flat list modes correctly

The flat list at lines 98-105 intentionally includes controllers, which is the documented behavior per the retrieved learnings.


115-129: LGTM on recursive tree building.

The getDeviceAndChildren method correctly:

  • Inherits vendor/product IDs from parent when not specified
  • Uses createDevice() factory for polymorphic instantiation
  • Handles missing hub entries gracefully with getOrDefault
oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java (5)

38-38: LGTM!

Clean refactoring to abstract class enabling the template method pattern for JNA/FFM implementations.


58-67: LGTM!

The abstract method is well-documented with clear contract for the array structure. The null-on-failure semantic enables clean fallback logic in the caller.


69-107: LGTM!

Good extraction of buildUuidMap() as a reusable helper. The getFileStores method correctly passes this to enable polymorphic native calls.


109-115: LGTM!

Clean overload pattern maintaining backward compatibility. The comment on line 109 helpfully documents the null-instance use case from LinuxOSFileStore.


194-208: LGTM!

The null guards are properly placed and the fallback logic is preserved. The array access relies on the documented contract in queryStatvfs, which is appropriate for this internal protected API.

Comment thread oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java Outdated
Comment thread oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java Outdated
Comment thread oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java Outdated
Comment thread oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java (1)

55-58: Consider logging unknown architectures for easier debugging.

When the architecture doesn't match any known case, silently defaulting to 224 could make troubleshooting difficult on exotic platforms. A debug-level log would help identify when the fallback is used.

💡 Optional: Log unknown architecture
+        } else if (arch.contains("x86") || arch.contains("i386") || arch.contains("arm")) {
+            // x86-32, ARM32 — 224 is correct
+            SYS_GETTID = 224L;
         } else {
-            // x86-32, ARM32, and other arches — 224 is correct for these
+            // Unknown architecture — 224 is common fallback for 32-bit arches
+            LOG.debug("Unknown architecture '{}', using SYS_GETTID=224", arch);
             SYS_GETTID = 224L;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java` around
lines 55 - 58, In LinuxLibcFunctions, when falling back to the default
SYS_GETTID = 224L (the else branch), add a debug-level log that records the
unexpected architecture string (e.g., from System.getProperty("os.arch") or the
existing architecture variable) so maintainers can see when the fallback is
used; update the else block that sets SYS_GETTID to 224L to call the class
logger (or create/use the existing LOG) with a message like "Unknown
architecture, defaulting SYS_GETTID to 224" plus the architecture value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java`:
- Around line 47-53: The check after calling LIBC.getaddrinfo in
LinuxNetworkParamsJNA (the variable res from LIBC.getaddrinfo(hostname, null,
hint, ptr)) only catches positive errors; change the condition from "res > 0" to
"res != 0" so all non-zero return codes (including negative EAI_SYSTEM) are
treated as failures, log the error with LOG.error("Failed getaddrinfo(): {}",
LIBC.gai_strerror(res)) as before, and return early to avoid dereferencing ptr
when getaddrinfo failed.

---

Nitpick comments:
In `@oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java`:
- Around line 55-58: In LinuxLibcFunctions, when falling back to the default
SYS_GETTID = 224L (the else branch), add a debug-level log that records the
unexpected architecture string (e.g., from System.getProperty("os.arch") or the
existing architecture variable) so maintainers can see when the fallback is
used; update the else block that sets SYS_GETTID to 224L to call the class
logger (or create/use the existing LOG) with a message like "Unknown
architecture, defaulting SYS_GETTID to 224" plus the architecture value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81fd870b-964c-4a79-9ff3-a70ca3bc8ec1

📥 Commits

Reviewing files that changed from the base of the PR and between 7c5b315 and bd4a622.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Test JDK 11, macos-latest
  • GitHub Check: Test JDK 25, windows-latest
  • GitHub Check: Test JDK 25, macos-latest
  • GitHub Check: Analyze (java)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java11/src/main/java/module-info.java:10-17
Timestamp: 2026-03-29T20:01:06.452Z
Learning: In the oshi project, `oshi-core-java25/src/main/java/module-info.java` intentionally does NOT export `oshi.util.platform.unix.freebsd`, `oshi.util.platform.unix.openbsd`, or `oshi.util.platform.unix.solaris`, because the FFM (Foreign Function & Memory) implementation only supports Windows, macOS, and Linux. This differs from `oshi-core-java11/src/main/java/module-info.java` which exports all Unix platform packages. The asymmetry is by design.
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, only interfaces and classes in the root packages `oshi`, `oshi.hardware`, and `oshi.software.os` are considered the public API. Code in subpackages that represent specific implementations/platforms (e.g., `oshi.software.os.linux`, `oshi.hardware.platform.linux`, and similar subpackages) is implementation detail. When reviewing, do not treat changes to classes in these platform-specific subpackages as public API breaks (for example: making `LinuxOperatingSystem` abstract, narrowing constructor visibility, or adding/removing abstract methods should not be flagged as breaking public API).

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
📚 Learning: 2026-03-29T20:01:06.452Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java11/src/main/java/module-info.java:10-17
Timestamp: 2026-03-29T20:01:06.452Z
Learning: In the oshi project, `oshi-core-java25/src/main/java/module-info.java` intentionally does NOT export `oshi.util.platform.unix.freebsd`, `oshi.util.platform.unix.openbsd`, or `oshi.util.platform.unix.solaris`, because the FFM (Foreign Function & Memory) implementation only supports Windows, macOS, and Linux. This differs from `oshi-core-java11/src/main/java/module-info.java` which exports all Unix platform packages. The asymmetry is by design.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, the public API is strictly limited to interfaces and classes in the `oshi`, `oshi.hardware`, and `oshi.software.os` packages. All platform-specific code under subpackages like `oshi.software.os.linux`, `oshi.hardware.platform.linux`, etc., is considered implementation detail — not public API. Do not flag changes to platform-specific classes (e.g., making `LinuxOperatingSystem` abstract, narrowing constructor visibility, adding abstract methods) as public API breaks.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-03-29T20:03:00.830Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java25/src/main/java/oshi/util/platform/windows/PdhUtilFFM.java:87-89
Timestamp: 2026-03-29T20:03:00.830Z
Learning: In the OSHI FFM codebase (oshi-core-java25), all PDH/Kernel32/COM wrapper methods in classes like PdhFFM, Kernel32FFM, etc. declare `throws Throwable` solely as a compiler artifact of `MethodHandle.invokeExact`. They are native calls and do not throw Java checked exceptions in practice. The enclosing `getOpenFileDescriptors()` method in PdhUtilFFM already has an outer `catch (Throwable t)` that covers the entire method body including any `finally` blocks, so no exception-masking concern exists for cleanup calls like `PdhCloseQuery` in `finally`.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-04-05T05:40:43.357Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-04-05T05:39:18.791Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
🔇 Additional comments (10)
oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java (1)

57-59: Native cleanup is correctly placed in finally.

Good call freeing getaddrinfo results with freeaddrinfo in all return paths.

oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java (9)

85-96: LGTM!

The SYSINFO_LAYOUT correctly models the 64-bit Linux struct sysinfo with proper padding for 8-byte alignment.


100-117: LGTM!

The STATVFS_LAYOUT and its field accessors correctly model the 64-bit Linux struct statvfs.


119-150: LGTM!

RLIMIT_LAYOUT and ADDRINFO_LAYOUT are correctly defined. The modeling of ai_addrlen as JAVA_LONG (combining the 4-byte socklen_t with its alignment padding) maintains correct field offsets for accessing ai_canonname.


168-201: LGTM!

The static initializer correctly uses LINKER.defaultLookup() for libc symbols and implements a clean fallback pattern for gettid availability detection.


269-271: Unsigned procs handling is now correct.

The use of Short.toUnsignedInt() properly handles the unsigned 16-bit procs field, preventing negative values when process counts exceed 32,767.


351-353: gethostname ABI is now correct.

The long len parameter correctly matches the LP64 size_t type on 64-bit Linux.


284-342: LGTM!

The statvfs field accessors correctly return long values matching the unsigned long C types.


407-436: LGTM!

The getrlimit binding and rlimit field accessors are correctly implemented for 64-bit Linux.


385-388: No action required. getStringFromNativePointer is inherited from the parent class ForeignFunctions (which LinuxLibcFunctions extends) and is properly accessible to both gaiStrerror() and addrinfoCanoname().

- LinuxLibcFunctions: add SYS_GETTID mappings for riscv64/loongarch64
  (178), s390x (236), ppc64le (207); log unknown arch at debug level
  rather than silently using a potentially wrong value
- LinuxLibcFunctions: sysinfoProcs returns int via Short.toUnsignedInt
  to correctly expose the unsigned 16-bit procs field
- LinuxLibcFunctions: gethostname size_t parameter changed from
  JAVA_INT to JAVA_LONG to match LP64 Linux ABI; wrapper updated to
  accept long
- LinuxNetworkParamsFFM: pass long literal to gethostname call
- LinuxNetworkParamsJNA: fix getaddrinfo error check from res > 0 to
  res != 0 to catch negative EAI_SYSTEM codes; call freeaddrinfo()
  explicitly to release C-allocated addrinfo memory (Addrinfo.close()
  is a no-op for native allocations)
- LinuxUsbDeviceJNA: null-check udev_new() return before use
- LinuxOSProcessJNA: check getrlimit() return value; fall back to
  getProcessOpenFileLimit() on failure
- LinuxOSProcessFFM: same getrlimit() return value check and fallback
@dbwiddis
Copy link
Copy Markdown
Member Author

dbwiddis commented Apr 6, 2026

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 6, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java (1)

487-501: ⚠️ Potential issue | 🟠 Major

Handle "unlimited" in the procfs fallback.

Line 500 assumes the Max open files row always contains numeric soft/hard values. When /proc/<pid>/limits reports unlimited, this split yields too few elements and throws ArrayIndexOutOfBoundsException instead of returning a sentinel value. That becomes more reachable now that the new JNA/FFM implementations fall back to this helper.

Possible fix
-        final String[] split = maxOpenFilesLine.get().split("\\D+");
-        return ParseUtil.parseLongOrDefault(split[index], -1);
+        final String[] split = ParseUtil.whitespaces.split(maxOpenFilesLine.get().trim());
+        if (split.length <= index + 2) {
+            return -1L;
+        }
+        final String value = split[index + 2];
+        return "unlimited".equalsIgnoreCase(value) ? -1L : ParseUtil.parseLongOrDefault(value, -1L);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java` around
lines 487 - 501, The helper LinuxOSProcess.getProcessOpenFileLimit currently
assumes the "Max open files" line always has numeric values; update it to handle
"unlimited" and avoid ArrayIndexOutOfBounds by first checking
maxOpenFilesLine.get() for the literal "unlimited" (return -1) and then guarding
the split array length before indexing; if split.length <= index or the
split[index] is empty/non-numeric, return the sentinel -1, otherwise pass
split[index] to ParseUtil.parseLongOrDefault as before.
🧹 Nitpick comments (1)
oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java (1)

72-83: Route getProcess() through createOSProcess().

getProcess(int pid) is bypassing the helper you just added below and re-encoding the constructor choice inline. Calling createOSProcess(pid) here keeps the single-process and bulk-query paths aligned if process construction changes again. The same cleanup applies to the JNA sibling.

♻️ Proposed cleanup
     `@Override`
     public OSProcess getProcess(int pid) {
-        OSProcess proc = new LinuxOSProcessFFM(pid, this);
+        OSProcess proc = createOSProcess(pid);
         if (!proc.getState().equals(State.INVALID)) {
             return proc;
         }
         return null;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java`
around lines 72 - 83, getProcess(int pid) currently constructs a
LinuxOSProcessFFM directly instead of using the helper createOSProcess(pid);
update getProcess to call createOSProcess(pid) (so it becomes OSProcess proc =
createOSProcess(pid)) and retain the existing validity check
(proc.getState().equals(State.INVALID))/return logic; also make the analogous
change in the JNA sibling so both single-process and bulk-query paths use the
same factory method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java`:
- Around line 109-115: The current helper call hardcodes a null LinuxFileSystem
instance (via getFileStoreMatching(nameToMatch, uuidMap, localOnly) delegating
to the 4-arg overload with null), which prevents
LinuxOSFileStore.updateAttributes() from reaching queryStatvfs() on refresh;
change callers (notably LinuxOSFileStore.updateAttributes()) to call the
4-argument getFileStoreMatching(...) passing the active LinuxFileSystem
instance, or convert the matching logic into an instance method on
LinuxFileSystem so both initial enumeration and refresh paths use the same
instance-backed queryStatvfs() code; ensure the other duplicate call site region
(the similar 194-200 block) is updated the same way so inode/space values
continue to come from statvfs.

In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java`:
- Around line 129-135: The method getThreadCount currently returns info.procs
directly which can sign-extend the unsigned short value; change the return to
convert the unsigned short to an int by applying Short.toUnsignedInt(info.procs)
before returning (within the existing try-with-resources using CloseableSysinfo
and keeping the existing error handling and logging).

---

Outside diff comments:
In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java`:
- Around line 487-501: The helper LinuxOSProcess.getProcessOpenFileLimit
currently assumes the "Max open files" line always has numeric values; update it
to handle "unlimited" and avoid ArrayIndexOutOfBounds by first checking
maxOpenFilesLine.get() for the literal "unlimited" (return -1) and then guarding
the split array length before indexing; if split.length <= index or the
split[index] is empty/non-numeric, return the sentinel -1, otherwise pass
split[index] to ParseUtil.parseLongOrDefault as before.

---

Nitpick comments:
In
`@oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java`:
- Around line 72-83: getProcess(int pid) currently constructs a
LinuxOSProcessFFM directly instead of using the helper createOSProcess(pid);
update getProcess to call createOSProcess(pid) (so it becomes OSProcess proc =
createOSProcess(pid)) and retain the existing validity check
(proc.getState().equals(State.INVALID))/return logic; also make the analogous
change in the JNA sibling so both single-process and bulk-query paths use the
same factory method.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c3ad1532-1ad2-4769-adc0-91151400e98e

📥 Commits

Reviewing files that changed from the base of the PR and between 180c940 and 32de5e2.

📒 Files selected for processing (26)
  • CHANGELOG.md
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParams.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🧰 Additional context used
🧠 Learnings (16)
📓 Common learnings
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.
📚 Learning: 2026-04-05T15:37:16.042Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java:59-65
Timestamp: 2026-04-05T15:37:16.042Z
Learning: In OSHI's Linux USB device implementations (LinuxUsbDeviceFFM, and the JNA equivalent), when `getUsbDevices(boolean tree)` is called with `tree=false`, the returned flat list intentionally **includes** controller nodes — it does not exclude them. The existing Javadoc that says "returns a flat list excluding controllers" is incorrect. Do not flag the implementation for including controllers in flat mode; instead, flag only the Javadoc if it claims controllers are excluded.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
📚 Learning: 2026-04-05T05:40:43.357Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • CHANGELOG.md
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
📚 Learning: 2026-04-05T05:39:18.791Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • CHANGELOG.md
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, only interfaces and classes in the root packages `oshi`, `oshi.hardware`, and `oshi.software.os` are considered the public API. Code in subpackages that represent specific implementations/platforms (e.g., `oshi.software.os.linux`, `oshi.hardware.platform.linux`, and similar subpackages) is implementation detail. When reviewing, do not treat changes to classes in these platform-specific subpackages as public API breaks (for example: making `LinuxOperatingSystem` abstract, narrowing constructor visibility, or adding/removing abstract methods should not be flagged as breaking public API).

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParams.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, the public API is strictly limited to interfaces and classes in the `oshi`, `oshi.hardware`, and `oshi.software.os` packages. All platform-specific code under subpackages like `oshi.software.os.linux`, `oshi.hardware.platform.linux`, etc., is considered implementation detail — not public API. Do not flag changes to platform-specific classes (e.g., making `LinuxOperatingSystem` abstract, narrowing constructor visibility, adding abstract methods) as public API breaks.

Applied to files:

  • oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java
  • CHANGELOG.md
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-03-29T20:01:06.452Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java11/src/main/java/module-info.java:10-17
Timestamp: 2026-03-29T20:01:06.452Z
Learning: In the oshi project, `oshi-core-java25/src/main/java/module-info.java` intentionally does NOT export `oshi.util.platform.unix.freebsd`, `oshi.util.platform.unix.openbsd`, or `oshi.util.platform.unix.solaris`, because the FFM (Foreign Function & Memory) implementation only supports Windows, macOS, and Linux. This differs from `oshi-core-java11/src/main/java/module-info.java` which exports all Unix platform packages. The asymmetry is by design.

Applied to files:

  • oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
📚 Learning: 2026-04-05T16:15:03.230Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java:95-97
Timestamp: 2026-04-05T16:15:03.230Z
Learning: In `LinuxLogicalVolumeGroupJNA.getLogicalVolumeGroups()` (oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java), `physicalVolumesMap.get(e.getKey())` in the final stream mapping cannot return null: both `logicalVolumesMap` and `physicalVolumesMap` are populated via `computeIfAbsent` inside the same conditional block (`!Util.isBlank(vgName) && !Util.isBlank(lvName)`), so every VG key in `logicalVolumesMap` is guaranteed to have a corresponding entry in `physicalVolumesMap`. Do not flag this `.get()` call as a potential null-return issue. The FFM version uses `getOrDefault` as extra defensiveness, not because null is possible here.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystemJNA.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
📚 Learning: 2026-04-05T05:40:43.357Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI’s Linux FFM implementations (e.g., LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the corresponding JNA udev code, do not require/flag a missing null check around `udev_enumerate_new()`/`enumerateNew()`. Per libudev semantics, `udev_enumerate_new()` returns NULL only if udev itself is broken (e.g., OOM). Since `udev_new()` is already guarded and is the successful prerequisite for enumeration, treating `udev_enumerate_new()` as non-null is consistent with the established pattern; adding a redundant null guard would be inconsistent.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-04-05T05:39:18.791Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: When reviewing OSHI Linux FFM/JNA code that uses libudev, do not flag a missing null check on `udev_enumerate_new()` if `udev_new()` has already been guarded. Per libudev semantics, `udev_enumerate_new()` returns NULL only when the udev context itself is broken (e.g., OOM), so a successful `udev_new()` implies `udev_enumerate_new()` should succeed as well. Therefore, lack of a redundant null guard on `udev_enumerate_new()` is acceptable in this pattern.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-04-03T02:07:47.621Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3110
File: oshi-core-java25/src/main/java/oshi/hardware/platform/mac/MacHWDiskStoreFFM.java:119-121
Timestamp: 2026-04-03T02:07:47.621Z
Learning: In OSHI’s Java FFM/API implementation classes (e.g., *FFM.java) that implement/override `updateAttributes()`, preserve the intentional failure-handling pattern: catch `Throwable` and return `false` without logging. `updateAttributes()` is a public API whose contract is to signal failure via the boolean return value, and callers rely on that return value (so emitting logs from this path is not desired and should not be introduced during review unless the method contract changes).

Applied to files:

  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOSProcessFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
📚 Learning: 2026-03-29T20:03:00.830Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java25/src/main/java/oshi/util/platform/windows/PdhUtilFFM.java:87-89
Timestamp: 2026-03-29T20:03:00.830Z
Learning: In the OSHI FFM codebase (oshi-core-java25), all PDH/Kernel32/COM wrapper methods in classes like PdhFFM, Kernel32FFM, etc. declare `throws Throwable` solely as a compiler artifact of `MethodHandle.invokeExact`. They are native calls and do not throw Java checked exceptions in practice. The enclosing `getOpenFileDescriptors()` method in PdhUtilFFM already has an outer `catch (Throwable t)` that covers the entire method body including any `finally` blocks, so no exception-masking concern exists for cleanup calls like `PdhCloseQuery` in `finally`.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcessJNA.java
  • oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java
📚 Learning: 2026-04-05T15:37:16.042Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java:59-65
Timestamp: 2026-04-05T15:37:16.042Z
Learning: For OSHI Linux USB device implementations, the behavior of `getUsbDevices(boolean tree)` with `tree=false` is intentional: the returned flat list includes controller nodes (it is not meant to exclude them). In code review, do not flag the implementation for including controllers in flat mode; instead, only flag/update documentation/JavaDoc that incorrectly claims controllers are excluded (e.g., Javadoc text like "returns a flat list excluding controllers" should be corrected).

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceJNA.java
  • oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
📚 Learning: 2026-04-03T18:43:50.370Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3112
File: oshi-core/src/main/java/oshi/PlatformEnum.java:99-100
Timestamp: 2026-04-03T18:43:50.370Z
Learning: In oshi/oshi PlatformEnum.java, the DragonFly BSD detection uses `osName.equalsIgnoreCase("dragonflybsd")` intentionally, mirroring the exact same check in JNA's `Platform.java` source. Java's `os.name` property on DragonFly BSD is "dragonflybsd" (no space), so `startsWith("DragonFly")` is NOT the correct pattern here. Do not suggest changing it to `startsWith`.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
📚 Learning: 2026-04-03T18:44:46.910Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3112
File: oshi-core/src/main/java/oshi/PlatformEnum.java:63-66
Timestamp: 2026-04-03T18:44:46.910Z
Learning: In oshi/oshi PlatformEnum.java, the DRAGONFLYBSD enum constant is intentionally added for platform detection only (no implementation classes exist yet). It allows UnsupportedOperationException messages to say "DragonFly BSD" rather than "Unknown". Full DragonFly BSD support is tracked in issue `#2583`. Do not flag the missing switch-case implementations as a bug.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
📚 Learning: 2026-04-03T02:07:47.621Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3110
File: oshi-core-java25/src/main/java/oshi/hardware/platform/mac/MacHWDiskStoreFFM.java:119-121
Timestamp: 2026-04-03T02:07:47.621Z
Learning: In `oshi-core-java25/src/main/java/oshi/hardware/platform/mac/MacHWDiskStoreFFM.java` (and similar FFM/API implementations in OSHI), the `updateAttributes()` method intentionally uses `catch (Throwable e) { return false; }` without logging. This is by design: `updateAttributes()` is a public API method whose contract is to return `false` on failure; callers consume the boolean and would never see log output, so adding a log statement is not desired.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java
🔇 Additional comments (26)
oshi-core/src/main/java/oshi/util/platform/linux/ProcPath.java (1)

30-30: LGTM!

The new LOADAVG constant follows the established naming and ordering conventions for this utility class. The placement between DISKSTATS and MEMINFO maintains alphabetical ordering.

CHANGELOG.md (1)

5-7: LGTM!

Clean consolidation of related PR entries into a single changelog item describing the Linux FFM migration work.

oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParams.java (1)

15-19: LGTM!

Clean refactoring to an abstract base class. The Javadoc correctly documents that subclasses provide getDomainName() and getHostName() via native calls, while this class retains the command-line-based gateway implementations.

oshi-core/src/main/java/oshi/software/os/linux/LinuxNetworkParamsJNA.java (2)

36-62: LGTM!

The getDomainName() implementation correctly:

  1. Uses AI_CANONNAME hint to request canonical name resolution
  2. Checks for non-zero return from getaddrinfo() (handles all error codes)
  3. Properly frees the native addrinfo linked list with LIBC.freeaddrinfo() in a finally block before exiting
  4. Handles null ai_canonname by falling back to the original hostname

64-71: LGTM!

The getHostName() implementation correctly allocates HOST_NAME_MAX + 1 bytes for the null terminator and falls back to super.getHostName() on failure, which uses Java's InetAddress facilities.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java (3)

21-34: LGTM!

Clean refactoring to an abstract base class with protected constants and constructor. The shared USB attribute constants (SUBSYSTEM_USB, DEVTYPE_USB_DEVICE, etc.) are correctly extracted for use by both JNA and FFM subclasses.


47-64: LGTM!

Well-designed template method pattern with clear abstractions:

  • enumerateUsbDevices() lets subclasses populate the device maps via platform-specific udev bindings
  • createDevice() provides a factory method for instantiating the correct concrete subclass

The Javadoc clearly documents the expected behavior of each hook.


78-106: LGTM!

The queryUsbDevices() template method cleanly orchestrates enumeration via the abstract hook, then builds the device tree or flat list as needed. The use of getOrDefault(..., Collections.emptyList()) at line 121 is a nice defensive improvement.

oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxUsbDeviceFFM.java (2)

42-50: LGTM!

The static getUsbDevices() entry point correctly delegates to the inherited queryUsbDevices() template method, and createDevice() properly instantiates the FFM-specific subclass.


52-131: LGTM!

The enumerateUsbDevices() implementation correctly:

  1. Guards against missing udev support with early return
  2. Properly manages udev resources with nested try-finally blocks
  3. Follows the established pattern of not null-checking udev_enumerate_new() after successful udev_new()
  4. Uses inherited constants for attribute names ensuring consistency with the JNA implementation
oshi-core-java25/src/main/java/oshi/ffm/linux/LinuxLibcFunctions.java (4)

44-60: LGTM!

The SYS_GETTID syscall number mapping correctly handles the major Linux architectures:

  • aarch64/arm64/riscv64/loongarch64 → 178 (asm-generic)
  • amd64/x86_64 → 186 (x86-64 custom table)
  • s390x → 236
  • ppc64le → 207

The fallback to 224 (x86-32/ARM32) with debug logging for unknown architectures is a reasonable defensive default.


169-202: LGTM!

The static initializer correctly:

  1. Uses LINKER.defaultLookup() for libc symbols (avoiding the libc.so vs libc.so.6 issue on Linux)
  2. Binds gethostname with JAVA_LONG for the size_t parameter
  3. Gracefully handles missing gettid() by falling back to syscall(SYS_GETTID)

270-272: LGTM!

Correctly exposes the unsigned 16-bit sysinfo.procs field as a Java int using Short.toUnsignedInt(), preventing negative wrap-around on systems with more than 32,767 processes.


398-406: No null check needed for canonPtr.

The getStringFromNativePointer method already handles NULL segments gracefully (lines 75-77 in ForeignFunctions.java). It checks if the pointer is null or equals MemorySegment.NULL and returns null, making the additional null check in addrinfoCanoname unnecessary.

			> Likely an incorrect or invalid review comment.
oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxFileSystemFFM.java (1)

24-40: LGTM!

The queryStatvfs() implementation correctly:

  1. Returns the array in the expected order: [totalInodes, freeInodes, totalSpace, usableSpace, freeSpace]
  2. Properly scales block counts by fragment size (frsize) for space calculations
  3. Returns null on failure, allowing the caller to fall back to java.io.File methods
  4. Uses confined arena for deterministic resource cleanup

The field mapping matches the JNA implementation exactly, ensuring consistent behavior.

oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxNetworkParamsFFM.java (2)

28-38: LGTM on getHostName() implementation.

The FFM-based hostname retrieval correctly allocates a buffer with HOST_NAME_MAX + 1 for null termination, uses a confined arena for automatic cleanup, and properly falls back to the superclass on any failure.


58-70: ⚠️ Potential issue | 🔴 Critical

freeaddrinfo called even when getaddrinfo fails — undefined behavior.

When getaddrinfo returns non-zero (failure), the output pointer resPtr is not allocated by libc and must not be passed to freeaddrinfo. However, the current structure places freeaddrinfo in a finally block that executes unconditionally after the rc != 0 early return, causing potential crashes or undefined behavior.

The finally block at lines 68-70 will execute after the return "" at line 61, calling freeaddrinfo on an invalid pointer.

🐛 Proposed fix: move freeaddrinfo inside success path only
             int rc = LinuxLibcFunctions.getaddrinfo(nodeSeg, MemorySegment.NULL, hints, resPP);
             if (rc != 0) {
                 LOG.error("Failed getaddrinfo(): {}", LinuxLibcFunctions.gaiStrerror(rc, arena));
                 return "";
             }
             // resPP holds the pointer to the first addrinfo struct
             MemorySegment resPtr = resPP.get(ValueLayout.ADDRESS, 0);
             try {
                 String canonname = LinuxLibcFunctions.addrinfoCanoname(resPtr, arena);
                 return canonname == null ? hostname : canonname.trim();
             } finally {
-                LinuxLibcFunctions.freeaddrinfo(resPtr);
+                if (!MemorySegment.NULL.equals(resPtr)) {
+                    LinuxLibcFunctions.freeaddrinfo(resPtr);
+                }
             }

Alternatively, restructure to only enter the try-finally block after confirming success:

             int rc = LinuxLibcFunctions.getaddrinfo(nodeSeg, MemorySegment.NULL, hints, resPP);
             if (rc != 0) {
                 LOG.error("Failed getaddrinfo(): {}", LinuxLibcFunctions.gaiStrerror(rc, arena));
                 return "";
             }
             // resPP holds the pointer to the first addrinfo struct
             MemorySegment resPtr = resPP.get(ValueLayout.ADDRESS, 0);
-            try {
-                String canonname = LinuxLibcFunctions.addrinfoCanoname(resPtr, arena);
-                return canonname == null ? hostname : canonname.trim();
-            } finally {
+            String canonname = LinuxLibcFunctions.addrinfoCanoname(resPtr, arena);
+            String result = canonname == null ? hostname : canonname.trim();
+            LinuxLibcFunctions.freeaddrinfo(resPtr);
+            return result;
-                LinuxLibcFunctions.freeaddrinfo(resPtr);
-            }
			> Likely an incorrect or invalid review comment.
oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHardwareAbstractionLayerJNA.java (1)

39-41: LGTM on USB device delegation update.

The change correctly delegates to LinuxUsbDeviceJNA.getUsbDevices(tree) now that LinuxUsbDevice is an abstract base class. This aligns with the JNA/FFM refactor pattern used throughout this PR.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java (1)

7-7: LGTM on HAS_UDEV import source update.

Correctly sources the udev capability flag from LinuxOperatingSystemJNA rather than the now-abstract LinuxOperatingSystem. This is consistent with the JNA/FFM architecture split where each implementation path has its own capability detection.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxNetworkIFJNA.java (1)

7-7: LGTM on HAS_UDEV import source update.

Consistent with the JNA/FFM refactor pattern applied across other Linux JNA classes.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessor.java (2)

8-8: LGTM on new LOADAVG import.

Adding the static import for ProcPath.LOADAVG supports the refactored getSystemLoadAverage implementation.


500-511: LGTM on /proc/loadavg-based implementation.

This procfs-based fallback correctly:

  • Reads and parses /proc/loadavg without native calls
  • Handles missing or malformed entries by defaulting to -1d
  • Serves as a reliable base implementation for JNA/FFM subclasses to fall back on

This aligns well with the JNA/FFM split where subclasses use native getloadavg() and fall back to this pure-Java implementation on failure.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxHWDiskStoreJNA.java (1)

7-7: LGTM on HAS_UDEV import source update.

Consistent with the JNA/FFM refactor pattern. JNA-based disk store implementation correctly references the JNA-specific udev capability flag.

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceJNA.java (1)

7-7: LGTM on HAS_UDEV import source update.

Consistent with the JNA/FFM refactor pattern applied across all Linux JNA classes in this PR.

oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java (2)

11-11: LGTM on new FFM imports.

Added imports support the FFM-based getSystemLoadAverage implementation.

Also applies to: 23-23


38-55: LGTM on FFM-based getSystemLoadAverage implementation.

The implementation correctly:

  • Validates nelem bounds (1-3) consistent with the base class
  • Allocates a properly-sized native double array using ValueLayout.JAVA_DOUBLE
  • Handles partial results by checking retval > i before reading each value
  • Falls back gracefully to the procfs-based superclass implementation on any failure

The catch (Throwable e) with logging and fallback is appropriate here since this is not a boolean-returning API like updateAttributes().

Comment thread oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java Outdated
Comment thread oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java Outdated
…oring

- LinuxOperatingSystemJNA: fix unsigned short sign-extension in
  getThreadCount via Short.toUnsignedInt
- LinuxOperatingSystemJNA/FFM: use createOSProcess() in getProcess()
- LinuxFileSystem: make getFileStoreMatching a non-static instance
  method so queryStatvfs dispatches polymorphically; remove null-
  instance overload
- LinuxOSFileStore: store LinuxFileSystem reference; call
  fs.getFileStoreMatching() in updateAttributes() so statvfs is used
  on refresh
- LinuxOSProcess: handle 'unlimited' and guard array bounds in
  getProcessOpenFileLimit()
- Revert USB class refactoring (LinuxUsbDeviceJNA, abstract
  LinuxUsbDevice base) back to master branch state; fix HAS_UDEV
  import in LinuxUsbDevice to reference LinuxOperatingSystemJNA;
  USB consolidation deferred to a follow-up PR
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java (1)

47-59: Consider caching hasGettid() result like the JNA version.

The JNA implementation caches HAS_GETTID as a separate constant, allowing getThreadId() to use it directly. Here, LinuxLibcFunctions.hasGettid() is called dynamically on each getThreadId() invocation (line 99). For consistency and to avoid repeated checks, consider caching the result:

     private static final boolean HAS_SYSCALL_GETTID;
+    private static final boolean HAS_GETTID;

     static {
-        boolean hasSyscallGettid = LinuxLibcFunctions.hasGettid();
+        boolean hasGettid = LinuxLibcFunctions.hasGettid();
+        boolean hasSyscallGettid = hasGettid;
-        if (!hasSyscallGettid) {
+        if (!hasGettid) {
             try {
                 hasSyscallGettid = LinuxLibcFunctions.syscallGettid() > 0;
             } catch (Throwable e) {
                 LOG.debug("Did not find working syscall gettid via FFM. Using procfs");
             }
         }
         HAS_SYSCALL_GETTID = hasSyscallGettid;
+        HAS_GETTID = hasGettid;
     }

Then use HAS_GETTID in getThreadId():

-                return LinuxLibcFunctions.hasGettid() ? LinuxLibcFunctions.gettid()
+                return HAS_GETTID ? LinuxLibcFunctions.gettid()

Also applies to: 95-104

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java`
around lines 47 - 59, The code currently calls LinuxLibcFunctions.hasGettid()
dynamically in getThreadId(), causing repeated checks; instead use the
already-initialized static HAS_SYSCALL_GETTID constant (set in the static block)
to decide the path. Update getThreadId() to reference HAS_SYSCALL_GETTID and
only call LinuxLibcFunctions.syscallGettid() when true (with the same try/catch
behavior), and fall back to the procfs path otherwise; remove the repeated
LinuxLibcFunctions.hasGettid() invocation so the cached result is used
consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java`:
- Around line 77-79: In the catch block handling NoClassDefFoundError in
LinuxOperatingSystemJNA.java, the LOG.error message has a typo ("Did not JNA
classes..."); update the LOG.error call inside the catch (NoClassDefFoundError
e) to a proper, clear message such as "Did not find JNA classes. Investigate
incompatible version or missing native library." (keep the LOG.error(...)
invocation and existing catch handling intact).

---

Nitpick comments:
In
`@oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java`:
- Around line 47-59: The code currently calls LinuxLibcFunctions.hasGettid()
dynamically in getThreadId(), causing repeated checks; instead use the
already-initialized static HAS_SYSCALL_GETTID constant (set in the static block)
to decide the path. Update getThreadId() to reference HAS_SYSCALL_GETTID and
only call LinuxLibcFunctions.syscallGettid() when true (with the same try/catch
behavior), and fall back to the procfs path otherwise; remove the repeated
LinuxLibcFunctions.hasGettid() invocation so the cached result is used
consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 41366325-2773-43d0-b41f-fb69c4b5e692

📥 Commits

Reviewing files that changed from the base of the PR and between 32de5e2 and ad253ea.

📒 Files selected for processing (6)
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSFileStore.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
✅ Files skipped from review due to trivial changes (1)
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxUsbDevice.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Test JDK 11, windows-latest
  • GitHub Check: Test JDK 25, macos-latest
  • GitHub Check: Test JDK 21, macos-latest
  • GitHub Check: Test JDK 25, windows-latest
  • GitHub Check: Test JDK 11, macos-latest
  • GitHub Check: Analyze (java)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, only interfaces and classes in the root packages `oshi`, `oshi.hardware`, and `oshi.software.os` are considered the public API. Code in subpackages that represent specific implementations/platforms (e.g., `oshi.software.os.linux`, `oshi.hardware.platform.linux`, and similar subpackages) is implementation detail. When reviewing, do not treat changes to classes in these platform-specific subpackages as public API breaks (for example: making `LinuxOperatingSystem` abstract, narrowing constructor visibility, or adding/removing abstract methods should not be flagged as breaking public API).

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSFileStore.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
📚 Learning: 2026-04-05T16:15:03.230Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java:95-97
Timestamp: 2026-04-05T16:15:03.230Z
Learning: In `LinuxLogicalVolumeGroupJNA.getLogicalVolumeGroups()` (oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxLogicalVolumeGroupJNA.java), `physicalVolumesMap.get(e.getKey())` in the final stream mapping cannot return null: both `logicalVolumesMap` and `physicalVolumesMap` are populated via `computeIfAbsent` inside the same conditional block (`!Util.isBlank(vgName) && !Util.isBlank(lvName)`), so every VG key in `logicalVolumesMap` is guaranteed to have a corresponding entry in `physicalVolumesMap`. Do not flag this `.get()` call as a potential null-return issue. The FFM version uses `getOrDefault` as extra defensiveness, not because null is possible here.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOSFileStore.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
📚 Learning: 2026-04-05T05:40:43.357Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:40:43.357Z
Learning: In OSHI's Linux FFM and JNA udev code, a null guard on `udev_enumerate_new()` is intentionally absent and should not be flagged. This is consistent across all four FFM classes (LinuxPowerSourceFFM, LinuxHWDiskStoreFFM, LinuxLogicalVolumeGroupFFM, LinuxUsbDeviceFFM) and the JNA implementation (which also does not guard `enumerateNew()` for null). Per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` (which is guarded) implies `udev_enumerate_new()` will succeed. Adding a null guard would be both redundant and inconsistent with the established pattern.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
📚 Learning: 2026-04-05T05:39:18.791Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3117
File: oshi-core-java25/src/main/java/oshi/hardware/platform/linux/LinuxPowerSourceFFM.java:62-65
Timestamp: 2026-04-05T05:39:18.791Z
Learning: In OSHI's Linux FFM/JNA udev code, a null guard on `udev_enumerate_new()` is unnecessary if `udev_new()` has already been guarded: per libudev semantics, `udev_enumerate_new()` only returns NULL when udev itself is broken (e.g. OOM), so a successful `udev_new()` implies `udev_enumerate_new()` will succeed as well. Do not flag missing null checks on `udev_enumerate_new()` as an issue.

Applied to files:

  • oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java
  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
  • oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, the public API is strictly limited to interfaces and classes in the `oshi`, `oshi.hardware`, and `oshi.software.os` packages. All platform-specific code under subpackages like `oshi.software.os.linux`, `oshi.hardware.platform.linux`, etc., is considered implementation detail — not public API. Do not flag changes to platform-specific classes (e.g., making `LinuxOperatingSystem` abstract, narrowing constructor visibility, adding abstract methods) as public API breaks.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
📚 Learning: 2026-03-29T20:01:06.452Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3014
File: oshi-core-java11/src/main/java/module-info.java:10-17
Timestamp: 2026-03-29T20:01:06.452Z
Learning: In the oshi project, `oshi-core-java25/src/main/java/module-info.java` intentionally does NOT export `oshi.util.platform.unix.freebsd`, `oshi.util.platform.unix.openbsd`, or `oshi.util.platform.unix.solaris`, because the FFM (Foreign Function & Memory) implementation only supports Windows, macOS, and Linux. This differs from `oshi-core-java11/src/main/java/module-info.java` which exports all Unix platform packages. The asymmetry is by design.

Applied to files:

  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
📚 Learning: 2026-04-03T02:07:47.621Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3110
File: oshi-core-java25/src/main/java/oshi/hardware/platform/mac/MacHWDiskStoreFFM.java:119-121
Timestamp: 2026-04-03T02:07:47.621Z
Learning: In OSHI’s Java FFM/API implementation classes (e.g., *FFM.java) that implement/override `updateAttributes()`, preserve the intentional failure-handling pattern: catch `Throwable` and return `false` without logging. `updateAttributes()` is a public API whose contract is to signal failure via the boolean return value, and callers rely on that return value (so emitting logs from this path is not desired and should not be introduced during review unless the method contract changes).

Applied to files:

  • oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java
🔇 Additional comments (12)
oshi-core/src/main/java/oshi/software/os/linux/LinuxOSProcess.java (5)

49-49: LGTM!

Making the class abstract to enable JNA and FFM subclass implementations is appropriate. Per project conventions, this is implementation detail in a platform-specific subpackage, not a public API change.


69-76: LGTM!

Clean accessor for subclasses to access the OS instance for polymorphic dispatch to JNA/FFM implementations.


287-296: LGTM!

The delegation pattern correctly distinguishes between the current process (using native getrlimit via abstract methods) and other processes (parsing /proc/[pid]/limits). This aligns with the interface contract.


298-310: LGTM!

Clean abstraction for native getrlimit calls with proper Javadoc. Subclasses (JNA/FFM) will provide the native implementations.


487-508: LGTM!

Good improvements:

  1. Visibility change to protected enables subclass reuse for fallback logic
  2. Handling "unlimited" correctly returns -1 per the OSProcess interface contract
  3. Bounds check before array access prevents potential ArrayIndexOutOfBoundsException
oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystemJNA.java (2)

128-140: LGTM! Unsigned conversion fix applied correctly.

The use of Short.toUnsignedInt(info.procs) properly handles values above 32767, preventing sign-extension to negative integers. This aligns with the FFM implementation and correctly addresses the unsigned short handling.


45-83: Well-structured capability detection with comprehensive error handling.

The static initialization block properly probes for udev, gettid, and syscall availability with appropriate logging at each level. The outer NoClassDefFoundError catch ensures graceful degradation if JNA classes are missing entirely.

oshi-core-java25/src/main/java/oshi/software/os/linux/LinuxOperatingSystemFFM.java (3)

85-93: LGTM! Appropriate error handling with fallback.

The broad Throwable catch is consistent with FFM error handling patterns in this codebase, and logging at warn level is appropriate for getProcessId() failures since this isn't a boolean-return API method like updateAttributes().


113-125: LGTM! Proper Arena lifecycle and FFM error handling.

The use of Arena.ofConfined() with try-with-resources ensures proper memory management. The delegation to LinuxLibcFunctions.sysinfoProcs(info) (which handles the unsigned short conversion) maintains consistency with the JNA implementation's Short.toUnsignedInt() fix.


61-83: LGTM! Factory methods correctly delegate to FFM implementations.

The overrides properly instantiate FFM-specific classes (LinuxFileSystemFFM, LinuxNetworkParamsFFM, LinuxOSProcessFFM) and the getProcess() implementation correctly filters out processes with INVALID state.

oshi-core/src/main/java/oshi/software/os/linux/LinuxOSFileStore.java (1)

17-17: Nice fix for refresh-time filesystem dispatch.

Holding the originating LinuxFileSystem here keeps updateAttributes() on the same JNA/FFM-backed path, so refreshed inode and space values continue to come from the native statvfs() implementation.

Also applies to: 28-32, 84-85

oshi-core/src/main/java/oshi/software/os/linux/LinuxFileSystem.java (1)

58-72: Nice abstraction boundary for filesystem stats.

queryStatvfs() cleanly isolates the native call while this base class keeps mount parsing, UUID resolution, and JVM fallback centralized. That makes the JNA/FFM subclasses thinner and keeps enumeration and refresh behavior aligned.

Also applies to: 74-107, 109-109, 188-206

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant