package com.oracle.svm.core.windows;
import java.nio.charset.StandardCharsets;
import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.c.type.VoidPointer;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.c.NonmovableArrays;
import com.oracle.svm.core.jdk.SystemPropertiesSupport;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.core.windows.headers.FileAPI;
import com.oracle.svm.core.windows.headers.LibC;
import com.oracle.svm.core.windows.headers.LibC.WCharPointer;
import com.oracle.svm.core.windows.headers.Process;
import com.oracle.svm.core.windows.headers.SysinfoAPI;
import com.oracle.svm.core.windows.headers.VerRsrc;
import com.oracle.svm.core.windows.headers.WinBase;
import com.oracle.svm.core.windows.headers.WinVer;
@Platforms(Platform.WINDOWS.class)
public class WindowsSystemPropertiesSupport extends SystemPropertiesSupport {
private static final byte[] USERNAME = "USERNAME\0".getBytes(StandardCharsets.UTF_16LE);
private static final byte[] KERNEL32_DLL = "\\kernel32.dll\0".getBytes(StandardCharsets.UTF_16LE);
private static final byte[] ROOT_PATH = "\\\0".getBytes(StandardCharsets.UTF_16LE);
private static final int VER_NT_WORKSTATION = 0x0000001;
private static final int VER_PLATFORM_WIN32_WINDOWS = 1;
private static final int VER_PLATFORM_WIN32_NT = 2;
@Override
protected String userNameValue() {
WCharPointer userName = LibC._wgetenv(NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(USERNAME), 0));
UnsignedWord length = LibC.wcslen(userName);
if (userName.isNonNull() && length.aboveThan(0)) {
return toJavaString(userName, length);
}
int maxLength = WinBase.UNLEN + 1;
userName = StackValue.get(maxLength, WCharPointer.class);
CIntPointer lengthPointer = StackValue.get(CIntPointer.class);
lengthPointer.write(maxLength);
if (WinBase.GetUserNameW(userName, lengthPointer) == 0) {
return "unknown";
}
return toJavaString(userName, WordFactory.unsigned(lengthPointer.read() - 1));
}
@Override
protected String userHomeValue() {
WinBase.LPHANDLE tokenHandle = StackValue.get(WinBase.LPHANDLE.class);
if (Process.OpenProcessToken(Process.GetCurrentProcess(), Process.TOKEN_QUERY(), tokenHandle) == 0) {
return "C:\\";
}
int initialLen = WinBase.MAX_PATH + 1;
CIntPointer buffLenPointer = StackValue.get(CIntPointer.class);
buffLenPointer.write(initialLen);
WCharPointer userHome = StackValue.get(initialLen, WCharPointer.class);
int result = WinBase.GetUserProfileDirectoryW(tokenHandle.read(), userHome, buffLenPointer);
WinBase.CloseHandle(tokenHandle.read());
if (result == 0) {
return "C:\\";
}
return toJavaString(userHome, WordFactory.unsigned(buffLenPointer.read() - 1));
}
@Override
protected String userDirValue() {
int maxLength = WinBase.MAX_PATH;
WCharPointer userDir = StackValue.get(maxLength, WCharPointer.class);
int length = WinBase.GetCurrentDirectoryW(maxLength, userDir);
VMError.guarantee(length > 0 && length < maxLength, "Could not determine value of user.dir");
return toJavaString(userDir, WordFactory.unsigned(length));
}
@Override
protected String tmpdirValue() {
int maxLength = WinBase.MAX_PATH + 1;
WCharPointer tmpdir = StackValue.get(maxLength, WCharPointer.class);
int length = FileAPI.GetTempPathW(maxLength, tmpdir);
VMError.guarantee(length > 0, "Could not determine value of java.io.tmpdir");
return toJavaString(tmpdir, WordFactory.unsigned(length));
}
private static String (WCharPointer wcString, UnsignedWord length) {
return CTypeConversion.toJavaString((CCharPointer) wcString, SizeOf.unsigned(WCharPointer.class).multiply(length), StandardCharsets.UTF_16LE);
}
private Pair<String, String> cachedOsNameAndVersion;
@Override
protected String osNameValue() {
if (cachedOsNameAndVersion == null) {
cachedOsNameAndVersion = getOsNameAndVersion();
}
return cachedOsNameAndVersion.getLeft();
}
@Override
protected String osVersionValue() {
if (cachedOsNameAndVersion == null) {
cachedOsNameAndVersion = getOsNameAndVersion();
}
return cachedOsNameAndVersion.getRight();
}
public Pair<String, String> getOsNameAndVersion() {
SysinfoAPI.OSVERSIONINFOEXA ver = StackValue.get(SysinfoAPI.OSVERSIONINFOEXA.class);
ver.dwOSVersionInfoSize(SizeOf.get(SysinfoAPI.OSVERSIONINFOEXA.class));
SysinfoAPI.GetVersionExA(ver);
boolean is64bit = ImageSingletons.lookup(Platform.class).getArchitecture().endsWith("64");
boolean isWorkstation = ver.wProductType() == VER_NT_WORKSTATION;
int platformId = ver.dwPlatformId();
int majorVersion = ver.dwMajorVersion();
int minorVersion = ver.dwMinorVersion();
int buildNumber = ver.dwBuildNumber();
do {
LibC.WCharPointer kernel32Path = StackValue.get(WinBase.MAX_PATH, LibC.WCharPointer.class);
LibC.WCharPointer kernel32Dll = NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(KERNEL32_DLL), 0);
int len = WinBase.MAX_PATH - (int) LibC.wcslen(kernel32Dll).rawValue() - 1;
int ret = SysinfoAPI.GetSystemDirectoryW(kernel32Path, len);
if (ret == 0 || ret > len) {
break;
}
LibC.wcsncat(kernel32Path, kernel32Dll, WordFactory.unsigned(WinBase.MAX_PATH - ret));
int versionSize = WinVer.GetFileVersionInfoSizeW(kernel32Path, WordFactory.nullPointer());
if (versionSize == 0) {
break;
}
VoidPointer versionInfo = LibC.malloc(WordFactory.unsigned(versionSize));
if (versionInfo.isNull()) {
break;
}
if (WinVer.GetFileVersionInfoW(kernel32Path, 0, versionSize, versionInfo) == 0) {
LibC.free(versionInfo);
break;
}
LibC.WCharPointer rootPath = NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(ROOT_PATH), 0);
WordPointer fileInfoPointer = StackValue.get(WordPointer.class);
CIntPointer lengthPointer = StackValue.get(CIntPointer.class);
if (WinVer.VerQueryValueW(versionInfo, rootPath, fileInfoPointer, lengthPointer) == 0) {
LibC.free(versionInfo);
break;
}
VerRsrc.VS_FIXEDFILEINFO fileInfo = fileInfoPointer.read();
majorVersion = (short) (fileInfo.dwProductVersionMS() >> 16);
minorVersion = (short) fileInfo.dwProductVersionMS();
buildNumber = (short) (fileInfo.dwProductVersionLS() >> 16);
LibC.free(versionInfo);
} while (false);
String osVersion = majorVersion + "." + minorVersion;
String osName;
switch (platformId) {
case VER_PLATFORM_WIN32_WINDOWS:
if (majorVersion == 4) {
switch (minorVersion) {
case 0:
osName = "Windows 95";
break;
case 10:
osName = "Windows 98";
break;
case 90:
osName = "Windows Me";
break;
default:
osName = "Windows 9X (unknown)";
break;
}
} else {
osName = "Windows 9X (unknown)";
}
break;
case VER_PLATFORM_WIN32_NT:
if (majorVersion <= 4) {
osName = "Windows NT";
} else if (majorVersion == 5) {
switch (minorVersion) {
case 0:
osName = "Windows 2000";
break;
case 1:
osName = "Windows XP";
break;
case 2:
if (isWorkstation && is64bit) {
osName = "Windows XP";
} else {
osName = "Windows 2003";
}
break;
default:
osName = "Windows NT (unknown)";
break;
}
} else if (majorVersion == 6) {
if (isWorkstation) {
switch (minorVersion) {
case 0:
osName = "Windows Vista";
break;
case 1:
osName = "Windows 7";
break;
case 2:
osName = "Windows 8";
break;
case 3:
osName = "Windows 8.1";
break;
default:
osName = "Windows NT (unknown)";
}
} else {
switch (minorVersion) {
case 0:
osName = "Windows Server 2008";
break;
case 1:
osName = "Windows Server 2008 R2";
break;
case 2:
osName = "Windows Server 2012";
break;
case 3:
osName = "Windows Server 2012 R2";
break;
default:
osName = "Windows NT (unknown)";
}
}
} else if (majorVersion == 10) {
if (isWorkstation) {
switch (minorVersion) {
case 0:
osName = "Windows 10";
break;
default:
osName = "Windows NT (unknown)";
}
} else {
switch (minorVersion) {
case 0:
if (buildNumber > 17762) {
osName = "Windows Server 2019";
} else {
osName = "Windows Server 2016";
}
break;
default:
osName = "Windows NT (unknown)";
}
}
} else {
osName = "Windows NT (unknown)";
}
break;
default:
osName = "Windows (unknown)";
break;
}
return Pair.create(osName, osVersion);
}
}
@Platforms(Platform.WINDOWS.class)
@AutomaticFeature
class WindowsSystemPropertiesFeature implements Feature {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
ImageSingletons.add(SystemPropertiesSupport.class, new WindowsSystemPropertiesSupport());
}
}