From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andrew Kaster <andrewdkaster@gmail.com>
Date: Sun, 12 Jun 2022 23:15:17 -0600
Subject: [PATCH] java.base: Enable java.lang.Process on minerva

---
 make/modules/java.base/Launcher.gmk           |   2 +-
 make/modules/java.base/lib/CoreLibraries.gmk  |   3 +
 .../libjava/ProcessHandleImpl_minerva.cpp    | 163 ++++++++++++++++++
 .../unix/classes/java/lang/ProcessImpl.java   |   7 +-
 4 files changed, 173 insertions(+), 2 deletions(-)
 create mode 100644 src/java.base/minerva/native/libjava/ProcessHandleImpl_minerva.cpp

diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk
index 700ddefda49e891ac1a2cfd8602fb8a9409ad1d4..78c884dae8271aea4431976823a0f18506cab4b4 100644
--- a/make/modules/java.base/Launcher.gmk
+++ b/make/modules/java.base/Launcher.gmk
@@ -73,7 +73,7 @@ endif
 
 ################################################################################
 
-ifeq ($(call isTargetOs, macosx aix linux), true)
+ifeq ($(call isTargetOs, macosx aix linux minerva), true)
   $(eval $(call SetupJdkExecutable, BUILD_JSPAWNHELPER, \
       NAME := jspawnhelper, \
       SRC := $(TOPDIR)/src/$(MODULE)/unix/native/jspawnhelper, \
diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk
index e29f9d5ad78d6da367579dfda7b8e9c0d09be2c9..6054e584b13928cecf4ef0547bf32f5a9d830050 100644
--- a/make/modules/java.base/lib/CoreLibraries.gmk
+++ b/make/modules/java.base/lib/CoreLibraries.gmk
@@ -90,6 +90,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
     OPTIMIZATION := HIGH, \
     CFLAGS := $(CFLAGS_JDKLIB) \
         $(LIBJAVA_CFLAGS), \
+    CXXFLAGS := $(CXXFLAGS_JDKLIB) \
+        $(LIBJAVA_CXXFLAGS) -fno-exceptions, \
     jdk_util.c_CFLAGS := $(VERSION_CFLAGS), \
     EXTRA_HEADER_DIRS := libfdlibm, \
     WARNINGS_AS_ERRORS_xlc := false, \
@@ -102,6 +104,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
     LIBS_unix := -ljvm, \
     LIBS_linux := $(LIBDL), \
     LIBS_aix := $(LIBDL) $(LIBM),\
+    LIBS_minerva := $(LIBDL) -lcore -lcoreminimal, \
     LIBS_macosx := -framework CoreFoundation \
         -framework Foundation \
         -framework SystemConfiguration, \
diff --git a/src/java.base/minerva/native/libjava/ProcessHandleImpl_minerva.cpp b/src/java.base/minerva/native/libjava/ProcessHandleImpl_minerva.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a3d5cdc94f59d38f7dd216f9661592cde746bb02
--- /dev/null
+++ b/src/java.base/minerva/native/libjava/ProcessHandleImpl_minerva.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#define AK_DONT_REPLACE_STD
+
+#include "jvm.h"
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_String.h"
+
+extern "C" {
+#include "ProcessHandleImpl_unix.h"
+}
+
+#include <AK/JsonArray.h>
+#include <LibCore/File.h>
+#include <LibCore/ProcessStatisticsReader.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * Implementation of native ProcessHandleImpl functions for MINERVA.
+ * See ProcessHandleImpl_unix.c for more details.
+ */
+
+#define JAVA_TRY(expression, message) \
+    ({                                                                                   \
+        auto _temporary_result = (expression);                                           \
+        if (_temporary_result.is_error()) [[unlikely]]                                   \
+            return throwMinervaError(env, _temporary_result.release_error(), (message));  \
+        _temporary_result.release_value();                                               \
+    })
+
+
+extern "C" {
+void os_initNative(JNIEnv *env, jclass clazz) {
+}
+
+jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
+                    jlongArray jparentArray, jlongArray jstimesArray) {
+    return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
+}
+
+pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
+    // Won't read usernames, not even UIDs:
+    auto maybe_stats = Core::ProcessStatisticsReader::get_all(false);
+    if (maybe_stats.is_error()) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed");
+            return -1;
+    }
+    auto stats = maybe_stats.release_value();
+    auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) {
+        return proc_stats.pid == pid;
+    });
+    if (proc_it == stats.processes.end()) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "Selected pid does not exist");
+        return -1;
+    }
+    auto& proc = *proc_it;
+
+    for (auto& thread : proc.threads) {
+        *total += thread.time_user + thread.time_kernel;
+    }
+
+    *start = 0; // FIXME: When did thread start? not reported in /sys/kernel/processes
+
+    return proc.ppid;
+}
+
+
+static void throwMinervaError(JNIEnv* env, Error const& e, StringView msg) {
+    char err_buf[256];
+    if (e.is_errno())
+        getErrorString(e.code(), err_buf, sizeof(err_buf));
+    else
+        strncpy(err_buf, e.string_literal().characters_without_null_termination(), sizeof(err_buf) - 1);
+    jstring s = JNU_NewStringPlatform(env, err_buf);
+    if (s != NULL) {
+        jobject x = JNU_NewObjectByName(env, "java/lang/RuntimeException",
+                                        "(Ljava/lang/String;)V", s);
+        if (x != NULL) {
+            env->Throw((jthrowable)x);
+        }
+    }
+    if (!env->ExceptionOccurred()) {
+        JNU_ThrowByName(env, "java/lang/RuntimeException", msg.characters_without_null_termination());
+    }
+}
+
+void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+    // Won't read usernames, only UIDs:
+    auto maybe_stats = Core::ProcessStatisticsReader::get_all(false);
+    if (maybe_stats.is_error()) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed");
+        return;
+    }
+
+    auto stats = maybe_stats.release_value();
+    auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) {
+        return proc_stats.pid == pid;
+    });
+    if (proc_it == stats.processes.end()) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "Selected pid does not exist");
+        return;
+    }
+    auto& proc = *proc_it;
+
+    unix_getUserInfo(env, jinfo, proc.pid);
+    JNU_CHECK_EXCEPTION(env);
+
+    auto cmdline_file = JAVA_TRY(Core::File::open(ByteString::formatted("/proc/{}/cmdline", pid), Core::File::OpenMode::Read), "Unable to open /proc/pid/cmdline"sv);
+    auto contents = JAVA_TRY(cmdline_file->read_until_eof(), "Unable to read /proc/pid/cmdline"sv);
+    auto cmdline = JAVA_TRY(JsonValue::from_string(contents), "Invalid JSON in /proc/pid/cmdline"sv);
+
+    if (!cmdline.is_array())
+        return throwMinervaError(env, Error::from_string_literal("Not an array"), "Unexpected JSON in /proc/pid/cmdline"sv);
+
+    jstring cmdexe = JNU_NewStringPlatform(env, cmdline.as_array()[0].as_string().characters());
+    env->ExceptionClear();        // unconditionally clear any exception
+    env->SetObjectField(jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
+
+    int arr_size = cmdline.as_array().size();
+    jclass string_clazz = JNU_ClassString(env);
+    CHECK_NULL(string_clazz);
+    jobjectArray java_cmdline = env->NewObjectArray(arr_size, string_clazz, NULL);
+    CHECK_NULL(java_cmdline);
+    jstring elem = NULL;
+    for (int i = 0; i < arr_size; ++i) {
+        elem = JNU_NewStringPlatform(env, cmdline.as_array()[i].as_string().characters());
+        CHECK_NULL(elem);
+        env->SetObjectArrayElement(java_cmdline, i, elem);
+        JNU_CHECK_EXCEPTION(env);
+    }
+    env->SetObjectField(jinfo, ProcessHandleImpl_Info_argumentsID, java_cmdline);
+    JNU_CHECK_EXCEPTION(env);
+}
+}
diff --git a/src/java.base/unix/classes/java/lang/ProcessImpl.java b/src/java.base/unix/classes/java/lang/ProcessImpl.java
index 2bf36f8f136794af4030e12c64026ab217696959..317bbf158a2032cc23ef3b73528a9e9c801612cf 100644
--- a/src/java.base/unix/classes/java/lang/ProcessImpl.java
+++ b/src/java.base/unix/classes/java/lang/ProcessImpl.java
@@ -89,7 +89,9 @@ final class ProcessImpl extends Process {
 
         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
 
-        AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
+        AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
+
+        MINERVA(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
 
         final LaunchMechanism defaultLaunchMechanism;
         final Set<LaunchMechanism> validLaunchMechanisms;
@@ -135,6 +137,7 @@ final class ProcessImpl extends Process {
             if (osName.equals("Linux")) { return LINUX; }
             if (osName.contains("OS X")) { return BSD; }
             if (osName.equals("AIX")) { return AIX; }
+            if (osName.equals("Minerva")) { return MINERVA; }
 
             throw new Error(osName + " is not a supported OS platform.");
         }
@@ -348,6 +351,7 @@ final class ProcessImpl extends Process {
         switch (platform) {
             case LINUX:
             case BSD:
+            case MINERVA:
                 stdin = (fds[0] == -1) ?
                         ProcessBuilder.NullOutputStream.INSTANCE :
                         new ProcessPipeOutputStream(fds[0]);
@@ -467,6 +471,7 @@ final class ProcessImpl extends Process {
             case LINUX:
             case BSD:
             case AIX:
+            case MINERVA:
                 // There is a risk that pid will be recycled, causing us to
                 // kill the wrong process!  So we only terminate processes
                 // that appear to still be running.  Even with this check,
