Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support systems with both glibc and Musl #661

Merged
merged 8 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
waahm7 marked this conversation as resolved.
Show resolved Hide resolved
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