From 8d9cea2ce0355c8ae907e164a72885216d1ba4e4 Mon Sep 17 00:00:00 2001 From: Nico Kruber Date: Mon, 12 Aug 2019 08:37:27 +0200 Subject: [PATCH] Try to load native linux libraries with matching classifier first (#9411) Motivation: Users' runtime systems may have incompatible dynamic libraries to the ones our tcnative wrappers link to. Unfortunately, we cannot determine and catch these scenarios (in which the JVM crashes) but we can make a more educated guess on what library to load and try to find one that works better before crashing. Modifications: 1) Build dynamically linked openSSL builds for more OSs (netty-tcnative) 2) Load native linux libraries with matching classifier (first) Result: More developers / users can use the dynamically-linked native libraries. --- .../util/internal/PlatformDependent.java | 82 +++++++++++++++++++ .../java/io/netty/handler/ssl/OpenSsl.java | 16 +++- pom.xml | 3 +- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 91a265b30e1..b7ce81484fb 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -15,6 +15,7 @@ */ package io.netty.util.internal; +import io.netty.util.CharsetUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.jctools.queues.MpscArrayQueue; @@ -28,19 +29,28 @@ import org.jctools.util.Pow2; import org.jctools.util.UnsafeAccess; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Collections; import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Queue; import java.util.Random; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentMap; @@ -97,6 +107,10 @@ public final class PlatformDependent { private static final String NORMALIZED_ARCH = normalizeArch(SystemPropertyUtil.get("os.arch", "")); private static final String NORMALIZED_OS = normalizeOs(SystemPropertyUtil.get("os.name", "")); + // keep in sync with maven's pom.xml via os.detection.classifierWithLikes! + private static final String[] ALLOWED_LINUX_OS_CLASSIFIERS = {"fedora", "suse", "arch"}; + private static final Set LINUX_OS_CLASSIFIERS; + private static final int ADDRESS_SIZE = addressSize0(); private static final boolean USE_DIRECT_BUFFER_NO_CLEANER; private static final AtomicLong DIRECT_MEMORY_COUNTER; @@ -196,6 +210,50 @@ public Random current() { "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + "instability."); } + + // For specifications, see https://www.freedesktop.org/software/systemd/man/os-release.html + final String[] OS_RELEASE_FILES = {"/etc/os-release", "/usr/lib/os-release"}; + final String LINUX_ID_PREFIX = "ID="; + final String LINUX_ID_LIKE_PREFIX = "ID_LIKE="; + Set allowedClassifiers = new HashSet(Arrays.asList(ALLOWED_LINUX_OS_CLASSIFIERS)); + allowedClassifiers = Collections.unmodifiableSet(allowedClassifiers); + Set availableClassifiers = new LinkedHashSet(); + + for (String osReleaseFileName : OS_RELEASE_FILES) { + final File file = new File(osReleaseFileName); + if (file.exists()) { + BufferedReader reader = null; + try { + reader = new BufferedReader( + new InputStreamReader( + new FileInputStream(file), CharsetUtil.UTF_8)); + + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith(LINUX_ID_PREFIX)) { + String id = normalizeOsReleaseVariableValue(line.substring(LINUX_ID_PREFIX.length())); + addClassifier(allowedClassifiers, availableClassifiers, id); + } else if (line.startsWith(LINUX_ID_LIKE_PREFIX)) { + line = normalizeOsReleaseVariableValue(line.substring(LINUX_ID_LIKE_PREFIX.length())); + addClassifier(allowedClassifiers, availableClassifiers, line.split("[ ]+")); + } + } + } catch (IOException ignored) { + // Ignore + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignored) { + // Ignore + } + } + } + // specification states we should only fall back if /etc/os-release does not exist + break; + } + } + LINUX_OS_CLASSIFIERS = Collections.unmodifiableSet(availableClassifiers); } public static boolean hasDirectBufferNoCleanerConstructor() { @@ -1274,6 +1332,30 @@ public static String normalizedOs() { return NORMALIZED_OS; } + public static Set normalizedLinuxClassifiers() { + return LINUX_OS_CLASSIFIERS; + } + + /** + * Adds only those classifier strings to dest which are present in allowed. + * + * @param allowed allowed classifiers + * @param dest destination set + * @param maybeClassifiers potential classifiers to add + */ + private static void addClassifier(Set allowed, Set dest, String... maybeClassifiers) { + for (String id : maybeClassifiers) { + if (allowed.contains(id)) { + dest.add(id); + } + } + } + + private static String normalizeOsReleaseVariableValue(String value) { + // Variable assignment values may be enclosed in double or single quotes. + return value.trim().replaceAll("[\"']", ""); + } + private static String normalize(String value) { return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 3d404838071..8dbca2b138c 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -552,15 +552,25 @@ private static void loadTcNative() throws Exception { String os = PlatformDependent.normalizedOs(); String arch = PlatformDependent.normalizedArch(); - Set libNames = new LinkedHashSet(4); + Set libNames = new LinkedHashSet(5); String staticLibName = "netty_tcnative"; // First, try loading the platform-specific library. Platform-specific // libraries will be available if using a tcnative uber jar. - libNames.add(staticLibName + "_" + os + '_' + arch); if ("linux".equalsIgnoreCase(os)) { - // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0).. + Set classifiers = PlatformDependent.normalizedLinuxClassifiers(); + for (String classifier : classifiers) { + libNames.add(staticLibName + "_" + os + '_' + arch + "_" + classifier); + } + // generic arch-dependent library + libNames.add(staticLibName + "_" + os + '_' + arch); + + // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0). + // note: should already be included from the classifiers but if not, we use this as an + // additional fallback option here libNames.add(staticLibName + "_" + os + '_' + arch + "_fedora"); + } else { + libNames.add(staticLibName + "_" + os + '_' + arch); } libNames.add(staticLibName + "_" + arch); libNames.add(staticLibName); diff --git a/pom.xml b/pom.xml index fcd1e39422f..d82943e7c0e 100644 --- a/pom.xml +++ b/pom.xml @@ -296,8 +296,9 @@ -D_ - fedora 1.6.2 + + fedora,suse,arch netty-tcnative 2.0.25.Final ${os.detected.classifier}