• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2025 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 #include "plugins/ets/stdlib/native/core/stdlib_ani_helpers.h"
30 
31 namespace ark::ets::stdlib {
32 
33 enum Signals : uint32_t { SIG_INT = 2, SIG_QUIT = 3, SIG_KILL = 9, SIG_TERM = 15 };
34 
35 #ifdef PANDA_TARGET_OHOS
36 // constexpr variables used by isAppUid & isIsolatedProcess, refered to arkts 1.0
37 // See: https://gitee.com/openharmony/js_sys_module/blob/master/process/js_process.cpp#L38
38 constexpr int PER_USER_RANGE = 100000;
39 // See: https://gitee.com/openharmony/js_sys_module/blob/master/process/js_process.h#L242
40 constexpr std::pair<int, int> APP_UID_RANGE = {10000, 19999};
41 // Only isolateuid numbers between 99000 and 99999.
42 // See: https://gitee.com/openharmony/js_sys_module/blob/master/process/js_process.cpp#L290
43 constexpr std::pair<int, int> ISOLATE_UID_RANGE = {99000, 99999};
44 // Only appuid numbers between 9000 and 98999.
45 // See: https://gitee.com/openharmony/js_sys_module/blob/master/process/js_process.cpp#L291
46 constexpr std::pair<int, int> GENERAL_UID_RANGE = {9000, 98999};
47 #endif
48 
GetPipeHandler(int stdOutFdIn,int stdOutFdOut,int stdErrFdIn,int stdErrFdOut)49 static auto GetPipeHandler(int stdOutFdIn, int stdOutFdOut, int stdErrFdIn, int stdErrFdOut)
50 {
51     auto handlePipes = [stdOutFdIn, stdOutFdOut, stdErrFdIn, stdErrFdOut] {
52         std::pair<ark::os::unique_fd::UniqueFd, ark::os::unique_fd::UniqueFd> out {stdOutFdIn, stdOutFdOut};
53         std::pair<ark::os::unique_fd::UniqueFd, ark::os::unique_fd::UniqueFd> err {stdErrFdIn, stdErrFdOut};
54         out.first.Reset();
55         err.first.Reset();
56         ark::os::unique_fd::UniqueFd defaultStdOut {1};
57         ark::os::unique_fd::UniqueFd defaultStdErr {2};
58         os::Dup2(out.second, defaultStdOut);
59         os::Dup2(err.second, defaultStdErr);
60         out.second.Release();
61         err.second.Release();
62         defaultStdOut.Release();
63         defaultStdErr.Release();
64     };
65     return handlePipes;
66 }
67 
68 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
69 os::memory::Mutex g_terminatedChildSetLock {};
70 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
GUARDED_BY(g_terminatedChildSetLock)71 static std::set<ani_double> g_terminatedChildSet GUARDED_BY(g_terminatedChildSetLock) {};
72 
SpawnChildProcess(ani_env * env,ani_object child,ani_string cmd,ani_int timeout,ani_int signal)73 static void SpawnChildProcess(ani_env *env, ani_object child, ani_string cmd, ani_int timeout, ani_int signal)
74 {
75     auto stdOutFd = os::CreatePipe();
76     if (!stdOutFd.first.IsValid()) {
77         ThrowNewError(env, "Lstd/core/RuntimeException;", "Failed to create a child process", "Lstd/core/String;:V");
78         return;
79     }
80     auto stdErrFd = os::CreatePipe();
81     if (!stdErrFd.first.IsValid()) {
82         ThrowNewError(env, "Lstd/core/RuntimeException;", "Failed to create a child process", "Lstd/core/String;:V");
83         return;
84     }
85 
86     auto pipeHandler =
87         GetPipeHandler(stdOutFd.first.Get(), stdOutFd.second.Get(), stdErrFd.first.Get(), stdErrFd.second.Get());
88 
89     auto cmdString = ConvertFromAniString(env, cmd);
90     auto result = ark::os::exec::ExecWithCallbackNoWait(pipeHandler, "/bin/sh", "-c", cmdString.c_str());
91 
92     stdOutFd.second.Reset();
93     stdErrFd.second.Reset();
94 
95     if (result.HasValue()) {
96         ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Double(child, "pid", result.Value()));
97     } else {
98         stdOutFd.first.Reset();
99         stdErrFd.first.Reset();
100         ThrowNewError(env, "Lstd/core/RuntimeException;", "Failed to create a child process", "Lstd/core/String;:V");
101         return;
102     }
103 
104     ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Double(child, "ppid", ark::os::thread::GetPid()));
105     ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Int(child, "outFd", stdOutFd.first.Release()));
106     ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Int(child, "errorFd", stdErrFd.first.Release()));
107 
108     if (timeout == 0) {
109         return;
110     }
111 
112     ani_double pidToTerminate;
113     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Double(child, "pid", &pidToTerminate));
114 
115     auto terminator = [timeout, signal, pidToTerminate] {
116         ark::os::thread::NativeSleep(timeout);
117         os::memory::LockHolder lock {g_terminatedChildSetLock};
118         if (g_terminatedChildSet.count(pidToTerminate) == 0 && kill(pidToTerminate, signal) == 0) {
119             auto signalType = std::array {SIG_INT, SIG_QUIT, SIG_KILL, SIG_TERM};
120             if (std::find(signalType.begin(), signalType.end(), signal) != signalType.end()) {
121                 g_terminatedChildSet.insert(pidToTerminate);
122             }
123         }
124     };
125 
126     std::thread timeoutListener {terminator};
127     timeoutListener.detach();
128 }
129 
ReadFinalizer(ani_env * env,ani_object child,bool isStdErr)130 static void ReadFinalizer(ani_env *env, ani_object child, bool isStdErr)
131 {
132     bool terminated = false;
133 
134     ani_double exitCode;
135     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Double(child, "exitCode", &exitCode));
136 
137     if (exitCode > -1) {
138         terminated = true;
139     } else {
140         os::memory::LockHolder lock {g_terminatedChildSetLock};
141         ani_double pid;
142         ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Double(child, "pid", &pid));
143         terminated = g_terminatedChildSet.count(pid) != 0U;
144     }
145 
146     if (terminated) {
147         std::string fdName = isStdErr ? "errorFd" : "outFd";
148         ani_int fd;
149         ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Int(child, fdName.data(), &fd));
150         ark::os::unique_fd::UniqueFd out {fd};
151         out.Reset();
152         ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Int(child, fdName.data(), -1));
153     }
154 }
155 
ReadChildProcessStdOut(ani_env * env,ani_object child)156 static void ReadChildProcessStdOut(ani_env *env, ani_object child)
157 {
158     ani_int fd;
159     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Int(child, "outFd", &fd));
160     if (fd == -1) {
161         return;
162     }
163 
164     ani_ref bufferObject;
165     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Ref(child, "outBuffer", &bufferObject));
166 
167     ani_ref outBuffer;
168     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Ref(reinterpret_cast<ani_object>(bufferObject), "data", &outBuffer));
169 
170     while (true) {
171         ani_int outBytesRead;
172         ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Int(child, "outBytesRead", &outBytesRead));
173 
174         ani_int outBufferLength;
175         ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Int(reinterpret_cast<ani_object>(bufferObject), "_byteLength",
176                                                           &outBufferLength));
177 
178         if (outBytesRead >= outBufferLength) {
179             break;
180         }
181 
182         constexpr int MAX_SIZE = 1024;
183         std::array<int8_t, MAX_SIZE> buffer {};
184         int bytesRead = read(fd, buffer.data(), sizeof(buffer.size()) - 1);
185         if (bytesRead == 0) {
186             ReadFinalizer(env, child, false);
187             break;
188         }
189 
190         if (bytesRead + outBytesRead > outBufferLength) {
191             bytesRead = outBufferLength - outBytesRead;
192             ark::os::unique_fd::UniqueFd out {fd};
193             out.Reset();
194             ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Int(child, "outFd", -1));
195         }
196 
197         ANI_FATAL_IF_ERROR(env->Array_SetRegion_Byte(reinterpret_cast<ani_array_byte>(outBuffer), outBytesRead,
198                                                      bytesRead, buffer.data()));
199         ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Int(child, "outBytesRead", outBytesRead + bytesRead));
200     }
201 }
202 
ReadChildProcessStdErr(ani_env * env,ani_object child)203 static void ReadChildProcessStdErr(ani_env *env, ani_object child)
204 {
205     ani_int fd;
206     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Int(child, "errorFd", &fd));
207     if (fd == -1) {
208         return;
209     }
210 
211     ani_ref bufferObject;
212     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Ref(child, "errBuffer", &bufferObject));
213 
214     ani_ref errBuffer;
215     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Ref(reinterpret_cast<ani_object>(bufferObject), "data", &errBuffer));
216 
217     ani_type childClassType;
218     ANI_FATAL_IF_ERROR(env->Object_GetType(child, &childClassType));
219 
220     ani_field errBytesReadId;
221     ANI_FATAL_IF_ERROR(env->Class_FindField(static_cast<ani_class>(childClassType), "errBytesRead", &errBytesReadId));
222 
223     while (true) {
224         ani_int errBytesRead;
225         ANI_FATAL_IF_ERROR(env->Object_GetField_Int(child, errBytesReadId, &errBytesRead));
226 
227         ani_int errBufferLength;
228         ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Int(reinterpret_cast<ani_object>(bufferObject), "_byteLength",
229                                                           &errBufferLength));
230 
231         if (errBytesRead >= errBufferLength) {
232             break;
233         }
234 
235         constexpr int MAX_SIZE = 1024;
236         std::array<int8_t, MAX_SIZE> buffer {};
237         int bytesRead = read(fd, buffer.data(), sizeof(buffer.size()) - 1);
238         if (bytesRead == 0) {
239             ReadFinalizer(env, child, true);
240             break;
241         }
242 
243         if (bytesRead + errBytesRead > errBufferLength) {
244             bytesRead = errBufferLength - errBytesRead;
245 
246             ark::os::unique_fd::UniqueFd out {fd};
247             out.Reset();
248             ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Int(child, "errorFd", -1));
249         }
250 
251         ANI_FATAL_IF_ERROR(env->Array_SetRegion_Byte(reinterpret_cast<ani_array_byte>(errBuffer), errBytesRead,
252                                                      bytesRead, buffer.data()));
253         ANI_FATAL_IF_ERROR(env->Object_SetField_Int(child, errBytesReadId, errBytesRead + bytesRead));
254     }
255 }
256 
WaitChildProcess(ani_env * env,ani_object child)257 static void WaitChildProcess(ani_env *env, ani_object child)
258 {
259     ani_double pid;
260     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Double(child, "pid", &pid));
261 
262     ani_type childClassType;
263     ANI_FATAL_IF_ERROR(env->Object_GetType(child, &childClassType));
264 
265     ani_field exitCodeId;
266     ANI_FATAL_IF_ERROR(env->Class_FindField(static_cast<ani_class>(childClassType), "exitCode", &exitCodeId));
267 
268     ani_double exitCode;
269     ANI_FATAL_IF_ERROR(env->Object_GetField_Double(child, exitCodeId, &exitCode));
270 
271     if (exitCode < 0) {
272         auto result = ark::os::exec::Wait(pid, false);
273         if (result.HasValue()) {
274             ANI_FATAL_IF_ERROR(env->Object_SetField_Double(child, exitCodeId, result.Value()));
275         } else {
276             ThrowNewError(env, "Lstd/core/RuntimeException;", "Wait failed", "Lstd/core/String;:V");
277             return;
278         }
279     }
280 
281     ReadChildProcessStdOut(env, child);
282     ReadChildProcessStdErr(env, child);
283 
284     {
285         os::memory::LockHolder lock {g_terminatedChildSetLock};
286         g_terminatedChildSet.erase(pid);
287     }
288 }
289 
KillChildProcess(ani_env * env,ani_object child,ani_int signal)290 static void KillChildProcess(ani_env *env, ani_object child, ani_int signal)
291 {
292     auto signalType = std::array {SIG_INT, SIG_QUIT, SIG_KILL, SIG_TERM};
293 
294     auto intSignal = static_cast<int>(signal);
295 
296     ani_double pid;
297     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Double(child, "pid", &pid));
298 
299     if (ark::os::kill_process::Kill(pid, intSignal) == 0) {
300         if (std::find(signalType.begin(), signalType.end(), intSignal) != signalType.end()) {
301             os::memory::LockHolder lock {g_terminatedChildSetLock};
302             g_terminatedChildSet.insert(pid);
303         }
304         ANI_FATAL_IF_ERROR(env->Object_SetFieldByName_Boolean(child, "killed", 1U));
305         return;
306     }
307 
308     ThrowNewError(env, "Lstd/core/RuntimeException;", "Kill failed", "Lstd/core/String;:V");
309 }
310 
CloseChildProcess(ani_env * env,ani_object child)311 static void CloseChildProcess(ani_env *env, ani_object child)
312 {
313     ani_type childClassType;
314     ANI_FATAL_IF_ERROR(env->Object_GetType(child, &childClassType));
315 
316     ani_field exitCodeId;
317     ANI_FATAL_IF_ERROR(env->Class_FindField(static_cast<ani_class>(childClassType), "exitCode", &exitCodeId));
318 
319     ani_double pid;
320     ANI_FATAL_IF_ERROR(env->Object_GetFieldByName_Double(child, "pid", &pid));
321 
322     ani_double exitCode;
323     ANI_FATAL_IF_ERROR(env->Object_GetField_Double(child, exitCodeId, &exitCode));
324 
325     if (exitCode > -1) {
326         return;
327     }
328 
329     {
330         os::memory::LockHolder lock {g_terminatedChildSetLock};
331 
332         if (g_terminatedChildSet.count(pid) != 0U) {
333             return;
334         }
335     }
336 
337     auto status = ark::os::kill_process::Close(pid);
338     if (LIKELY(status != -1)) {
339         ANI_FATAL_IF_ERROR(env->Object_SetField_Double(child, exitCodeId, status));
340         return;
341     }
342 
343     ThrowNewError(env, "Lstd/core/RuntimeException;", "Close failed", "Lstd/core/String;:V");
344 }
345 
PManagerIsAppUid(ani_env * env,ani_object process,ani_double uid)346 static ani_boolean PManagerIsAppUid([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object process,
347                                     [[maybe_unused]] ani_double uid)
348 {
349 #ifdef PANDA_TARGET_OHOS
350     // refer to https://gitee.com/openharmony/js_sys_module/blob/master/process/js_process.cpp#L300
351     int number = static_cast<int>(uid);
352     if (number > 0) {
353         const auto appId = number % PER_USER_RANGE;
354         return (appId >= APP_UID_RANGE.first && appId <= APP_UID_RANGE.second) ? ANI_TRUE : ANI_FALSE;
355     }
356 
357     return ANI_FALSE;
358 #else
359     ThrowNewError(env, "Lstd/core/RuntimeException;", "not implemented for Non-OHOS target", "Lstd/core/String;:V");
360     return ANI_FALSE;
361 #endif
362 }
363 
PManagerGetUidForName(ani_env * env,ani_object process,ani_string name)364 static ani_double PManagerGetUidForName(ani_env *env, [[maybe_unused]] ani_object process, ani_string name)
365 {
366     auto str = ConvertFromAniString(env, name);
367     auto result = ark::os::system_environment::GetUidForName(str);
368     return result;
369 }
370 
PManagerGetThreadPriority(ani_env * env,ani_object process,ani_double tid)371 static ani_double PManagerGetThreadPriority([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object process,
372                                             ani_double tid)
373 {
374     return ark::os::thread::GetPriority(tid);
375 }
376 
PManagerGetSystemConfig(ani_env * env,ani_object process,ani_double name)377 static ani_double PManagerGetSystemConfig([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object process,
378                                           ani_double name)
379 {
380     return ark::os::system_environment::GetSystemConfig(name);
381 }
382 
PManagerGetEnvironmentVar(ani_env * env,ani_object process,ani_string name)383 static ani_string PManagerGetEnvironmentVar(ani_env *env, [[maybe_unused]] ani_object process, ani_string name)
384 {
385     auto str = ConvertFromAniString(env, name);
386     auto envVar = ark::os::system_environment::GetEnvironmentVar(str.c_str());
387     return CreateUtf8String(env, envVar.data(), envVar.size());
388 }
389 
PManagerExit(ani_env * env,ani_object process,ani_double code)390 static void PManagerExit([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object process, ani_double code)
391 {
392     std::exit(code);
393 }
394 
PManagerKill(ani_env * env,ani_object process,ani_double signal,ani_double pid)395 static ani_boolean PManagerKill(ani_env *env, [[maybe_unused]] ani_object process, ani_double signal, ani_double pid)
396 {
397     int integerPid = static_cast<int>(pid);
398     auto ownPid = ark::os::thread::GetPid();
399     if (integerPid == 0 || integerPid == -1 || integerPid == ownPid || integerPid == -ownPid) {
400         ThrowNewError(env, "Lstd/core/IllegalArgumentException;", "Invalid pid argument", "Lstd/core/String;:V");
401         return 0U;
402     }
403 
404     constexpr int MIN_SIGNAL_VALUE = 1;
405     constexpr int MAX_SINAL_VALUE = 64;
406 
407     if (std::trunc(signal) != signal || signal < MIN_SIGNAL_VALUE || signal > MAX_SINAL_VALUE) {
408         ThrowNewError(env, "Lstd/core/IllegalArgumentException;", "Invalid signal argument", "Lstd/core/String;:V");
409         return 0U;
410     }
411 
412     return static_cast<ani_boolean>(ark::os::kill_process::Kill(integerPid, signal) == 0);
413 }
414 
GetTid(ani_env * env)415 static ani_double GetTid([[maybe_unused]] ani_env *env)
416 {
417     return ark::os::thread::GetCurrentThreadId();
418 }
419 
IsIsolatedProcImpl(ani_env * env)420 static ani_boolean IsIsolatedProcImpl([[maybe_unused]] ani_env *env)
421 {
422 #ifdef PANDA_TARGET_OHOS
423     // refer to https://gitee.com/openharmony/js_sys_module/blob/master/process/js_process.cpp#L284
424     auto uid = ark::os::thread::GetUid();
425     uid %= PER_USER_RANGE;
426     return ((uid >= ISOLATE_UID_RANGE.first && uid <= ISOLATE_UID_RANGE.second) ||
427             (uid >= GENERAL_UID_RANGE.first && uid <= GENERAL_UID_RANGE.second))
428                ? ANI_TRUE
429                : ANI_FALSE;
430 #else
431     ThrowNewError(env, "Lstd/core/RuntimeException;", "not implemented for Non-OHOS target", "Lstd/core/String;:V");
432     return ANI_FALSE;
433 #endif
434 }
435 
GetPid(ani_env * env)436 static ani_double GetPid([[maybe_unused]] ani_env *env)
437 {
438     return ark::os::thread::GetPid();
439 }
440 
GetPPid(ani_env * env)441 static ani_double GetPPid([[maybe_unused]] ani_env *env)
442 {
443     return ark::os::thread::GetPPid();
444 }
445 
GetUid(ani_env * env)446 static ani_double GetUid([[maybe_unused]] ani_env *env)
447 {
448     return ark::os::thread::GetUid();
449 }
450 
GetEuid(ani_env * env)451 static ani_double GetEuid([[maybe_unused]] ani_env *env)
452 {
453     return ark::os::thread::GetEuid();
454 }
455 
GetGid(ani_env * env)456 static ani_double GetGid([[maybe_unused]] ani_env *env)
457 {
458     return ark::os::thread::GetGid();
459 }
460 
GetEgid(ani_env * env)461 static ani_double GetEgid([[maybe_unused]] ani_env *env)
462 {
463     return ark::os::thread::GetEgid();
464 }
465 
GetGroupIDs(ani_env * env)466 static ani_array_double GetGroupIDs(ani_env *env)
467 {
468     auto groups = ark::os::thread::GetGroups();
469     auto groupIds = std::vector<ani_double>(groups.begin(), groups.end());
470 
471     ani_array_double result;
472     ANI_FATAL_IF_ERROR(env->Array_New_Double(groups.size(), &result));
473 
474     if (groups.empty()) {
475         ThrowNewError(env, "Lstd/core/RuntimeException;", "Failed to get process groups", "Lstd/core/String;:V");
476         return result;
477     }
478 
479     ANI_FATAL_IF_ERROR(env->Array_SetRegion_Double(result, 0, groups.size(), groupIds.data()));
480 
481     return result;
482 }
483 
Is64BitProcess(ani_env * env)484 static ani_boolean Is64BitProcess([[maybe_unused]] ani_env *env)
485 {
486     constexpr int SIZE_OF_64_BIT_PTR = 8;
487     return static_cast<ani_boolean>(sizeof(char *) == SIZE_OF_64_BIT_PTR);
488 }
489 
GetProcessStartRealTime(ani_env * env)490 static ani_double GetProcessStartRealTime([[maybe_unused]] ani_env *env)
491 {
492     return ark::os::time::GetStartRealTime<std::chrono::milliseconds>();
493 }
494 
GetProcessPastCpuTime(ani_env * env)495 static ani_double GetProcessPastCpuTime([[maybe_unused]] ani_env *env)
496 {
497     constexpr int PROCESS_CLOCK = 2;
498     return ark::os::time::GetClockTime<std::chrono::milliseconds>(PROCESS_CLOCK);
499 }
500 
AbortProcess(ani_env * env)501 static void AbortProcess([[maybe_unused]] ani_env *env)
502 {
503     std::abort();
504 }
505 
GetCurrentWorkingDirectory(ani_env * env)506 static ani_string GetCurrentWorkingDirectory(ani_env *env)
507 {
508     auto workDir = ark::os::GetCurrentWorkingDirectory();
509     return CreateUtf8String(env, workDir.data(), workDir.size());
510 }
511 
ChangeCurrentWorkingDirectory(ani_env * env,ani_string path)512 static void ChangeCurrentWorkingDirectory(ani_env *env, ani_string path)
513 {
514     auto str = ConvertFromAniString(env, path);
515     os::ChangeCurrentWorkingDirectory(str);
516 }
517 
GetSystemUptime(ani_env * env)518 static ani_double GetSystemUptime([[maybe_unused]] ani_env *env)
519 {
520     constexpr int BOOTTIME_CLOCK = 7;
521     return ark::os::time::GetClockTime<std::chrono::milliseconds>(BOOTTIME_CLOCK);
522 }
523 
RegisterProcessNativeMethods(ani_env * env)524 void RegisterProcessNativeMethods(ani_env *env)
525 {
526     const auto childProcessImpls =
527         std::array {ani_native_function {"readOutput", ":V", reinterpret_cast<void *>(ReadChildProcessStdOut)},
528                     ani_native_function {"readErrorOutput", ":V", reinterpret_cast<void *>(ReadChildProcessStdErr)},
529                     ani_native_function {"close", ":V", reinterpret_cast<void *>(CloseChildProcess)},
530                     ani_native_function {"killImpl", "I:V", reinterpret_cast<void *>(KillChildProcess)},
531                     ani_native_function {"spawn", "Lstd/core/String;II:V", reinterpret_cast<void *>(SpawnChildProcess)},
532                     ani_native_function {"waitImpl", ":D", reinterpret_cast<void *>(WaitChildProcess)}};
533 
534     const auto processManagerImpls = std::array {
535         ani_native_function {"isAppUid", "D:Z", reinterpret_cast<void *>(PManagerIsAppUid)},
536         ani_native_function {"getUidForName", "Lstd/core/String;:D", reinterpret_cast<void *>(PManagerGetUidForName)},
537         ani_native_function {"getThreadPriority", "D:D", reinterpret_cast<void *>(PManagerGetThreadPriority)},
538         ani_native_function {"getSystemConfig", "D:D", reinterpret_cast<void *>(PManagerGetSystemConfig)},
539         ani_native_function {"getEnvironmentVar", "Lstd/core/String;:Lstd/core/String;",
540                              reinterpret_cast<void *>(PManagerGetEnvironmentVar)},
541         ani_native_function {"exit", "D:V", reinterpret_cast<void *>(PManagerExit)},
542         ani_native_function {"kill", "DD:Z", reinterpret_cast<void *>(PManagerKill)},
543     };
544 
545     const auto processImpls = std::array {
546         ani_native_function {"tid", ":D", reinterpret_cast<void *>(GetTid)},
547         ani_native_function {"pid", ":D", reinterpret_cast<void *>(GetPid)},
548         ani_native_function {"ppid", ":D", reinterpret_cast<void *>(GetPPid)},
549         ani_native_function {"uid", ":D", reinterpret_cast<void *>(GetUid)},
550         ani_native_function {"euid", ":D", reinterpret_cast<void *>(GetEuid)},
551         ani_native_function {"gid", ":D", reinterpret_cast<void *>(GetGid)},
552         ani_native_function {"egid", ":D", reinterpret_cast<void *>(GetEgid)},
553         ani_native_function {"groups", ":[D", reinterpret_cast<void *>(GetGroupIDs)},
554         ani_native_function {"is64Bit", ":Z", reinterpret_cast<void *>(Is64BitProcess)},
555         ani_native_function {"getStartRealtime", ":D", reinterpret_cast<void *>(GetProcessStartRealTime)},
556         ani_native_function {"getPastCpuTime", ":D", reinterpret_cast<void *>(GetProcessPastCpuTime)},
557         ani_native_function {"abort", ":V", reinterpret_cast<void *>(AbortProcess)},
558         ani_native_function {"cwd", ":Lstd/core/String;", reinterpret_cast<void *>(GetCurrentWorkingDirectory)},
559         ani_native_function {"chdir", "Lstd/core/String;:V", reinterpret_cast<void *>(ChangeCurrentWorkingDirectory)},
560         ani_native_function {"uptime", ":D", reinterpret_cast<void *>(GetSystemUptime)},
561         ani_native_function {"isIsolatedProcess", ":Z", reinterpret_cast<void *>(IsIsolatedProcImpl)},
562     };
563 
564     ani_class childProcessKlass;
565     ANI_FATAL_IF_ERROR(env->FindClass("Lescompat/StdProcess/ChildProcess;", &childProcessKlass));
566     ANI_FATAL_IF_ERROR(
567         env->Class_BindNativeMethods(childProcessKlass, childProcessImpls.data(), childProcessImpls.size()));
568 
569     ani_class processManagerKlass;
570     ANI_FATAL_IF_ERROR(env->FindClass("Lescompat/StdProcess/ProcessManager;", &processManagerKlass));
571     ANI_FATAL_IF_ERROR(
572         env->Class_BindNativeMethods(processManagerKlass, processManagerImpls.data(), processManagerImpls.size()));
573 
574     ani_namespace ns {};
575     ANI_FATAL_IF_ERROR(env->FindNamespace("Lescompat/StdProcess;", &ns));
576 
577     ANI_FATAL_IF_ERROR(env->Namespace_BindNativeFunctions(ns, processImpls.data(), processImpls.size()));
578 }
579 
580 }  // namespace ark::ets::stdlib
581