0008-java.base-Enable-java.lang.Process-on-serenity.patch 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
  2. From: Andrew Kaster <andrewdkaster@gmail.com>
  3. Date: Sun, 12 Jun 2022 23:15:17 -0600
  4. Subject: [PATCH] java.base: Enable java.lang.Process on serenity
  5. ---
  6. make/modules/java.base/Launcher.gmk | 2 +-
  7. make/modules/java.base/lib/CoreLibraries.gmk | 3 +
  8. .../libjava/ProcessHandleImpl_serenity.cpp | 167 ++++++++++++++++++
  9. .../unix/classes/java/lang/ProcessImpl.java | 7 +-
  10. 4 files changed, 177 insertions(+), 2 deletions(-)
  11. create mode 100644 src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp
  12. diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk
  13. index 700ddefda..78c884dae 100644
  14. --- a/make/modules/java.base/Launcher.gmk
  15. +++ b/make/modules/java.base/Launcher.gmk
  16. @@ -73,7 +73,7 @@ endif
  17. ################################################################################
  18. -ifeq ($(call isTargetOs, macosx aix linux), true)
  19. +ifeq ($(call isTargetOs, macosx aix linux serenity), true)
  20. $(eval $(call SetupJdkExecutable, BUILD_JSPAWNHELPER, \
  21. NAME := jspawnhelper, \
  22. SRC := $(TOPDIR)/src/$(MODULE)/unix/native/jspawnhelper, \
  23. diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk
  24. index 0a61d009f..7867a3095 100644
  25. --- a/make/modules/java.base/lib/CoreLibraries.gmk
  26. +++ b/make/modules/java.base/lib/CoreLibraries.gmk
  27. @@ -90,6 +90,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
  28. OPTIMIZATION := HIGH, \
  29. CFLAGS := $(CFLAGS_JDKLIB) \
  30. $(LIBJAVA_CFLAGS), \
  31. + CXXFLAGS := $(CXXFLAGS_JDKLIB) \
  32. + $(LIBJAVA_CXXFLAGS), \
  33. jdk_util.c_CFLAGS := $(VERSION_CFLAGS), \
  34. EXTRA_HEADER_DIRS := libfdlibm, \
  35. WARNINGS_AS_ERRORS_xlc := false, \
  36. @@ -102,6 +104,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
  37. LIBS_unix := -ljvm, \
  38. LIBS_linux := $(LIBDL), \
  39. LIBS_aix := $(LIBDL) $(LIBM),\
  40. + LIBS_serenity := $(LIBDL) -lcore, \
  41. LIBS_macosx := -framework CoreFoundation \
  42. -framework Foundation \
  43. -framework SystemConfiguration, \
  44. diff --git a/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp b/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp
  45. new file mode 100644
  46. index 000000000..e9bb2ec4a
  47. --- /dev/null
  48. +++ b/src/java.base/serenity/native/libjava/ProcessHandleImpl_serenity.cpp
  49. @@ -0,0 +1,167 @@
  50. +/*
  51. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
  52. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  53. + *
  54. + * This code is free software; you can redistribute it and/or modify it
  55. + * under the terms of the GNU General Public License version 2 only, as
  56. + * published by the Free Software Foundation. Oracle designates this
  57. + * particular file as subject to the "Classpath" exception as provided
  58. + * by Oracle in the LICENSE file that accompanied this code.
  59. + *
  60. + * This code is distributed in the hope that it will be useful, but WITHOUT
  61. + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  62. + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  63. + * version 2 for more details (a copy is included in the LICENSE file that
  64. + * accompanied this code).
  65. + *
  66. + * You should have received a copy of the GNU General Public License version
  67. + * 2 along with this work; if not, write to the Free Software Foundation,
  68. + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  69. + *
  70. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  71. + * or visit www.oracle.com if you need additional information or have any
  72. + * questions.
  73. + */
  74. +
  75. +#define AK_DONT_REPLACE_STD
  76. +
  77. +#include "jvm.h"
  78. +#include "jni.h"
  79. +#include "jni_util.h"
  80. +#include "java_lang_String.h"
  81. +
  82. +extern "C" {
  83. +#include "ProcessHandleImpl_unix.h"
  84. +}
  85. +
  86. +#include <AK/JsonArray.h>
  87. +#include <LibCore/ProcessStatisticsReader.h>
  88. +#include <LibCore/Stream.h>
  89. +#include <stdio.h>
  90. +#include <string.h>
  91. +
  92. +/*
  93. + * Implementation of native ProcessHandleImpl functions for SERENITY.
  94. + * See ProcessHandleImpl_unix.c for more details.
  95. + */
  96. +
  97. +#define JAVA_TRY(expression, message) \
  98. + ({ \
  99. + auto _temporary_result = (expression); \
  100. + if (_temporary_result.is_error()) [[unlikely]] \
  101. + return throwSerenityError(env, _temporary_result.release_error(), (message)); \
  102. + _temporary_result.release_value(); \
  103. + })
  104. +
  105. +
  106. +static RefPtr<Core::File> proc_all;
  107. +
  108. +extern "C" {
  109. +void os_initNative(JNIEnv *env, jclass clazz) {
  110. + proc_all = MUST(Core::File::open("/proc/all", Core::OpenMode::ReadOnly));
  111. +}
  112. +
  113. +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
  114. + jlongArray jparentArray, jlongArray jstimesArray) {
  115. + return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
  116. +}
  117. +
  118. +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
  119. + auto maybe_stats = Core::ProcessStatisticsReader::get_all(proc_all);
  120. + if (!maybe_stats.has_value()) {
  121. + JNU_ThrowByNameWithLastError(env,
  122. + "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed");
  123. + return -1;
  124. + }
  125. + auto stats = maybe_stats.release_value();
  126. + auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) {
  127. + return proc_stats.pid == pid;
  128. + });
  129. + if (proc_it == stats.processes.end()) {
  130. + JNU_ThrowByNameWithLastError(env,
  131. + "java/lang/RuntimeException", "Selected pid does not exist");
  132. + return -1;
  133. + }
  134. + auto& proc = *proc_it;
  135. +
  136. + for (auto& thread : proc.threads) {
  137. + *total += thread.time_user + thread.time_kernel;
  138. + }
  139. +
  140. + *start = 0; // FIXME: When did thread start? not reported in /proc/all
  141. +
  142. + return proc.ppid;
  143. +}
  144. +
  145. +
  146. +static void throwSerenityError(JNIEnv* env, Error const& e, StringView msg) {
  147. + char err_buf[256];
  148. + if (e.is_errno())
  149. + getErrorString(e.code(), err_buf, sizeof(err_buf));
  150. + else
  151. + strncpy(err_buf, e.string_literal().characters_without_null_termination(), sizeof(err_buf) - 1);
  152. + jstring s = JNU_NewStringPlatform(env, err_buf);
  153. + if (s != NULL) {
  154. + jobject x = JNU_NewObjectByName(env, "java/lang/RuntimeException",
  155. + "(Ljava/lang/String;)V", s);
  156. + if (x != NULL) {
  157. + env->Throw((jthrowable)x);
  158. + }
  159. + }
  160. + if (!env->ExceptionOccurred()) {
  161. + JNU_ThrowByName(env, "java/lang/RuntimeException", msg.characters_without_null_termination());
  162. + }
  163. +}
  164. +
  165. +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
  166. + auto maybe_stats = Core::ProcessStatisticsReader::get_all(proc_all);
  167. + if (!maybe_stats.has_value()) {
  168. + JNU_ThrowByNameWithLastError(env,
  169. + "java/lang/RuntimeException", "ProcessStatisticsReader::get_all failed");
  170. + return;
  171. + }
  172. +
  173. + auto stats = maybe_stats.release_value();
  174. + auto proc_it = find_if(stats.processes.begin(), stats.processes.end(), [pid](auto& proc_stats) {
  175. + return proc_stats.pid == pid;
  176. + });
  177. + if (proc_it == stats.processes.end()) {
  178. + JNU_ThrowByNameWithLastError(env,
  179. + "java/lang/RuntimeException", "Selected pid does not exist");
  180. + return;
  181. + }
  182. + auto& proc = *proc_it;
  183. +
  184. + unix_getUserInfo(env, jinfo, proc.pid);
  185. + JNU_CHECK_EXCEPTION(env);
  186. +
  187. + char cmdline_name[256];
  188. + snprintf(cmdline_name , sizeof(cmdline_name) - 1, "/proc/%d/cmdline", pid);
  189. +
  190. + auto cmdline_file = JAVA_TRY(Core::Stream::File::open(cmdline_name, Core::Stream::OpenMode::Read), "Unable to open /proc/pid/cmdline"sv);
  191. + auto contents = JAVA_TRY(cmdline_file->read_all(), "Unable to read /proc/pid/cmdline"sv);
  192. + auto cmdline = JAVA_TRY(JsonValue::from_string(contents), "Invalid JSON in /proc/pid/cmdline"sv);
  193. +
  194. + if (!cmdline.is_array())
  195. + return throwSerenityError(env, Error::from_string_literal("Not an array"), "Unexpected JSON in /proc/pid/cmdline");
  196. +
  197. + jstring cmdexe = JNU_NewStringPlatform(env, cmdline.as_array()[0].as_string().characters());
  198. + env->ExceptionClear(); // unconditionally clear any exception
  199. + env->SetObjectField(jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
  200. +
  201. + int arr_size = cmdline.as_array().size();
  202. + jclass string_clazz = JNU_ClassString(env);
  203. + CHECK_NULL(string_clazz);
  204. + jobjectArray java_cmdline = env->NewObjectArray(arr_size, string_clazz, NULL);
  205. + CHECK_NULL(java_cmdline);
  206. + jstring elem = NULL;
  207. + for (int i = 0; i < arr_size; ++i) {
  208. + elem = JNU_NewStringPlatform(env, cmdline.as_array()[i].as_string().characters());
  209. + CHECK_NULL(elem);
  210. + env->SetObjectArrayElement(java_cmdline, i, elem);
  211. + JNU_CHECK_EXCEPTION(env);
  212. + }
  213. + env->SetObjectField(jinfo, ProcessHandleImpl_Info_argumentsID, java_cmdline);
  214. + JNU_CHECK_EXCEPTION(env);
  215. +}
  216. +}
  217. diff --git a/src/java.base/unix/classes/java/lang/ProcessImpl.java b/src/java.base/unix/classes/java/lang/ProcessImpl.java
  218. index 2bf36f8f1..317bbf158 100644
  219. --- a/src/java.base/unix/classes/java/lang/ProcessImpl.java
  220. +++ b/src/java.base/unix/classes/java/lang/ProcessImpl.java
  221. @@ -89,7 +89,9 @@ final class ProcessImpl extends Process {
  222. BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  223. - AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
  224. + AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  225. +
  226. + SERENITY(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
  227. final LaunchMechanism defaultLaunchMechanism;
  228. final Set<LaunchMechanism> validLaunchMechanisms;
  229. @@ -135,6 +137,7 @@ final class ProcessImpl extends Process {
  230. if (osName.equals("Linux")) { return LINUX; }
  231. if (osName.contains("OS X")) { return BSD; }
  232. if (osName.equals("AIX")) { return AIX; }
  233. + if (osName.equals("SerenityOS")) { return SERENITY; }
  234. throw new Error(osName + " is not a supported OS platform.");
  235. }
  236. @@ -348,6 +351,7 @@ final class ProcessImpl extends Process {
  237. switch (platform) {
  238. case LINUX:
  239. case BSD:
  240. + case SERENITY:
  241. stdin = (fds[0] == -1) ?
  242. ProcessBuilder.NullOutputStream.INSTANCE :
  243. new ProcessPipeOutputStream(fds[0]);
  244. @@ -467,6 +471,7 @@ final class ProcessImpl extends Process {
  245. case LINUX:
  246. case BSD:
  247. case AIX:
  248. + case SERENITY:
  249. // There is a risk that pid will be recycled, causing us to
  250. // kill the wrong process! So we only terminate processes
  251. // that appear to still be running. Even with this check,