• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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