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