Failed to create temporary file for libjnidispatch.so library

I’m packaging a java application “besu”.

The snap builds just fine, but after installation seems to want to update some lib and of course get a Permission Denied.

2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.UnsatisfiedLinkError: Failed to create temporary file for /com/sun/jna/linux-x86-64/libjnidispatch.so library: Permission denied [in thread "main"]
  • It works if I install the snap with “–devmode”
  • It fails if I install with “–dangerous”

This is more from the logs:

2024-07-14 13:10:45.083+02:00 | main | ERROR | LinuxOperatingSystem | Did not find Udev class from JNA. There is likely an old JNA version on the classpath.
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 2024-07-14 13:10:45.093+02:00 | main | ERROR | Besu | Uncaught exception in thread "main"
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: java.lang.NoClassDefFoundError: Could not initialize class com.sun.jna.Native
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at oshi.jna.platform.linux.LinuxLibc.<clinit>(LinuxLibc.java:22)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at oshi.software.os.linux.LinuxOperatingSystem.<clinit>(LinuxOperatingSystem.java:98)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at oshi.hardware.platform.linux.LinuxGlobalMemory.<clinit>(LinuxGlobalMemory.java:28)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at oshi.hardware.platform.linux.LinuxHardwareAbstractionLayer.createMemory(LinuxHardwareAbstractionLayer.java:38)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at oshi.util.Memoizer$1.get(Memoizer.java:61)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at oshi.hardware.common.AbstractHardwareAbstractionLayer.getMemory(AbstractHardwareAbstractionLayer.java:60)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.ConfigurationOverviewBuilder.build(ConfigurationOverviewBuilder.java:389)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.generateConfigurationOverview(BesuCommand.java:2819)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.configure(BesuCommand.java:1828)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.run(BesuCommand.java:1168)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine.access$1500(CommandLine.java:148)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine$RunLast.handle(CommandLine.java:2453)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine$RunLast.handle(CommandLine.java:2415)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine.execute(CommandLine.java:2170)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.lambda$createExecuteTask$1(BesuCommand.java:1099)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine.execute(CommandLine.java:2170)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.lambda$createPluginRegistrationTask$2(BesuCommand.java:1109)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine.execute(CommandLine.java:2170)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.util.ConfigDefaultValueProviderStrategy.execute(ConfigDefaultValueProviderStrategy.java:58)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at picocli.CommandLine.execute(CommandLine.java:2170)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.executeCommandLine(BesuCommand.java:1136)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.parse(BesuCommand.java:1078)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.Besu.main(Besu.java:41)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.UnsatisfiedLinkError: Failed to create temporary file for /com/sun/jna/linux-x86-64/libjnidispatch.so library: Permission denied [in thread "main"]
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1055)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:1011)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at com.sun.jna.Native.<clinit>(Native.java:221)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1.createContext(LibSecp256k1.java:56)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.nativelib.secp256k1.LibSecp256k1.<clinit>(LibSecp256k1.java:52)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.crypto.SECP256K1.maybeEnableNative(SECP256K1.java:77)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.crypto.SECP256K1.<init>(SECP256K1.java:62)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.crypto.SignatureAlgorithmFactory.getInstance(SignatureAlgorithmFactory.java:66)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.subcommands.TxParseSubCommand.<clinit>(TxParseSubCommand.java:58)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.addSubCommands(BesuCommand.java:1212)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.initializeCommandLineSettings(BesuCommand.java:1088)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	at org.hyperledger.besu.cli.BesuCommand.parse(BesuCommand.java:1067)
2024-07-14T13:10:45+02:00 besu.besu-daemon[3826290]: 	... 1 more
2024-07-14T13:10:45+02:00 systemd[1]: snap.besu.besu-daemon.service: Main process exited, code=exited, status=1/FAILURE
2024-07-14T13:10:45+02:00 systemd[1]: snap.besu.besu-daemon.service: Failed with result 'exit-code'.
2024-07-14T13:10:45+02:00 systemd[1]: snap.besu.besu-daemon.service: Consumed 13.387s CPU time.

Any take on how to get past this error?

Where is this temp file being created?

Its defaulting to some path in $HOME/.cache/JNA/ but could be changed with JAVA_OPTS to -Djna.tmpdir=/tmp

So, I could get through this with:

  1. Stage libjemalloc-dev (To get libjnidispatch.system.so ) into the snap.
  2. Add “-Djna.library.path=$SNAP/usr/lib/x86_64-linux-gnu/jni/libjnidispatch.system.so” to JAVA_OPT so that know this lib is found (not sure I need it).
  3. Stage: libjna-java which I think contains needed java libs that somehow needs to be extracted to into the -Djna.tmpdir=/tmp which is also added to the JAVA_OPTS.

I then need to set environment as:

JAVA_HOME: $SNAP/usr/lib/jvm/java-21-openjdk-amd64/
LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/usr/lib

The final snapcraft now looks like this:

name: besu
title: Besu 
base: core24
adopt-info: besu
summary: Besu - Hyperledger Ethereum client.
license: GPL-3.0-with-GCC-exception
contact: info@dwellir.com
issues: https://github.com/dwellir-public/snap-besu/issues
source-code: https://github.com/dwellir-public/snap-besu
website: https://www.dwellir.com
grade: stable
confinement: strict

