-
Notifications
You must be signed in to change notification settings - Fork 337
Expand file tree
/
Copy pathOperatingSystem.java
More file actions
227 lines (201 loc) · 6.42 KB
/
OperatingSystem.java
File metadata and controls
227 lines (201 loc) · 6.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package datadog.environment;
import static datadog.environment.OperatingSystem.Architecture.ARM64;
import static datadog.environment.OperatingSystem.Type.LINUX;
import static datadog.environment.OperatingSystem.Type.MACOS;
import static datadog.environment.OperatingSystem.Type.WINDOWS;
import static java.util.Locale.ROOT;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/** Detects operating systems and libc library. */
public final class OperatingSystem {
private static final String OS_NAME_PROPERTY = "os.name";
private static final String OS_ARCH_PROPERTY = "os.arch";
private static final Type TYPE = Type.current();
private static final Architecture ARCHITECTURE = Architecture.current();
private OperatingSystem() {}
/**
* Checks whether the architecture is arm64.
*
* @return @{@code true} if architecture is arm64, {@code false} otherwise.
*/
public static boolean isArm64() {
return ARCHITECTURE == ARM64;
}
/**
* Checks whether the operating system is Linux based.
*
* @return @{@code true} if operating system is Linux based, {@code false} otherwise.
*/
public static boolean isLinux() {
return TYPE == LINUX;
}
/**
* Checks whether the operating system is Windows.
*
* @return @{@code true} if operating system is Windows, {@code false} otherwise.
*/
public static boolean isWindows() {
return TYPE == WINDOWS;
}
/**
* Checks whether the operating system is macOS.
*
* @return @{@code true} if operating system is macOS, {@code false} otherwise.
*/
public static boolean isMacOs() {
return TYPE == MACOS;
}
/**
* Gets the operating system type.
*
* @return The operating system type, {@link Type#UNKNOWN} if not properly detected or supported.
*/
public static Type type() {
return TYPE;
}
/** Gets the operating system architecture . */
public static Architecture architecture() {
return ARCHITECTURE;
}
/**
* Checks whether the libc is MUSL.
*
* @return {@code true} if the libc is MUSL, {@code false} otherwise.
*/
public static boolean isMusl() {
if (!isLinux()) {
return false;
}
// check the Java exe then fall back to proc/self maps
try {
return isMuslJavaExecutable();
} catch (IOException e) {
try {
return isMuslProcSelfMaps();
} catch (IOException ignore) {
return false;
}
}
}
private static boolean isMuslProcSelfMaps() throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("-musl-")) {
return true;
}
if (line.contains("/libc.")) {
return false;
}
}
}
return false;
}
/**
* There is information about the linking in the ELF file. Since properly parsing ELF is not
* trivial this code will attempt a brute-force approach and will scan the first 4096 bytes of the
* 'java' program image for anything prefixed with `/ld-` - in practice this will contain
* `/ld-musl` for musl systems and probably something else for non-musl systems (e.g.
* `/ld-linux-...`). However, if such string is missing should indicate that the system is not a
* musl one.
*/
private static boolean isMuslJavaExecutable() throws IOException {
byte[] magic = new byte[] {(byte) 0x7f, (byte) 'E', (byte) 'L', (byte) 'F'};
byte[] prefix = new byte[] {(byte) '/', (byte) 'l', (byte) 'd', (byte) '-'}; // '/ld-*'
byte[] musl = new byte[] {(byte) 'm', (byte) 'u', (byte) 's', (byte) 'l'}; // 'musl'
Path binary = Paths.get(SystemProperties.getOrDefault("java.home", ""), "bin", "java");
byte[] buffer = new byte[4096];
try (InputStream is = Files.newInputStream(binary)) {
int read = is.read(buffer, 0, 4);
if (read != 4 || !containsArray(buffer, 0, magic)) {
throw new IOException(Arrays.toString(buffer));
}
read = is.read(buffer);
if (read <= 0) {
throw new IOException();
}
int prefixPos = 0;
for (int i = 0; i < read; i++) {
if (buffer[i] == prefix[prefixPos]) {
if (++prefixPos == prefix.length) {
return containsArray(buffer, i + 1, musl);
}
} else {
prefixPos = 0;
}
}
}
return false;
}
private static boolean containsArray(byte[] container, int offset, byte[] contained) {
for (int i = 0; i < contained.length; i++) {
int leftPos = offset + i;
if (leftPos >= container.length) {
return false;
}
if (container[leftPos] != contained[i]) {
return false;
}
}
return true;
}
public enum Type {
WINDOWS("Windows"),
MACOS("MacOS"),
LINUX("Linux"),
UNKNOWN("unknown");
private final String name;
Type(String name) {
this.name = name;
}
static Type current() {
String property = SystemProperties.getOrDefault(OS_NAME_PROPERTY, "").toLowerCase(ROOT);
// https://mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/
if (property.contains("linux")) {
return LINUX;
} else if (property.contains("win")) {
return WINDOWS;
} else if (property.contains("mac")) {
return MACOS;
} else {
return UNKNOWN;
}
}
@Override
public String toString() {
return this.name;
}
}
/** Detects the operating system architecture. */
public enum Architecture {
X64("x86_64", "amd64", "k8"),
X86("x86", "i386", "i486", "i586", "i686"),
ARM("arm", "aarch32"),
ARM64("arm64", "aarch64"),
UNKNOWN();
private final Set<String> identifiers;
Architecture(String... identifiers) {
this.identifiers = new HashSet<>(Arrays.asList(identifiers));
}
static Architecture of(String identifier) {
for (Architecture architecture : Architecture.values()) {
if (architecture.identifiers.contains(identifier)) {
return architecture;
}
}
return UNKNOWN;
}
static Architecture current() {
String property = SystemProperties.getOrDefault(OS_ARCH_PROPERTY, "").toLowerCase(ROOT);
return Architecture.of(property);
}
}
}