1 /**
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <algorithm>
17 #include <cmath>
18 #include <thread>
19 #include <set>
20
21 #include "libpandabase/macros.h"
22 #include "libpandabase/os/exec.h"
23 #include "libpandabase/os/filesystem.h"
24 #include "libpandabase/os/kill.h"
25 #include "libpandabase/os/pipe.h"
26 #include "libpandabase/os/system_environment.h"
27 #include "libpandabase/os/time.h"
28 #include "plugins/ets/stdlib/native/escompat/Process.h"
29
30 namespace ark::ets::stdlib {
31
32 enum Signals : uint32_t { SIG_INT = 2, SIG_QUIT = 3, SIG_KILL = 9, SIG_TERM = 15 };
33
GetPipeHandler(int stdOutFdIn,int stdOutFdOut,int stdErrFdIn,int stdErrFdOut)34 static auto GetPipeHandler(int stdOutFdIn, int stdOutFdOut, int stdErrFdIn, int stdErrFdOut)
35 {
36 auto handlePipes = [stdOutFdIn, stdOutFdOut, stdErrFdIn, stdErrFdOut] {
37 std::pair<ark::os::unique_fd::UniqueFd, ark::os::unique_fd::UniqueFd> out {stdOutFdIn, stdOutFdOut};
38 std::pair<ark::os::unique_fd::UniqueFd, ark::os::unique_fd::UniqueFd> err {stdErrFdIn, stdErrFdOut};
39 out.first.Reset();
40 err.first.Reset();
41 ark::os::unique_fd::UniqueFd defaultStdOut {1};
42 ark::os::unique_fd::UniqueFd defaultStdErr {2};
43 os::Dup2(out.second, defaultStdOut);
44 os::Dup2(err.second, defaultStdErr);
45 out.second.Release();
46 err.second.Release();
47 defaultStdOut.Release();
48 defaultStdErr.Release();
49 };
50 return handlePipes;
51 }
52
53 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
54 os::memory::Mutex g_terminatedChildSetLock {};
55 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
GUARDED_BY(g_terminatedChildSetLock)56 static std::set<ets_double> g_terminatedChildSet GUARDED_BY(g_terminatedChildSetLock) {};
57
SpawnChildProcess(EtsEnv * env,ets_object child,ets_string cmd,ets_int timeout,ets_int signal)58 static void SpawnChildProcess(EtsEnv *env, ets_object child, ets_string cmd, ets_int timeout, ets_int signal)
59 {
60 auto stdOutFd = os::CreatePipe();
61 if (!stdOutFd.first.IsValid()) {
62 env->ThrowErrorNew(env->FindClass("std/core/RuntimeException"), "Failed to create a child process");
63 return;
64 }
65 auto stdErrFd = os::CreatePipe();
66 if (!stdErrFd.first.IsValid()) {
67 env->ThrowErrorNew(env->FindClass("std/core/RuntimeException"), "Failed to create a child process");
68 return;
69 }
70
71 auto pipeHandler =
72 GetPipeHandler(stdOutFd.first.Get(), stdOutFd.second.Get(), stdErrFd.first.Get(), stdErrFd.second.Get());
73
74 const char *cmdString = env->GetStringUTFChars(cmd, nullptr);
75 auto result = ark::os::exec::ExecWithCallbackNoWait(pipeHandler, "/bin/sh", "-c", cmdString);
76 env->ReleaseStringUTFChars(cmd, cmdString);
77 stdOutFd.second.Reset();
78 stdErrFd.second.Reset();
79
80 auto childKlass = env->GetObjectClass(child);
81 if (result.HasValue()) {
82 env->SetDoubleField(child, env->Getp_field(childKlass, "pid", "D"), result.Value());
83 } else {
84 stdOutFd.first.Reset();
85 stdErrFd.first.Reset();
86 env->ThrowErrorNew(env->FindClass("std/core/RuntimeException"), "Failed to create a child process");
87 return;
88 }
89
90 env->SetDoubleField(child, env->Getp_field(childKlass, "ppid", "D"), ark::os::thread::GetPid());
91 env->SetIntField(child, env->Getp_field(childKlass, "outFd", "I"), stdOutFd.first.Release());
92 env->SetIntField(child, env->Getp_field(childKlass, "errorFd", "I"), stdErrFd.first.Release());
93
94 if (timeout == 0) {
95 return;
96 }
97
98 auto pidToTerminate = env->GetDoubleField(child, env->Getp_field(childKlass, "pid", "D"));
99 auto terminator = [timeout, signal, pidToTerminate] {
100 ark::os::thread::NativeSleep(timeout);
101 os::memory::LockHolder lock {g_terminatedChildSetLock};
102 if (g_terminatedChildSet.count(pidToTerminate) == 0 && kill(pidToTerminate, signal) == 0) {
103 auto signalType = std::array {SIG_INT, SIG_QUIT, SIG_KILL, SIG_TERM};
104 if (std::find(signalType.begin(), signalType.end(), signal) != signalType.end()) {
105 g_terminatedChildSet.insert(pidToTerminate);
106 }
107 }
108 };
109
110 std::thread timeoutListener {terminator};
111 timeoutListener.detach();
112 }
113
ReadFinalizer(EtsEnv * env,ets_object child,bool isStdErr)114 static void ReadFinalizer(EtsEnv *env, ets_object child, bool isStdErr)
115 {
116 bool terminated = false;
117 auto childKlass = env->GetObjectClass(child);
118 if (env->GetDoubleField(child, env->Getp_field(childKlass, "exitCode", "D")) > -1) {
119 terminated = true;
120 } else {
121 os::memory::LockHolder lock {g_terminatedChildSetLock};
122 auto pidId = env->Getp_field(childKlass, "pid", "D");
123 terminated = g_terminatedChildSet.count(env->GetDoubleField(child, pidId)) != 0U;
124 }
125
126 if (terminated) {
127 std::string fdName = isStdErr ? "errorFd" : "outFd";
128 ets_field fdId = env->Getp_field(childKlass, fdName.c_str(), "I");
129 auto fd = env->GetIntField(child, fdId);
130 ark::os::unique_fd::UniqueFd out {fd};
131 out.Reset();
132 env->SetIntField(child, fdId, -1);
133 }
134 }
135
ReadChildProcessStdOut(EtsEnv * env,ets_object child)136 static void ReadChildProcessStdOut(EtsEnv *env, ets_object child)
137 {
138 auto childKlass = env->GetObjectClass(child);
139 ets_field outFdId = env->Getp_field(childKlass, "outFd", "I");
140 auto fd = env->GetIntField(child, outFdId);
141 if (fd == -1) {
142 return;
143 }
144
145 auto bufferObject = env->GetObjectField(child, env->Getp_field(childKlass, "outBuffer", "Lescompat/ArrayBuffer;"));
146 auto arrayBufferClass = env->GetObjectClass(bufferObject);
147 auto arrayBufferDataId = env->Getp_field(arrayBufferClass, "data", "[B");
148 ASSERT(arrayBufferDataId);
149 while (true) {
150 auto outBytesRead = env->GetIntField(child, env->Getp_field(childKlass, "outBytesRead", "I"));
151 auto outBufferLength = env->GetIntField(bufferObject, env->Getp_field(arrayBufferClass, "_byteLength", "I"));
152 if (outBytesRead >= outBufferLength) {
153 break;
154 }
155
156 constexpr int MAX_SIZE = 1024;
157 std::array<int8_t, MAX_SIZE> buffer {};
158 int bytesRead = read(fd, buffer.data(), sizeof(buffer.size()) - 1);
159 if (bytesRead == 0) {
160 ReadFinalizer(env, child, false);
161 break;
162 }
163
164 if (bytesRead + outBytesRead > outBufferLength) {
165 bytesRead = outBufferLength - outBytesRead;
166 ark::os::unique_fd::UniqueFd out {fd};
167 out.Reset();
168 env->SetIntField(child, outFdId, -1);
169 }
170 auto outBuffer = reinterpret_cast<ets_byteArray>(env->GetObjectField(bufferObject, arrayBufferDataId));
171 env->SetByteArrayRegion(outBuffer, outBytesRead, bytesRead, buffer.data());
172 env->SetIntField(child, env->Getp_field(childKlass, "outBytesRead", "I"), outBytesRead + bytesRead);
173 }
174 }
175
ReadChildProcessStdErr(EtsEnv * env,ets_object child)176 static void ReadChildProcessStdErr(EtsEnv *env, ets_object child)
177 {
178 auto childKlass = env->GetObjectClass(child);
179 ets_field errFdId = env->Getp_field(childKlass, "errorFd", "I");
180 auto fd = env->GetIntField(child, errFdId);
181 if (fd == -1) {
182 return;
183 }
184
185 auto bufferObject = env->GetObjectField(child, env->Getp_field(childKlass, "errBuffer", "Lescompat/ArrayBuffer;"));
186 auto arrayBufferClass = env->GetObjectClass(bufferObject);
187 auto arrayBufferDataId = env->Getp_field(arrayBufferClass, "data", "[B");
188 ASSERT(arrayBufferDataId);
189 while (true) {
190 auto errBytesRead = env->GetIntField(child, env->Getp_field(childKlass, "errBytesRead", "I"));
191 auto errBufferLength = env->GetIntField(bufferObject, env->Getp_field(arrayBufferClass, "_byteLength", "I"));
192 if (errBytesRead >= errBufferLength) {
193 break;
194 }
195
196 constexpr int MAX_SIZE = 1024;
197 std::array<int8_t, MAX_SIZE> buffer {};
198 int bytesRead = read(fd, buffer.data(), sizeof(buffer.size()) - 1);
199 if (bytesRead == 0) {
200 ReadFinalizer(env, child, true);
201 break;
202 }
203
204 if (bytesRead + errBytesRead > errBufferLength) {
205 bytesRead = errBufferLength - errBytesRead;
206
207 ark::os::unique_fd::UniqueFd out {fd};
208 out.Reset();
209 env->SetIntField(child, errFdId, -1);
210 }
211 auto errBuffer = reinterpret_cast<ets_byteArray>(env->GetObjectField(bufferObject, arrayBufferDataId));
212
213 env->SetByteArrayRegion(errBuffer, errBytesRead, bytesRead, buffer.data());
214 env->SetIntField(child, env->Getp_field(childKlass, "errBytesRead", "I"), errBytesRead + bytesRead);
215 }
216 }
217
WaitChildProcess(EtsEnv * env,ets_object child)218 static void WaitChildProcess(EtsEnv *env, ets_object child)
219 {
220 auto childKlass = env->GetObjectClass(child);
221 auto exitCodeId = env->Getp_field(childKlass, "exitCode", "D");
222 auto pidId = env->Getp_field(childKlass, "pid", "D");
223 auto pid = env->GetDoubleField(child, pidId);
224
225 if (env->GetDoubleField(child, exitCodeId) < 0) {
226 auto result = ark::os::exec::Wait(pid, false);
227 if (result.HasValue()) {
228 env->SetDoubleField(child, exitCodeId, result.Value());
229 } else {
230 auto exceptionClass = env->FindClass("std/core/RuntimeException");
231 env->ThrowErrorNew(exceptionClass, "Wait failed");
232 return;
233 }
234 }
235
236 ReadChildProcessStdOut(env, child);
237 ReadChildProcessStdErr(env, child);
238
239 {
240 os::memory::LockHolder lock {g_terminatedChildSetLock};
241 g_terminatedChildSet.erase(pid);
242 }
243 }
244
KillChildProcess(EtsEnv * env,ets_object child,ets_int signal)245 static void KillChildProcess(EtsEnv *env, ets_object child, ets_int signal)
246 {
247 auto signalType = std::array {SIG_INT, SIG_QUIT, SIG_KILL, SIG_TERM};
248
249 auto intSignal = static_cast<int>(signal);
250 auto childKlass = env->GetObjectClass(child);
251 auto pidId = env->Getp_field(childKlass, "pid", "D");
252 auto pid = env->GetDoubleField(child, pidId);
253 if (ark::os::kill_process::Kill(pid, intSignal) == 0) {
254 if (std::find(signalType.begin(), signalType.end(), intSignal) != signalType.end()) {
255 os::memory::LockHolder lock {g_terminatedChildSetLock};
256 g_terminatedChildSet.insert(pid);
257 }
258 auto killedId = env->Getp_field(childKlass, "killed", "Z");
259 env->SetBooleanField(child, killedId, 1U);
260 return;
261 }
262
263 auto exceptionClass = env->FindClass("std/core/RuntimeException");
264 env->ThrowErrorNew(exceptionClass, "Kill failed");
265 }
266
CloseChildProcess(EtsEnv * env,ets_object child)267 static void CloseChildProcess(EtsEnv *env, ets_object child)
268 {
269 auto childKlass = env->GetObjectClass(child);
270 auto exitCodeId = env->Getp_field(childKlass, "exitCode", "D");
271 auto pidId = env->Getp_field(childKlass, "pid", "D");
272 auto pid = env->GetDoubleField(child, pidId);
273
274 if (env->GetDoubleField(child, exitCodeId) > -1) {
275 return;
276 }
277
278 {
279 os::memory::LockHolder lock {g_terminatedChildSetLock};
280
281 if (g_terminatedChildSet.count(pid) != 0U) {
282 return;
283 }
284 }
285
286 auto status = ark::os::kill_process::Close(pid);
287 if (LIKELY(status != -1)) {
288 env->SetDoubleField(child, exitCodeId, status);
289 return;
290 }
291
292 auto exceptionClass = env->FindClass("std/core/RuntimeException");
293 env->ThrowErrorNew(exceptionClass, "Close failed");
294 }
295
PManagerGetUidForName(EtsEnv * env,ets_object process,ets_string name)296 static ets_double PManagerGetUidForName(EtsEnv *env, [[maybe_unused]] ets_object process, ets_string name)
297 {
298 const char *str = env->GetStringUTFChars(name, nullptr);
299 auto result = ark::os::system_environment::GetUidForName(str);
300 env->ReleaseStringUTFChars(name, str);
301
302 return result;
303 }
304
PManagerGetThreadPriority(EtsEnv * env,ets_object process,ets_double tid)305 static ets_double PManagerGetThreadPriority([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process,
306 ets_double tid)
307 {
308 return ark::os::thread::GetPriority(tid);
309 }
310
PManagerGetSystemConfig(EtsEnv * env,ets_object process,ets_double name)311 static ets_double PManagerGetSystemConfig([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process,
312 ets_double name)
313 {
314 return ark::os::system_environment::GetSystemConfig(name);
315 }
316
PManagerGetEnvironmentVar(EtsEnv * env,ets_object process,ets_string name)317 static ets_string PManagerGetEnvironmentVar(EtsEnv *env, [[maybe_unused]] ets_object process, ets_string name)
318 {
319 const char *str = env->GetStringUTFChars(name, nullptr);
320 auto result = env->NewStringUTF(ark::os::system_environment::GetEnvironmentVar(std::string(str).c_str()).c_str());
321 env->ReleaseStringUTFChars(name, str);
322
323 return result;
324 }
325
PManagerExit(EtsEnv * env,ets_object process,ets_double code)326 static void PManagerExit([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process, ets_double code)
327 {
328 std::exit(code);
329 }
330
PManagerKill(EtsEnv * env,ets_object process,ets_double signal,ets_double pid)331 static ets_boolean PManagerKill(EtsEnv *env, [[maybe_unused]] ets_object process, ets_double signal, ets_double pid)
332 {
333 int integerPid = static_cast<int>(pid);
334 auto ownPid = ark::os::thread::GetPid();
335 if (integerPid == 0 || integerPid == -1 || integerPid == ownPid || integerPid == -ownPid) {
336 auto exceptionClass = env->FindClass("std/core/IllegalArgumentException");
337 env->ThrowErrorNew(exceptionClass, "Invalid pid argument");
338
339 return 0U;
340 }
341
342 constexpr int MIN_SIGNAL_VALUE = 1;
343 constexpr int MAX_SINAL_VALUE = 64;
344
345 if (std::trunc(signal) != signal || signal < MIN_SIGNAL_VALUE || signal > MAX_SINAL_VALUE) {
346 auto exceptionClass = env->FindClass("std/core/IllegalArgumentException");
347 env->ThrowErrorNew(exceptionClass, "Invalid signal argument");
348
349 return 0U;
350 }
351
352 return static_cast<ets_boolean>(ark::os::kill_process::Kill(integerPid, signal) == 0);
353 }
354
GetTid(EtsEnv * env,ets_object process)355 static ets_double GetTid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
356 {
357 return ark::os::thread::GetCurrentThreadId();
358 }
359
GetPid(EtsEnv * env,ets_object process)360 static ets_double GetPid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
361 {
362 return ark::os::thread::GetPid();
363 }
364
GetPPid(EtsEnv * env,ets_object process)365 static ets_double GetPPid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
366 {
367 return ark::os::thread::GetPPid();
368 }
369
GetUid(EtsEnv * env,ets_object process)370 static ets_double GetUid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
371 {
372 return ark::os::thread::GetUid();
373 }
374
GetEuid(EtsEnv * env,ets_object process)375 static ets_double GetEuid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
376 {
377 return ark::os::thread::GetEuid();
378 }
379
GetGid(EtsEnv * env,ets_object process)380 static ets_double GetGid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
381 {
382 return ark::os::thread::GetGid();
383 }
384
GetEgid(EtsEnv * env,ets_object process)385 static ets_double GetEgid([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
386 {
387 return ark::os::thread::GetEgid();
388 }
389
GetGroupIDs(EtsEnv * env,ets_object process)390 static ets_doubleArray GetGroupIDs(EtsEnv *env, [[maybe_unused]] ets_object process)
391 {
392 auto groups = ark::os::thread::GetGroups();
393 auto groupIds = std::vector<ets_double>(groups.begin(), groups.end());
394
395 auto result = env->NewDoubleArray(groups.size());
396
397 if (groups.empty()) {
398 auto exceptionClass = env->FindClass("std/core/RuntimeException");
399 env->ThrowErrorNew(exceptionClass, "Failed to get process groups");
400 return result;
401 }
402
403 env->SetDoubleArrayRegion(result, 0, groups.size(), groupIds.data());
404
405 return result;
406 }
407
Is64BitProcess(EtsEnv * env,ets_object process)408 static ets_boolean Is64BitProcess([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
409 {
410 constexpr int SIZE_OF_64_BIT_PTR = 8;
411 return static_cast<ets_boolean>(sizeof(char *) == SIZE_OF_64_BIT_PTR);
412 }
413
GetProcessStartRealTime(EtsEnv * env,ets_object process)414 static ets_double GetProcessStartRealTime([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
415 {
416 return ark::os::time::GetStartRealTime<std::chrono::milliseconds>();
417 }
418
GetProcessPastCpuTime(EtsEnv * env,ets_object process)419 static ets_double GetProcessPastCpuTime([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
420 {
421 constexpr int PROCESS_CLOCK = 2;
422 return ark::os::time::GetClockTime<std::chrono::milliseconds>(PROCESS_CLOCK);
423 }
424
AbortProcess(EtsEnv * env,ets_object process)425 static void AbortProcess([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
426 {
427 std::abort();
428 }
429
GetCurrentWorkingDirectory(EtsEnv * env,ets_object process)430 static ets_string GetCurrentWorkingDirectory(EtsEnv *env, [[maybe_unused]] ets_object process)
431 {
432 return env->NewStringUTF(ark::os::GetCurrentWorkingDirectory().c_str());
433 }
434
ChangeCurrentWorkingDirectory(EtsEnv * env,ets_object process,ets_string path)435 static void ChangeCurrentWorkingDirectory(EtsEnv *env, [[maybe_unused]] ets_object process, ets_string path)
436 {
437 const char *str = env->GetStringUTFChars(path, nullptr);
438 os::ChangeCurrentWorkingDirectory(str);
439 env->ReleaseStringUTFChars(path, str);
440 }
441
GetSystemUptime(EtsEnv * env,ets_object process)442 static ets_double GetSystemUptime([[maybe_unused]] EtsEnv *env, [[maybe_unused]] ets_object process)
443 {
444 constexpr int BOOTTIME_CLOCK = 7;
445 return ark::os::time::GetClockTime<std::chrono::milliseconds>(BOOTTIME_CLOCK);
446 }
447
RegisterProcessNativeMethods(EtsEnv * env)448 void RegisterProcessNativeMethods(EtsEnv *env)
449 {
450 const auto childProcessImpls =
451 std::array {EtsNativeMethod {"readOutput", nullptr, reinterpret_cast<void *>(ReadChildProcessStdOut)},
452 EtsNativeMethod {"readErrorOutput", nullptr, reinterpret_cast<void *>(ReadChildProcessStdErr)},
453 EtsNativeMethod {"close", nullptr, reinterpret_cast<void *>(CloseChildProcess)},
454 EtsNativeMethod {"killImpl", nullptr, reinterpret_cast<void *>(KillChildProcess)},
455 EtsNativeMethod {"spawn", nullptr, reinterpret_cast<void *>(SpawnChildProcess)},
456 EtsNativeMethod {"waitImpl", nullptr, reinterpret_cast<void *>(WaitChildProcess)}};
457
458 const auto processManagerImpls = std::array {
459 EtsNativeMethod {"getUidForName", nullptr, reinterpret_cast<void *>(PManagerGetUidForName)},
460 EtsNativeMethod {"getThreadPriority", nullptr, reinterpret_cast<void *>(PManagerGetThreadPriority)},
461 EtsNativeMethod {"getSystemConfig", nullptr, reinterpret_cast<void *>(PManagerGetSystemConfig)},
462 EtsNativeMethod {"getEnvironmentVar", nullptr, reinterpret_cast<void *>(PManagerGetEnvironmentVar)},
463 EtsNativeMethod {"exit", nullptr, reinterpret_cast<void *>(PManagerExit)},
464 EtsNativeMethod {"kill", nullptr, reinterpret_cast<void *>(PManagerKill)},
465 };
466
467 const auto processImpls =
468 std::array {EtsNativeMethod {"getTidImpl", nullptr, reinterpret_cast<void *>(GetTid)},
469 EtsNativeMethod {"getPidImpl", nullptr, reinterpret_cast<void *>(GetPid)},
470 EtsNativeMethod {"getPpidImpl", nullptr, reinterpret_cast<void *>(GetPPid)},
471 EtsNativeMethod {"getUidImpl", nullptr, reinterpret_cast<void *>(GetUid)},
472 EtsNativeMethod {"getEuidImpl", nullptr, reinterpret_cast<void *>(GetEuid)},
473 EtsNativeMethod {"getGidImpl", nullptr, reinterpret_cast<void *>(GetGid)},
474 EtsNativeMethod {"getEgidImpl", nullptr, reinterpret_cast<void *>(GetEgid)},
475 EtsNativeMethod {"getGroupsImpl", nullptr, reinterpret_cast<void *>(GetGroupIDs)},
476 EtsNativeMethod {"is64Bit", nullptr, reinterpret_cast<void *>(Is64BitProcess)},
477 EtsNativeMethod {"getStartRealtime", nullptr, reinterpret_cast<void *>(GetProcessStartRealTime)},
478 EtsNativeMethod {"getPastCpuTime", nullptr, reinterpret_cast<void *>(GetProcessPastCpuTime)},
479 EtsNativeMethod {"abort", nullptr, reinterpret_cast<void *>(AbortProcess)},
480 EtsNativeMethod {"cwd", nullptr, reinterpret_cast<void *>(GetCurrentWorkingDirectory)},
481 EtsNativeMethod {"chdir", nullptr, reinterpret_cast<void *>(ChangeCurrentWorkingDirectory)},
482 EtsNativeMethod {"uptime", nullptr, reinterpret_cast<void *>(GetSystemUptime)}};
483
484 auto childProcessKlass = env->FindClass("escompat/ChildProcess");
485 env->RegisterNatives(childProcessKlass, childProcessImpls.data(), childProcessImpls.size());
486
487 auto processManagerKlass = env->FindClass("escompat/ProcessManager");
488 env->RegisterNatives(processManagerKlass, processManagerImpls.data(), processManagerImpls.size());
489
490 auto processKlass = env->FindClass("escompat/process");
491 env->RegisterNatives(processKlass, processImpls.data(), processImpls.size());
492 }
493
494 } // namespace ark::ets::stdlib
495