platforms:
  amd64:
  arm64:

description: |
  Hyperledger Besu is an Ethereum client designed to be enterprise-friendly for both public and private permissioned network use cases, with an extractable EVM implementation. 
  It can also be run on test networks such as Holesky and Sepolia. Hyperledger Besu includes several consensus algorithms including Proof of Stake, Proof of Work, and Proof of Authority (IBFT 2.0, QBFT, and Clique). Its comprehensive permissioning schemes are designed specifically for use in a consortium environment.

parts:
  besu:
    after: [utils]
    plugin: nil
    build-packages: 
      - openjdk-21-jdk-headless
    source: https://github.com/hyperledger/besu.git
    source-tag: 24.7.0
    stage-packages:
      - openjdk-21-jre-headless
      # libjemalloc requires $LD_LIBRARY_PATH:$SNAP/usr/lib
      - libjemalloc-dev
      # The jna package is needed which also needs -Djna.xxx configs to work.
      - libjna-java

    override-pull: |
      craftctl default
      craftctl set version="24.7.0-$(git rev-parse --short HEAD)"

    override-build: |
      echo "BEGIN BUILD OF BESU - gradlew"

      echo "Extracting potential https_proxy and/pr http_proxy as needed when building with Canonical buildfarm"
      PROXY_OPTS=$($SNAPCRAFT_STAGE/utils/getproxy.sh)
      GRADLE_OPTS="-Djava.net.useSystemProxies=true $PROXY_OPTS" ./gradlew --no-daemon installDist
      
      echo "COMPLETED BUILD OF BESU - gradlew"

    override-stage: |
      printenv
      mkdir -p "${CRAFT_PRIME}"/bin
      cp ${CRAFT_PART_BUILD}/build/install/besu/bin/besu ${SNAPCRAFT_PRIME}/bin/besu
      cp ${CRAFT_PART_BUILD}/build/install/besu/bin/evmtool ${SNAPCRAFT_PRIME}/bin/evmtool
      cp -r ${CRAFT_PART_BUILD}/build/install/besu/lib ${SNAPCRAFT_PRIME}/
      craftctl default

  wrappers:
    plugin: dump
    source-type: local
    source: .
    stage:
      - wrappers/


  utils:
    plugin: dump
    source: .
    stage:
      - utils/

# Explanation of some configs needed here:
#   -Djna.library.path => Loads the jmalloc
#   -Cjna.tmpdir => Is needed to point to a writable location for the snap

apps:
  besu-daemon:
    command: wrappers/start-besu.bash
    daemon: simple
    install-mode: disable
    refresh-mode: endure
    restart-condition: never
    plugs:
      - network
      - network-bind
      - removable-media
      - system-observe
    environment:
      LC_ALL: C.UTF-8
      LANG: C.UTF-8
      JAVA_HOME: $SNAP/usr/lib/jvm/java-21-openjdk-amd64/
      LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/usr/lib
      JAVA_OPTS: -Djna.library.path=$SNAP/usr/lib/x86_64-linux-gnu/jni/libjnidispatch.system.so -Djna.tmpdir=/tmp

  besu:
    command: bin/besu
    plugs:
      - network
      - network-bind
      - system-observe
      - removable-media
    environment:
      LC_ALL: C.UTF-8
      LANG: C.UTF-8
      JAVA_HOME: $SNAP/usr/lib/jvm/java-21-openjdk-amd64/
      LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/usr/lib
      JAVA_OPTS: -Djna.library.path=$SNAP/usr/lib/x86_64-linux-gnu/jni/libjnidispatch.system.so -Djna.tmpdir=/tmp

I still get alot of DENY in the journal which I would like to get rid of, but that might be a separate post.

= AppArmor =
Time: Jul 14 23:22:15
Log: apparmor="DENIED" operation="open" class="file" profile="snap.besu.besu-daemon" name="/sys/devices/virtual/net/docker0/statistics/rx_bytes" pid=1785303 comm=4A465220506572696F646963205461 requested_mask="r" denied_mask="r" fsuid=0 ouid=0
File: /sys/devices/virtual/net/docker0/statistics/rx_bytes (read)
Suggestions:
* adjust program to not access '/sys/devices/virtual/net/docker0/statistics/rx_bytes'
* adjust program to not access '/sys/devices/virtual/net/docker[0-9]*/statistics/rx_bytes'

= AppArmor =
Time: Jul 14 23:22:15
Log: apparmor="DENIED" operation="open" class="file" profile="snap.besu.besu-daemon" name="/sys/devices/virtual/net/docker0/statistics/tx_bytes" pid=1785303 comm=4A465220506572696F646963205461 requested_mask="r" denied_mask="r" fsuid=0 ouid=0
File: /sys/devices/virtual/net/docker0/statistics/tx_bytes (read)
Suggestions:
* adjust program to not access '/sys/devices/virtual/net/docker0/statistics/tx_bytes'
* adjust program to not access '/sys/devices/virtual/net/docker[0-9]*/statistics/tx_bytes'

The snap starts besu now at least.