You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
10KB

  1. /*
  2. * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
  3. *
  4. * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
  5. *
  6. * Use of this source code is governed by MIT license that can be found in the
  7. * LICENSE file in the root of the source tree. All contributing project authors
  8. * may be found in the AUTHORS file in the root of the source tree.
  9. */
  10. #include <limits.h>
  11. #include <sys/stat.h>
  12. #ifndef _WIN32
  13. #include <sys/resource.h>
  14. #include <unistd.h>
  15. #else
  16. #include <io.h>
  17. #include <windows.h>
  18. #endif
  19. #include <csignal>
  20. #include <stdexcept>
  21. #include "Process.h"
  22. #include "Util/File.h"
  23. #include "Util/util.h"
  24. #include "Util/logger.h"
  25. #include "Util/uv_errno.h"
  26. #include "Poller/EventPoller.h"
  27. #define STACK_SIZE (8192 * 1024)
  28. using namespace std;
  29. using namespace toolkit;
  30. #ifndef _WIN32
  31. static void setupChildProcess() {
  32. //取消cpu亲和性设置,防止FFmpeg进程cpu占用率不能超过100%的问题
  33. setThreadAffinity(-1);
  34. //子进程关闭core文件生成
  35. struct rlimit rlim = { 0, 0 };
  36. setrlimit(RLIMIT_CORE, &rlim);
  37. //子进程恢复默认信号处理
  38. signal(SIGINT, SIG_DFL);
  39. signal(SIGTERM, SIG_DFL);
  40. signal(SIGSEGV, SIG_DFL);
  41. signal(SIGABRT, SIG_DFL);
  42. }
  43. /* Start function for cloned child */
  44. static int runChildProcess(string cmd, string log_file) {
  45. setupChildProcess();
  46. if (log_file.empty()) {
  47. //未指定子进程日志文件时,重定向至/dev/null
  48. log_file = "/dev/null";
  49. } else {
  50. log_file = StrPrinter << log_file << "." << getpid();
  51. }
  52. if (isatty(STDIN_FILENO)) {
  53. /* bb_error_msg("ignoring input"); */
  54. close(STDIN_FILENO);
  55. open("/dev/null", O_RDONLY, 0666); /* will be fd 0 (STDIN_FILENO) */
  56. }
  57. //重定向shell日志至文件
  58. auto fp = File::create_file(log_file.data(), "ab");
  59. if (!fp) {
  60. fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
  61. } else {
  62. auto log_fd = fileno(fp);
  63. // dup to stdout and stderr.
  64. if (dup2(log_fd, STDOUT_FILENO) < 0) {
  65. fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
  66. }
  67. if (dup2(log_fd, STDERR_FILENO) < 0) {
  68. fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
  69. }
  70. // 关闭日志文件
  71. ::fclose(fp);
  72. }
  73. fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data());
  74. auto params = split(cmd, " ");
  75. // memory leak in child process, it's ok.
  76. char **charpv_params = new char *[params.size() + 1];
  77. for (int i = 0; i < (int)params.size(); i++) {
  78. std::string &p = params[i];
  79. charpv_params[i] = (char *)p.data();
  80. }
  81. // EOF: NULL
  82. charpv_params[params.size()] = NULL;
  83. // TODO: execv or execvp
  84. auto ret = execv(params[0].c_str(), charpv_params);
  85. delete[] charpv_params;
  86. if (ret < 0) {
  87. fprintf(stderr, "execv process failed:%d(%s)\r\n", get_uv_error(), get_uv_errmsg());
  88. }
  89. return ret;
  90. }
  91. static int cloneFunc(void *ptr) {
  92. auto pair = reinterpret_cast<std::pair<string, string> *>(ptr);
  93. return runChildProcess(pair->first, pair->second);
  94. }
  95. #endif
  96. void Process::run(const string &cmd, string &log_file) {
  97. kill(2000);
  98. #ifdef _WIN32
  99. STARTUPINFO si = { 0 };
  100. PROCESS_INFORMATION pi = { 0 };
  101. if (log_file.empty()) {
  102. //未指定子进程日志文件时,重定向至/dev/null
  103. log_file = "NUL";
  104. } else {
  105. log_file = StrPrinter << log_file << "." << getCurrentMillisecond();
  106. }
  107. //重定向shell日志至文件
  108. auto fp = File::create_file(log_file.data(), "ab");
  109. if (!fp) {
  110. fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg());
  111. } else {
  112. auto log_fd = (HANDLE)(_get_osfhandle(_fileno(fp)));
  113. // dup to stdout and stderr.
  114. si.wShowWindow = SW_HIDE;
  115. // STARTF_USESHOWWINDOW:The wShowWindow member contains additional information.
  116. // STARTF_USESTDHANDLES:The hStdInput, hStdOutput, and hStdError members contain additional information.
  117. si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  118. si.hStdError = log_fd;
  119. si.hStdOutput = log_fd;
  120. }
  121. LPTSTR lpDir = const_cast<char *>(cmd.data());
  122. if (CreateProcess(NULL, lpDir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
  123. //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
  124. CloseHandle(pi.hThread);
  125. _pid = pi.dwProcessId;
  126. _handle = pi.hProcess;
  127. fprintf(fp, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", _pid, cmd.data());
  128. InfoL << "start child process " << _pid << ", log file:" << log_file;
  129. } else {
  130. WarnL << "start child process fail: " << get_uv_errmsg();
  131. }
  132. fclose(fp);
  133. #else
  134. #if (defined(__linux) || defined(__linux__))
  135. _process_stack = malloc(STACK_SIZE);
  136. auto args = std::make_pair(cmd, log_file);
  137. _pid = clone(reinterpret_cast<int (*)(void *)>(&cloneFunc), (char *)_process_stack + STACK_SIZE, CLONE_FS | SIGCHLD, (void *)(&args));
  138. if (_pid == -1) {
  139. WarnL << "clone process failed:" << get_uv_errmsg();
  140. free(_process_stack);
  141. _process_stack = nullptr;
  142. throw std::runtime_error(StrPrinter << "fork child process failed,err:" << get_uv_errmsg());
  143. }
  144. #else
  145. _pid = fork();
  146. if (_pid == -1) {
  147. throw std::runtime_error(StrPrinter << "fork child process failed,err:" << get_uv_errmsg());
  148. }
  149. if (_pid == 0) {
  150. //子进程
  151. exit(runChildProcess(cmd, log_file));
  152. }
  153. #endif
  154. if (log_file.empty()) {
  155. //未指定子进程日志文件时,重定向至/dev/null
  156. log_file = "/dev/null";
  157. } else {
  158. log_file = StrPrinter << log_file << "." << _pid;
  159. }
  160. InfoL << "start child process " << _pid << ", log file:" << log_file;
  161. #endif // _WIN32
  162. }
  163. /**
  164. * 获取进程是否存活状态
  165. * @param pid 进程号
  166. * @param exit_code_ptr 进程返回代码
  167. * @param block 是否阻塞等待
  168. * @return 进程是否还在运行
  169. */
  170. static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) {
  171. if (pid <= 0) {
  172. return false;
  173. }
  174. #ifdef _WIN32
  175. DWORD code = 0;
  176. if (block) {
  177. //一直等待
  178. code = WaitForSingleObject(handle, INFINITE);
  179. } else {
  180. code = WaitForSingleObject(handle, 0);
  181. }
  182. if (code == WAIT_FAILED || code == WAIT_OBJECT_0) {
  183. //子进程已经退出了,获取子进程退出代码
  184. DWORD exitCode = 0;
  185. if (exit_code_ptr && GetExitCodeProcess(handle, &exitCode)) {
  186. *exit_code_ptr = exitCode;
  187. }
  188. return false;
  189. }
  190. if (code == WAIT_TIMEOUT) {
  191. //子进程还在线
  192. return true;
  193. }
  194. //不太可能运行到此处
  195. WarnL << "WaitForSingleObject ret:" << code;
  196. return false;
  197. #else
  198. int status = 0;
  199. pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG);
  200. int exit_code = (status & 0xFF00) >> 8;
  201. if (exit_code_ptr) {
  202. *exit_code_ptr = exit_code;
  203. }
  204. if (p < 0) {
  205. WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg();
  206. return false;
  207. }
  208. if (p > 0) {
  209. InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code;
  210. return false;
  211. }
  212. return true;
  213. #endif // _WIN32
  214. }
  215. #ifdef _WIN32
  216. // Inspired from http://stackoverflow.com/a/15281070/1529139
  217. // and http://stackoverflow.com/q/40059902/1529139
  218. bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent) {
  219. bool success = false;
  220. DWORD thisConsoleId = GetCurrentProcessId();
  221. // Leave current console if it exists
  222. // (otherwise AttachConsole will return ERROR_ACCESS_DENIED)
  223. bool consoleDetached = (FreeConsole() != FALSE);
  224. if (AttachConsole(dwProcessId) != FALSE) {
  225. // Add a fake Ctrl-C handler for avoid instant kill is this console
  226. // WARNING: do not revert it or current program will be also killed
  227. SetConsoleCtrlHandler(nullptr, true);
  228. success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE);
  229. FreeConsole();
  230. }
  231. if (consoleDetached) {
  232. // Create a new console if previous was deleted by OS
  233. if (AttachConsole(thisConsoleId) == FALSE) {
  234. int errorCode = GetLastError();
  235. if (errorCode == 31) {
  236. // 31=ERROR_GEN_FAILURE
  237. AllocConsole();
  238. }
  239. }
  240. }
  241. return success;
  242. }
  243. #endif // _WIN32
  244. static void s_kill(pid_t pid, void *handle, int max_delay, bool force) {
  245. if (pid <= 0) {
  246. // pid无效
  247. return;
  248. }
  249. #ifdef _WIN32
  250. // windows下目前没有比较好的手段往子进程发送SIGTERM或信号
  251. //所以杀死子进程的方式全部强制为立即关闭
  252. force = true;
  253. if (force) {
  254. //强制关闭子进程
  255. TerminateProcess(handle, 0);
  256. } else {
  257. //非强制关闭,发送Ctr+C信号
  258. signalCtrl(pid, CTRL_C_EVENT);
  259. }
  260. #else
  261. if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
  262. //进程可能已经退出了
  263. WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
  264. return;
  265. }
  266. #endif // _WIN32
  267. if (force) {
  268. //发送SIGKILL信号后,阻塞等待退出
  269. s_wait(pid, handle, nullptr, true);
  270. DebugL << "force kill " << pid << " success!";
  271. return;
  272. }
  273. //发送SIGTERM信号后,2秒后检查子进程是否已经退出
  274. EventPollerPool::Instance().getPoller()->doDelayTask(max_delay, [pid, handle]() {
  275. if (!s_wait(pid, handle, nullptr, false)) {
  276. //进程已经退出了
  277. return 0;
  278. }
  279. //进程还在运行
  280. WarnL << "process still working,force kill it:" << pid;
  281. s_kill(pid, handle, 0, true);
  282. return 0;
  283. });
  284. }
  285. void Process::kill(int max_delay, bool force) {
  286. if (_pid <= 0) {
  287. return;
  288. }
  289. s_kill(_pid, _handle, max_delay, force);
  290. _pid = -1;
  291. #ifdef _WIN32
  292. if (_handle) {
  293. CloseHandle(_handle);
  294. _handle = nullptr;
  295. }
  296. #elif ((defined(__linux) || defined(__linux__)))
  297. if (_process_stack) {
  298. free(_process_stack);
  299. _process_stack = nullptr;
  300. }
  301. #endif
  302. }
  303. Process::~Process() {
  304. kill(2000);
  305. }
  306. Process::Process() {}
  307. bool Process::wait(bool block) {
  308. return s_wait(_pid, _handle, &_exit_code, block);
  309. }
  310. int Process::exit_code() {
  311. return _exit_code;
  312. }