• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "js_childprocess.h"
17 
18 #include <map>
19 #include <vector>
20 
21 #include <csignal>
22 #include <cstdlib>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <sys/wait.h>
26 
27 #include "securec.h"
28 #include "utils/log.h"
29 
30 namespace OHOS::JsSysModule::Process {
31     constexpr int MAXSIZE = 1024;
32     constexpr int TIME_EXCHANGE = 1000;
33     std::map<std::string, int> g_signalsMap = {
34         {"SIGHUP", 1},
35         {"SIGINT", 2},
36         {"SIGQUIT", 3},
37         {"SIGILL", 4},
38         {"SIGTRAP", 5},
39         {"SIGABRT", 6},
40         {"SIGBUS", 7},
41         {"SIGFPE", 8},
42         {"SIGKILL", 9},
43         {"SIGUSR1", 10},
44         {"SIGSEGV", 11},
45         {"SIGUSR2", 12},
46         {"SIGPIPE", 13},
47         {"SIGALRM", 14},
48         {"SIGTERM", 15},
49         {"SIGSTKFLT", 16},
50         {"SIGCHLD", 17},
51         {"SIGCONT", 18},
52         {"SIGSTOP", 19},
53         {"SIGTSTP", 20},
54         {"SIGTTIN", 21},
55         {"SIGTTOU", 22},
56         {"SIGURG", 23},
57         {"SIGXCPU", 24},
58         {"SIGXFSZ", 25},
59         {"SIGVTALRM", 26},
60         {"SIGPROF", 27},
61         {"SIGWINCH", 28},
62         {"SIGIO", 29},
63         {"SIGPWR", 30},
64         {"SIGSYS", 31}
65     };
66 
Spawn(napi_env env,napi_value command)67     void ChildProcess::Spawn(napi_env env, napi_value command)
68     {
69         int ret = pipe(stdOutFd_);
70         if (ret < 0) {
71             HILOG_ERROR("pipe1 failed %{public}d", errno);
72             return;
73         }
74         ret = pipe(stdErrFd_);
75         if (ret < 0) {
76             HILOG_ERROR("pipe2 failed %{public}d", errno);
77             return;
78         }
79         std::string strCommnd = RequireStrValue(env, command);
80         pid_t pid = fork();
81         if (!pid) {
82             close(stdErrFd_[0]);
83             close(stdOutFd_[0]);
84             dup2(stdOutFd_[1], 1);
85             dup2(stdErrFd_[1], 2); // 2:The value of parameter
86             if (execl("/bin/sh", "sh", "-c", strCommnd.c_str(), nullptr) == -1) {
87                 HILOG_ERROR("execl command failed");
88                 _exit(127); // 127:The parameter value
89             }
90         } else if (pid > 0) {
91             if (optionsInfo_ == nullptr) {
92                 HILOG_ERROR("optionsInfo_ is nullptr");
93                 return;
94             }
95             optionsInfo_->pid = pid;
96             ppid_ = getpid();
97             CreateWorker(env);
98             napi_value resourceName = nullptr;
99             napi_create_string_utf8(env, "TimeoutListener", strlen("TimeoutListener"), &resourceName);
100             napi_create_async_work(
101                 env, nullptr, resourceName, TimeoutListener,
102                 [](napi_env env, napi_status status, void* data) {
103                     OptionsInfo* optionsInfo = reinterpret_cast<OptionsInfo*>(data);
104                     napi_delete_async_work(env, optionsInfo->worker);
105                     delete optionsInfo;
106                     optionsInfo = nullptr;
107                 },
108                 reinterpret_cast<void*>(optionsInfo_), &optionsInfo_->worker);
109             napi_queue_async_work(env, optionsInfo_->worker);
110             close(stdErrFd_[1]);
111             close(stdOutFd_[1]);
112         } else {
113             HILOG_ERROR("child process create failed");
114         }
115     }
116 
Wait(napi_env env)117     napi_value ChildProcess::Wait(napi_env env)
118     {
119         napi_value promise = nullptr;
120         auto waitInfo = new WaitInfo;
121         napi_create_promise(env, &(waitInfo->deferred), &promise);
122 
123         if (isWait_) {
124             int32_t status;
125             isWait_ = false;
126             if (optionsInfo_ == nullptr) {
127                 napi_value res = nullptr;
128                 napi_get_undefined(env, &res);
129                 delete waitInfo;
130                 waitInfo = nullptr;
131                 HILOG_ERROR("optionsInfo_ is nullptr");
132                 return res;
133             }
134             waitpid(optionsInfo_->pid, &status, 0);
135             exitCode_ = status;
136         }
137         isNeedRun_ = false;
138         napi_value result = nullptr;
139         napi_create_int32(env, static_cast<int8_t>(exitCode_), &result);
140         napi_resolve_deferred(env, waitInfo->deferred, result);
141         delete waitInfo;
142         waitInfo = nullptr;
143 
144         return promise;
145     }
146 
GetOutput(napi_env env) const147     napi_value ChildProcess::GetOutput(napi_env env) const
148     {
149         if (stdOutInfo_ == nullptr) {
150             napi_value res = nullptr;
151             NAPI_CALL(env, napi_get_undefined(env, &res));
152             HILOG_ERROR("stdOutInfo_ is nullptr");
153             return res;
154         }
155         NAPI_CALL(env, napi_create_promise(env, &stdOutInfo_->deferred, &stdOutInfo_->promise));
156         void* data = nullptr;
157         napi_value arrayBuffer = nullptr;
158         size_t bufferSize = stdOutInfo_->stdData.size() + 1;
159         NAPI_CALL(env, napi_create_arraybuffer(env, bufferSize, &data, &arrayBuffer));
160         if (memcpy_s(data, bufferSize, reinterpret_cast<const void*>(stdOutInfo_->stdData.c_str()),
161             stdOutInfo_->stdData.size()) != EOK) {
162             HILOG_ERROR("getOutput memcpy_s failed");
163             NAPI_CALL(env, napi_delete_async_work(env, stdOutInfo_->worker));
164             napi_value res = nullptr;
165             NAPI_CALL(env, napi_get_undefined(env, &res));
166             return res;
167         }
168 
169         napi_value result = nullptr;
170         NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, bufferSize, arrayBuffer, 0, &result));
171         NAPI_CALL(env, napi_resolve_deferred(env, stdOutInfo_->deferred, result));
172         return stdOutInfo_->promise;
173     }
174 
GetErrorOutput(napi_env env) const175     napi_value ChildProcess::GetErrorOutput(napi_env env) const
176     {
177         if (stdErrInfo_ == nullptr) {
178             napi_value res = nullptr;
179             NAPI_CALL(env, napi_get_undefined(env, &res));
180             HILOG_ERROR("stdErrInfo_ is nullptr");
181             return res;
182         }
183         NAPI_CALL(env, napi_create_promise(env, &stdErrInfo_->deferred, &stdErrInfo_->promise));
184         void* data = nullptr;
185         napi_value arrayBuffer = nullptr;
186         size_t bufferSize = stdErrInfo_->stdData.size() + 1;
187         NAPI_CALL(env, napi_create_arraybuffer(env, bufferSize, &data, &arrayBuffer));
188         if (memcpy_s(data, bufferSize, reinterpret_cast<const void*>(stdErrInfo_->stdData.c_str()),
189             stdErrInfo_->stdData.size()) != EOK) {
190             HILOG_ERROR("getErrOutput memcpy_s failed");
191             NAPI_CALL(env, napi_delete_async_work(env, stdErrInfo_->worker));
192             napi_value res = nullptr;
193             NAPI_CALL(env, napi_get_undefined(env, &res));
194             return res;
195         }
196 
197         napi_value result = nullptr;
198         NAPI_CALL(env, napi_create_typedarray(env, napi_uint8_array, bufferSize, arrayBuffer, 0, &result));
199         NAPI_CALL(env, napi_resolve_deferred(env, stdErrInfo_->deferred, result));
200         return stdErrInfo_->promise;
201     }
202 
GetKilled(napi_env env) const203     napi_value ChildProcess::GetKilled(napi_env env) const
204     {
205         napi_value result = nullptr;
206         NAPI_CALL(env, napi_get_boolean(env, killed_, &result));
207 
208         return result;
209     }
210 
Getpid(napi_env env) const211     napi_value ChildProcess::Getpid(napi_env env) const
212     {
213         napi_value result = nullptr;
214         if (optionsInfo_ == nullptr) {
215             napi_value res = nullptr;
216             NAPI_CALL(env, napi_get_undefined(env, &res));
217             HILOG_ERROR("optionsInfo_ is nullptr");
218             return res;
219         }
220         NAPI_CALL(env, napi_create_int32(env, optionsInfo_->pid, &result));
221 
222         return result;
223     }
224 
Getppid(napi_env env) const225     napi_value ChildProcess::Getppid(napi_env env) const
226     {
227         napi_value result = nullptr;
228         NAPI_CALL(env, napi_create_int32(env, ppid_, &result));
229 
230         return result;
231     }
232 
GetExitCode(napi_env env) const233     napi_value ChildProcess::GetExitCode(napi_env env) const
234     {
235         napi_value result = nullptr;
236         NAPI_CALL(env, napi_create_int32(env, static_cast<int8_t>(exitCode_), &result));
237 
238         return result;
239     }
240 
CreateWorker(napi_env env)241     void ChildProcess::CreateWorker(napi_env env)
242     {
243         // getstdout
244         napi_value resourceName = nullptr;
245         stdOutInfo_ = new StdInfo();
246         if (stdOutInfo_ == nullptr) {
247             HILOG_ERROR("stdOutInfo_ is nullptr");
248             return;
249         }
250         stdOutInfo_->isNeedRun = &isNeedRun_;
251         stdOutInfo_->fd = stdOutFd_[0];
252         if (optionsInfo_ == nullptr) {
253             HILOG_ERROR("optionsInfo_ is nullptr");
254             return;
255         }
256         stdOutInfo_->pid = optionsInfo_->pid;
257         stdOutInfo_->maxBuffSize = optionsInfo_->maxBuffer;
258         napi_create_string_utf8(env, "ReadStdOut", NAPI_AUTO_LENGTH, &resourceName);
259         napi_create_async_work(env, nullptr, resourceName, ReadStdOut, EndStdOut,
260                                reinterpret_cast<void*>(stdOutInfo_), &stdOutInfo_->worker);
261         napi_queue_async_work(env, stdOutInfo_->worker);
262 
263         // getstderr
264         stdErrInfo_ = new StdInfo();
265         if (stdErrInfo_ == nullptr) {
266             HILOG_ERROR("stdErrInfo_ is nullptr");
267             return;
268         }
269         stdErrInfo_->isNeedRun = &isNeedRun_;
270         stdErrInfo_->fd = stdErrFd_[0];
271         stdErrInfo_->pid = optionsInfo_->pid;
272         stdErrInfo_->maxBuffSize = optionsInfo_->maxBuffer;
273         napi_create_string_utf8(env, "ReadStdErr", NAPI_AUTO_LENGTH, &resourceName);
274         napi_create_async_work(env, nullptr, resourceName, ReadStdErr, EndStdErr,
275                                reinterpret_cast<void*>(stdErrInfo_), &stdErrInfo_->worker);
276         napi_queue_async_work(env, stdErrInfo_->worker);
277     }
278 
ReadStdOut(napi_env env,void * data)279     void ChildProcess::ReadStdOut(napi_env env, void* data)
280     {
281         auto stdOutInfo = reinterpret_cast<StdInfo*>(data);
282         char childStdout[MAXSIZE] = {0};
283         if (stdOutInfo->isNeedRun == nullptr) {
284             return;
285         }
286         while (*(stdOutInfo->isNeedRun)) {
287             auto readSize = read(stdOutInfo->fd, childStdout, sizeof(childStdout) - 1);
288             if (readSize >= 0) {
289                 stdOutInfo->stdData += childStdout;
290             }
291             if (stdOutInfo->stdData.size() > static_cast<size_t>(stdOutInfo->maxBuffSize) && *(stdOutInfo->isNeedRun)) {
292                 if (!kill(stdOutInfo->pid, SIGKILL)) {
293                     *(stdOutInfo->isNeedRun) = false;
294                     stdOutInfo->stdData = stdOutInfo->stdData.substr(0, stdOutInfo->maxBuffSize);
295                 } else {
296                     HILOG_ERROR("stdOut maxBuff kill signal failed");
297                 }
298             }
299             if (memset_s(childStdout, sizeof(childStdout), '\0', MAXSIZE) != EOK) {
300                 HILOG_ERROR("getOutput memset_s failed");
301                 return;
302             }
303         }
304     }
305 
EndStdOut(napi_env env,napi_status status,void * buffer)306     void ChildProcess::EndStdOut(napi_env env, napi_status status, void* buffer)
307     {
308         auto stdOutInfo = reinterpret_cast<StdInfo*>(buffer);
309         napi_delete_async_work(env, stdOutInfo->worker);
310         delete stdOutInfo;
311         stdOutInfo = nullptr;
312     }
313 
ReadStdErr(napi_env env,void * data)314     void ChildProcess::ReadStdErr(napi_env env, void* data)
315     {
316         auto stdErrInfo = reinterpret_cast<StdInfo*>(data);
317         char childStderr[MAXSIZE] = {0};
318         if (stdErrInfo->isNeedRun == nullptr) {
319             return;
320         }
321         while (*(stdErrInfo->isNeedRun)) {
322             auto readSize = read(stdErrInfo->fd, childStderr, sizeof(childStderr) - 1);
323             if (readSize >= 0) {
324                 stdErrInfo->stdData += childStderr;
325             }
326             if (stdErrInfo->stdData.size() > static_cast<size_t>(stdErrInfo->maxBuffSize) && *(stdErrInfo->isNeedRun)) {
327                 if (!kill(stdErrInfo->pid, SIGKILL)) {
328                     *(stdErrInfo->isNeedRun) = false;
329                     stdErrInfo->stdData = stdErrInfo->stdData.substr(0, stdErrInfo->maxBuffSize);
330                 } else {
331                     HILOG_ERROR("stdErr maxBuff kill signal failed");
332                 }
333             }
334             if (memset_s(childStderr, sizeof(childStderr), '\0', MAXSIZE) != EOK) {
335                 HILOG_ERROR("getOutput memset_s failed");
336                 return;
337             }
338         }
339     }
340 
EndStdErr(napi_env env,napi_status status,void * buffer)341     void ChildProcess::EndStdErr(napi_env env, napi_status status, void* buffer)
342     {
343         auto stdErrInfo = reinterpret_cast<StdInfo*>(buffer);
344         napi_delete_async_work(env, stdErrInfo->worker);
345         delete stdErrInfo;
346         stdErrInfo = nullptr;
347     }
348 
GetValidSignal(napi_env env,const napi_value signo)349     int ChildProcess::GetValidSignal(napi_env env, const napi_value signo)
350     {
351         int32_t sig = 0;
352         napi_valuetype valuetype = napi_undefined;
353         napi_typeof(env, signo, &valuetype);
354         if (valuetype == napi_valuetype::napi_number) {
355             napi_get_value_int32(env, signo, &sig);
356             return sig;
357         } else if (valuetype == napi_valuetype::napi_string) {
358             std::string buffer = RequireStrValue(env, signo);
359             auto iter = g_signalsMap.find(buffer);
360             if (iter != g_signalsMap.end()) {
361                 sig = iter->second;
362                 return sig;
363             } else {
364                 return g_signalsMap["SIGTERM"];
365             }
366         } else {
367             return g_signalsMap["SIGTERM"];
368         }
369     }
370 
Kill(napi_env env,const napi_value signo)371     void ChildProcess::Kill(napi_env env, const napi_value signo)
372     {
373         int signal = GetValidSignal(env, signo);
374         std::vector<int32_t> signalType = {SIGINT, SIGQUIT, SIGKILL, SIGTERM};
375         if (optionsInfo_ == nullptr) {
376             HILOG_ERROR("optionsInfo_ is nullptr");
377             return;
378         }
379         if (!kill(optionsInfo_->pid, signal)) {
380             auto res = std::find(signalType.begin(), signalType.end(), static_cast<int32_t>(signal));
381             (res != signalType.end()) ? isNeedRun_ = false : 0;
382             killed_ = true;
383         } else {
384             HILOG_ERROR("kill signal failed");
385         }
386     }
387 
Close()388     void ChildProcess::Close()
389     {
390         int32_t status = 0;
391         if (optionsInfo_ == nullptr) {
392             HILOG_ERROR("optionsInfo_ is nullptr");
393             return;
394         }
395         if (isWait_ && !(waitpid(optionsInfo_->pid, &status, WNOHANG)) && isNeedRun_) {
396             if (!kill(optionsInfo_->pid, SIGKILL)) {
397                 waitpid(optionsInfo_->pid, &status, 0);
398                 isWait_ = false;
399                 exitCode_ = status;
400                 isNeedRun_ = false;
401             } else {
402                 HILOG_ERROR("close kill SIGKILL signal failed");
403             }
404         }
405     }
406 
TimeoutListener(napi_env env,void * data)407     void ChildProcess::TimeoutListener(napi_env env, void* data)
408     {
409         std::vector<int32_t> signalType = {SIGINT, SIGQUIT, SIGKILL, SIGTERM};
410         auto temp = reinterpret_cast<OptionsInfo*>(data);
411         int32_t timeout = temp->timeout * TIME_EXCHANGE;
412         if (timeout > 0) {
413             usleep(static_cast<size_t>(timeout));
414             if (*(temp->isNeedRun)) {
415                 if (!kill(temp->pid, temp->killSignal)) {
416                     auto res = std::find(signalType.begin(), signalType.end(), temp->killSignal);
417                     (res != signalType.end()) ? *(temp->isNeedRun) = false : 0;
418                 } else {
419                     HILOG_ERROR("timeout kill signal failed");
420                 }
421             }
422         }
423     }
424 
InitOptionsInfo(napi_env env,napi_value options)425     void ChildProcess::InitOptionsInfo(napi_env env, napi_value options)
426     {
427         std::vector<std::string> keyStr = {"timeout", "killSignal", "maxBuffer"};
428         optionsInfo_ = new OptionsInfo();
429         if (optionsInfo_ == nullptr) {
430             HILOG_ERROR("optionsInfo_ is nullptr");
431             return;
432         }
433         size_t size = keyStr.size();
434         for (size_t i = 0; i < size; i++) {
435             napi_status status = napi_ok;
436             napi_value property = nullptr;
437             napi_get_named_property(env, options, keyStr[i].c_str(), &property);
438             switch (i) {
439                 case 0:
440                     status = napi_get_value_int32(env, property, &optionsInfo_->timeout);
441                     if (status != napi_ok) {
442                         optionsInfo_->timeout = 0;
443                     }
444                     break;
445                 case 1:
446                     optionsInfo_->killSignal = GetValidSignal(env, property);
447                     break;
448                 case 2: // 2:The parameter value
449                     status = napi_get_value_int64(env, property, &optionsInfo_->maxBuffer);
450                     if (status != napi_ok) {
451                         optionsInfo_->maxBuffer = static_cast<int64_t>(MAXSIZE) * static_cast<int64_t>(MAXSIZE);
452                     }
453                     break;
454                 default:
455                     break;
456             }
457         }
458         optionsInfo_->isNeedRun = &isNeedRun_;
459     }
460 
RequireStrValue(napi_env env,const napi_value strValue)461     std::string ChildProcess::RequireStrValue(napi_env env, const napi_value strValue)
462     {
463         size_t bufferSize = 0;
464         if (napi_get_value_string_utf8(env, strValue, nullptr, 0, &bufferSize) != napi_ok) {
465             HILOG_ERROR("can not get strValue size");
466             return nullptr;
467         }
468         std::string result = "";
469         result.reserve(bufferSize + 1);
470         result.resize(bufferSize);
471         if (napi_get_value_string_utf8(env, strValue, result.data(), bufferSize + 1, &bufferSize) != napi_ok) {
472             HILOG_ERROR("can not get strValue value");
473             return nullptr;
474         }
475         return result;
476     }
477 
~ChildProcess()478     ChildProcess::~ChildProcess()
479     {
480         close(stdOutFd_[0]);
481         close(stdErrFd_[0]);
482         if (isWait_) {
483             int32_t status = 0;
484             waitpid(optionsInfo_->pid, &status, 0);
485         }
486         isNeedRun_ = false;
487     }
488 } // namespace OHOS::JsSysModule::Process
489