package com.oracle.svm.core.posix;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.function.Function;
import com.oracle.svm.core.posix.linux.libc.GLibC;
import com.oracle.svm.core.c.libc.LibCBase;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
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.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder;
import org.graalvm.word.PointerBase;
import org.graalvm.word.SignedWord;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.CErrorNumber;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.posix.headers.Dlfcn;
import com.oracle.svm.core.posix.headers.Errno;
import com.oracle.svm.core.posix.headers.Locale;
import com.oracle.svm.core.posix.headers.Unistd;
import com.oracle.svm.core.posix.headers.Wait;
import com.oracle.svm.core.util.VMError;
public class PosixUtils {
static String setLocale(String category, String locale) {
int intCategory = getCategory(category);
return setLocale(intCategory, locale);
}
private static String setLocale(int category, String locale) {
if (locale == null) {
CCharPointer cstrResult = Locale.setlocale(category, WordFactory.nullPointer());
return CTypeConversion.toJavaString(cstrResult);
}
try (CCharPointerHolder localePin = CTypeConversion.toCString(locale)) {
CCharPointer cstrLocale = localePin.get();
CCharPointer cstrResult = Locale.setlocale(category, cstrLocale);
return CTypeConversion.toJavaString(cstrResult);
}
}
private static int getCategory(String category) {
switch (category) {
case "LC_ALL":
return Locale.LC_ALL();
case "LC_COLLATE":
return Locale.LC_COLLATE();
case "LC_CTYPE":
return Locale.LC_CTYPE();
case "LC_MONETARY":
return Locale.LC_MONETARY();
case "LC_NUMERIC":
return Locale.LC_NUMERIC();
case "LC_TIME":
return Locale.LC_TIME();
case "LC_MESSAGES":
return Locale.LC_MESSAGES();
}
if (Platform.includedIn(Platform.LINUX.class) && ImageSingletons.lookup(LibCBase.class).getClass().equals(GLibC.class)) {
switch (category) {
case "LC_PAPER":
return Locale.LC_PAPER();
case "LC_NAME":
return Locale.LC_NAME();
case "LC_ADDRESS":
return Locale.LC_ADDRESS();
case "LC_TELEPHONE":
return Locale.LC_TELEPHONE();
case "LC_MEASUREMENT":
return Locale.LC_MEASUREMENT();
case "LC_IDENTIFICATION":
return Locale.LC_IDENTIFICATION();
}
}
throw new IllegalArgumentException("Unknown locale category: " + category);
}
@TargetClass(java.io.FileDescriptor.class)
private static final class Target_java_io_FileDescriptor {
@Alias int fd;
}
public static int getFD(FileDescriptor descriptor) {
return SubstrateUtil.cast(descriptor, Target_java_io_FileDescriptor.class).fd;
}
public static void setFD(FileDescriptor descriptor, int fd) {
SubstrateUtil.cast(descriptor, Target_java_io_FileDescriptor.class).fd = fd;
}
public static String lastErrorString(String defaultMsg) {
int errno = CErrorNumber.getCErrorNumber();
return errorString(errno, defaultMsg);
}
public static IOException newIOExceptionWithLastError(String defaultMsg) {
return new IOException(lastErrorString(defaultMsg));
}
public static String errorString(int errno, String defaultMsg) {
String result = "";
if (errno != 0) {
result = CTypeConversion.toJavaString(Errno.strerror(errno));
}
return result.length() != 0 ? result : defaultMsg;
}
public static int getpid() {
return Unistd.getpid();
}
@Platforms(Platform.HOSTED_ONLY.class)
private static final class ProcessNameProvider implements Function<TargetClass, String> {
@Override
public String apply(TargetClass annotation) {
if (JavaVersionUtil.JAVA_SPEC <= 8) {
return "java.lang.UNIXProcess";
} else {
return "java.lang.ProcessImpl";
}
}
}
@TargetClass(classNameProvider = ProcessNameProvider.class)
private static final class Target_java_lang_UNIXProcess {
@Alias int pid;
}
public static int getpid(Process process) {
Target_java_lang_UNIXProcess instance = SubstrateUtil.cast(process, Target_java_lang_UNIXProcess.class);
return instance.pid;
}
public static int waitForProcessExit(int ppid) {
CIntPointer statusptr = StackValue.get(CIntPointer.class);
while (Wait.waitpid(ppid, statusptr, 0) < 0) {
if (CErrorNumber.getCErrorNumber() == Errno.ECHILD()) {
return 0;
} else if (CErrorNumber.getCErrorNumber() == Errno.EINTR()) {
break;
} else {
return -1;
}
}
int status = statusptr.read();
if (Wait.WIFEXITED(status)) {
return Wait.WEXITSTATUS(status);
} else if (Wait.WIFSIGNALED(status)) {
return 0x80 + Wait.WTERMSIG(status);
}
return status;
}
public static boolean writeBytes(FileDescriptor descriptor, CCharPointer bytes, UnsignedWord length) {
CCharPointer curBuf = bytes;
UnsignedWord curLen = length;
while (curLen.notEqual(0)) {
int fd = getFD(descriptor);
if (fd == -1) {
return false;
}
SignedWord n = Unistd.write(fd, curBuf, curLen);
if (n.equal(-1)) {
return false;
}
curBuf = curBuf.addressOf(n);
curLen = curLen.subtract((UnsignedWord) n);
}
return true;
}
public static boolean flush(FileDescriptor descriptor) {
int fd = getFD(descriptor);
return Unistd.fsync(fd) == 0;
}
public static PointerBase dlopen(String file, int mode) {
try (CCharPointerHolder pathPin = CTypeConversion.toCString(file)) {
CCharPointer pathPtr = pathPin.get();
return Dlfcn.dlopen(pathPtr, mode);
}
}
public static <T extends PointerBase> T dlsym(PointerBase handle, String name) {
try (CCharPointerHolder namePin = CTypeConversion.toCString(name)) {
CCharPointer namePtr = namePin.get();
return Dlfcn.dlsym(handle, namePtr);
}
}
public static String dlerror() {
return CTypeConversion.toJavaString(Dlfcn.dlerror());
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void checkStatusIs0(int status, String message) {
VMError.guarantee(status == 0, message);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean readEntirely(int fd, CCharPointer buffer, int bufferLen) {
int bufferOffset = 0;
for (;;) {
int readBytes = readBytes(fd, buffer, bufferLen - 1, bufferOffset);
if (readBytes < 0) {
return false;
}
bufferOffset += readBytes;
if (readBytes == 0) {
buffer.write(bufferOffset, (byte) 0);
return true;
}
}
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int readBytes(int fd, CCharPointer buffer, int bufferLen, int readOffset) {
int readBytes = -1;
if (readOffset < bufferLen) {
do {
readBytes = (int) Unistd.NoTransitions.read(fd, buffer.addressOf(readOffset), WordFactory.unsigned(bufferLen - readOffset)).rawValue();
} while (readBytes == -1 && CErrorNumber.getCErrorNumber() == Errno.EINTR());
}
return readBytes;
}
}