Skip to content

Commit

Permalink
Support systems with both glibc and Musl (#661)
Browse files Browse the repository at this point in the history
Let users specify "musl" or "glibc" via new system property "aws.crt.libc".
Otherwise, check whether the current Java executable is using glibc or Musl.
Otherwise, do pre-existing check.
  • Loading branch information
graebm authored Aug 11, 2023
1 parent 6fef7ee commit 44c38d2
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 28 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ The `aws-crt` JAR in Maven Central is a large "uber" jar that contains compiled
The [os-maven-plugin](https://github.com/trustin/os-maven-plugin) can automatically detect your platform's classifier at build time.

**NOTES**: The auto-detected `linux-arm_32` platform classifier is not supported, you must specify `linux-armv6` or `linux-armv7`.
Additionally, musl vs glibc detection is not supported either. If you are deploying to a musl-based system and wish to use
Additionally, musl vs glibc detection is not supported either. If you are deploying to a musl-based system and wish to use
a classifier-based jar, you must specify the classifier name yourself.

``` xml
Expand All @@ -125,6 +125,17 @@ a classifier-based jar, you must specify the classifier name yourself.
<dependencies>
```

## System Properties

- To enable logging, set `aws.crt.log.destination` or `aws.crt.log.level`:
- `aws.crt.log.level` - Log level. May be: "None", "Fatal", "Error", "Warn" (default), "Info", "Debug", "Trace".
- `aws.crt.log.destination` - Log destination. May be: "Stderr" (default), "Stdout", "File", "None".
- `aws.crt.log.filename` - File to use when `aws.crt.log.destination` is "File".
- `aws.crt.libc` - (Linux only) Set to "musl" or "glibc" if CRT cannot properly detect which to use.
- `aws.crt.lib.dir` - Set directory where CRT may extract its native library (by default, `java.io.tmpdir` is used)
- `aws.crt.memory.tracing` - May be: "0" (default, no tracing), "1" (track bytes), "2" (more detail).
Allows the CRT.nativeMemory() and CRT.dumpNativeMemory() functions to report native memory usage.

## Mac-Only TLS Behavior

Please note that on Mac, once a private key is used with a certificate, that certificate-key pair is imported into the Mac Keychain. All subsequent uses of that certificate will use the stored private key and ignore anything passed in programmatically. Beginning in v0.6.6, when a stored private key from the Keychain is used, the following will be logged at the "info" log level:
Expand Down
107 changes: 80 additions & 27 deletions src/main/java/software/amazon/awssdk/crt/CRT.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.regex.Pattern;

/**
* This class is responsible for loading the aws-crt-jni shared lib for the
Expand Down Expand Up @@ -152,40 +153,92 @@ public static String getCRuntime(String osIdentifier) {
return NON_LINUX_RUNTIME_TAG;
}

Runtime rt = Runtime.getRuntime();
String[] commands = {"ldd", "--version"};
try {
java.lang.Process proc = rt.exec(commands);

// the "normal" input stream of the proc is the stdout of the invoked command
BufferedReader stdOutput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));

// sometimes, ldd's output goes to stderr, so capture that too
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));

String line;
StringBuilder outputBuilder = new StringBuilder();
while ((line = stdOutput.readLine()) != null) {
outputBuilder.append(line);
// If system property is set, use that.
String systemPropertyOverride = System.getProperty("aws.crt.libc");
if (systemPropertyOverride != null) {
systemPropertyOverride = systemPropertyOverride.toLowerCase().trim();
if (!systemPropertyOverride.isEmpty()) {
return systemPropertyOverride;
}
}

StringBuilder errorBuilder = new StringBuilder();
while ((line = stdError.readLine()) != null) {
errorBuilder.append(line);
// Be warned, the system might have both musl and glibc on it:
// https://github.com/awslabs/aws-crt-java/issues/659

// Next, check which one java is using.
// Run: ldd /path/to/java
// If musl, has a line like: libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f7732ae4000)
// If glibc, has a line like: libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f112c894000)
Pattern muslWord = Pattern.compile("\\bmusl\\b", Pattern.CASE_INSENSITIVE);
Pattern libcWord = Pattern.compile("\\blibc\\b", Pattern.CASE_INSENSITIVE);
String javaHome = System.getProperty("java.home");
if (javaHome != null) {
File javaExecutable = new File(new File(javaHome, "bin"), "java");
if (javaExecutable.exists()) {
try {
String[] lddJavaCmd = {"ldd", javaExecutable.toString()};
List<String> lddJavaOutput = runProcess(lddJavaCmd);
for (String line : lddJavaOutput) {
// check if the "libc" line mentions "musl"
if (libcWord.matcher(line).find()) {
if (muslWord.matcher(line).find()) {
return MUSL_RUNTIME_TAG;
} else {
return GLIBC_RUNTIME_TAG;
}
}
}
// uncertain, continue to next check
} catch (IOException ex) {
// uncertain, continue to next check
}
}
}

String lddOutput = outputBuilder.toString();
String lddError = errorBuilder.toString();
if (lddOutput.contains("musl") || lddError.contains("musl")) {
return MUSL_RUNTIME_TAG;
} else {
return GLIBC_RUNTIME_TAG;
// Next, check whether ldd says it's using musl
// Run: ldd --version
// If musl, has a line like: musl libc (x86_64)
try {
String[] lddVersionCmd = {"ldd", "--version"};
List<String> lddVersionOutput = runProcess(lddVersionCmd);
for (String line : lddVersionOutput) {
// any mention of "musl" is sufficient
if (muslWord.matcher(line).find()) {
return MUSL_RUNTIME_TAG;
}
}
// uncertain, continue to next check

} catch (IOException io) {
return GLIBC_RUNTIME_TAG;
// uncertain, continue to next check
}

// Assume it's glibc
return GLIBC_RUNTIME_TAG;
}

// Run process and return lines of output.
// Output is stdout and stderr merged together.
// The exit code is ignored.
// We do it this way because, on some Linux distros (Alpine),
// "ldd --version" reports exit code 1 and prints to stderr.
// But on most Linux distros it reports exit code 0 and prints to stdout.
private static List<String> runProcess(String[] cmdArray) throws IOException {
java.lang.Process proc = new ProcessBuilder(cmdArray)
.redirectErrorStream(true) // merge stderr into stdout
.start();

// confusingly, getInputStream() gets you stdout
BufferedReader outputReader = new BufferedReader(new
InputStreamReader(proc.getInputStream()));

String line;
List<String> output = new ArrayList<String>();
while ((line = outputReader.readLine()) != null) {
output.add(line);
}

return output;
}

private static void extractAndLoadLibrary(String path) {
Expand Down

0 comments on commit 44c38d2

Please sign in to comment.