Commit b08ea0fc by xiongziliang

windows下子进程支持日志重定向、等待子进程推出、获取子进程退出码等特性

parent 8ef8c91f
...@@ -24,19 +24,16 @@ const string kSnap = FFmpeg_FIELD"snap"; ...@@ -24,19 +24,16 @@ const string kSnap = FFmpeg_FIELD"snap";
onceToken token([]() { onceToken token([]() {
#ifdef _WIN32 #ifdef _WIN32
string ffmpeg_bin = System::execute("where ffmpeg"); string ffmpeg_bin = trim(System::execute("where ffmpeg"));
//windows下先关闭FFmpeg日志(目前不支持日志重定向)
mINI::Instance()[kCmd] = "%s -re -i %s -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kSnap] = "%s -i %s -loglevel quiet -y -f mjpeg -t 0.001 %s";
#else #else
string ffmpeg_bin = System::execute("which ffmpeg"); string ffmpeg_bin = trim(System::execute("which ffmpeg"));
mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kSnap] = "%s -i %s -y -f mjpeg -t 0.001 %s";
#endif #endif
//默认ffmpeg命令路径为环境变量中路径 //默认ffmpeg命令路径为环境变量中路径
mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin;
//ffmpeg日志保存路径 //ffmpeg日志保存路径
mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log";
mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s";
mINI::Instance()[kSnap] = "%s -i %s -y -f mjpeg -t 0.001 %s";
}); });
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#else #else
//#include <TlHelp32.h> //#include <TlHelp32.h>
#include <windows.h> #include <windows.h>
#include <io.h>
#endif #endif
#include <stdexcept> #include <stdexcept>
...@@ -31,23 +32,44 @@ using namespace toolkit; ...@@ -31,23 +32,44 @@ using namespace toolkit;
void Process::run(const string &cmd, const string &log_file_tmp) { void Process::run(const string &cmd, const string &log_file_tmp) {
kill(2000); kill(2000);
#ifdef _WIN32 #ifdef _WIN32
STARTUPINFO si; STARTUPINFO si = {0};
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi = {0};
ZeroMemory(&si, sizeof(si)); //结构体初始化; string log_file;
ZeroMemory(&pi, sizeof(pi)); if (log_file_tmp.empty()) {
//未指定子进程日志文件时,重定向至/dev/null
log_file = "NUL";
}
else {
log_file = StrPrinter << log_file_tmp << "." << getCurrentMillisecond();
}
LPTSTR lpDir = const_cast<char*>(cmd.data()); //重定向shell日志至文件
auto fp = File::create_file(log_file.data(), "ab");
if (!fp) {
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
} else {
auto log_fd = (HANDLE)(_get_osfhandle(fileno(fp)));
// dup to stdout and stderr.
si.wShowWindow = SW_HIDE;
// STARTF_USESHOWWINDOW:The wShowWindow member contains additional information.
// STARTF_USESTDHANDLES:The hStdInput, hStdOutput, and hStdError members contain additional information.
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdError = log_fd;
si.hStdOutput = log_fd;
}
if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){ LPTSTR lpDir = const_cast<char*>(cmd.data());
if (CreateProcess(NULL, lpDir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)){
//下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
_pid = pi.dwProcessId; _pid = pi.dwProcessId;
fprintf(fp, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", _pid, cmd.data());
InfoL << "start child proces " << _pid; InfoL << "start child proces " << _pid;
} else { } else {
WarnL << "start child proces fail: " << GetLastError(); WarnL << "start child proces fail: " << get_uv_errmsg();
} }
fclose(fp);
#else #else
_pid = fork(); _pid = fork();
if (_pid < 0) { if (_pid < 0) {
...@@ -125,16 +147,42 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { ...@@ -125,16 +147,42 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) {
if (pid <= 0) { if (pid <= 0) {
return false; return false;
} }
int status = 0;
#ifdef _WIN32 #ifdef _WIN32
HANDLE hProcess = NULL; HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //打开目标进程
if (hProcess == NULL) { if (!hProcess) {
WarnL << "OpenProcess failed:" << get_uv_errmsg();
return false;
}
DWORD code = 0;
if (block) {
//一直等待
code = WaitForSingleObject(hProcess, INFINITE);
} else {
code = WaitForSingleObject(hProcess, 0);
}
if(code == WAIT_FAILED || code == WAIT_OBJECT_0){
//子进程已经退出了,获取子进程退出代码
DWORD exitCode = 0;
if(GetExitCodeProcess(hProcess, &exitCode) && exit_code_ptr){
*exit_code_ptr = exitCode;
}
CloseHandle(hProcess);
return false; return false;
} }
CloseHandle(hProcess); CloseHandle(hProcess);
if(code == WAIT_TIMEOUT){
//子进程还在线
return true;
}
//不太可能运行到此处
WarnL << "WaitForSingleObject ret:" << code;
return false;
#else #else
int status = 0;
pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG); pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG);
int exit_code = (status & 0xFF00) >> 8; int exit_code = (status & 0xFF00) >> 8;
if (exit_code_ptr) { if (exit_code_ptr) {
...@@ -148,10 +196,41 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { ...@@ -148,10 +196,41 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) {
InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code; InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code;
return false; return false;
} }
return true;
#endif // _WIN32 #endif // _WIN32
}
return true; #ifdef _WIN32
// Inspired from http://stackoverflow.com/a/15281070/1529139
// and http://stackoverflow.com/q/40059902/1529139
bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent){
bool success = false;
DWORD thisConsoleId = GetCurrentProcessId();
// Leave current console if it exists
// (otherwise AttachConsole will return ERROR_ACCESS_DENIED)
bool consoleDetached = (FreeConsole() != FALSE);
if (AttachConsole(dwProcessId) != FALSE){
// Add a fake Ctrl-C handler for avoid instant kill is this console
// WARNING: do not revert it or current program will be also killed
SetConsoleCtrlHandler(nullptr, true);
success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE);
FreeConsole();
}
if (consoleDetached){
// Create a new console if previous was deleted by OS
if (AttachConsole(thisConsoleId) == FALSE){
int errorCode = GetLastError();
if (errorCode == 31){
// 31=ERROR_GEN_FAILURE
AllocConsole();
}
}
}
return success;
} }
#endif // _WIN32
static void s_kill(pid_t pid,int max_delay,bool force){ static void s_kill(pid_t pid,int max_delay,bool force){
if (pid <= 0) { if (pid <= 0) {
...@@ -161,13 +240,20 @@ static void s_kill(pid_t pid,int max_delay,bool force){ ...@@ -161,13 +240,20 @@ static void s_kill(pid_t pid,int max_delay,bool force){
#ifdef _WIN32 #ifdef _WIN32
HANDLE hProcess = NULL; HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
if (hProcess == NULL) { if (!hProcess) {
WarnL << "\nOpen Process fAiled: " << GetLastError(); //子进程可能已经推出了
return; return;
} }
DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 if(force){
if (ret == 0) { //强制关闭
WarnL << GetLastError; DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程
CloseHandle(hProcess);
if (ret == 0) {
WarnL << "TerminateProcess " << pid << " failed:" << get_uv_errmsg();
}
}else{
//非强制关闭,发生Ctr+C信号
signalCtrl(pid, CTRL_C_EVENT);
} }
#else #else
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) { if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
...@@ -177,7 +263,6 @@ static void s_kill(pid_t pid,int max_delay,bool force){ ...@@ -177,7 +263,6 @@ static void s_kill(pid_t pid,int max_delay,bool force){
} }
#endif // _WIN32 #endif // _WIN32
if(force){ if(force){
//发送SIGKILL信号后,阻塞等待退出 //发送SIGKILL信号后,阻塞等待退出
s_wait(pid, NULL, true); s_wait(pid, NULL, true);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论