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