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